亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JAVA設(shè)計模式之責(zé)任鏈模式詳解

 更新時間:2015年04月02日 09:12:10   投稿:junjie  
這篇文章主要介紹了JAVA設(shè)計模式之責(zé)任鏈模式詳解,需要的朋友可以參考下

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述責(zé)任鏈(Chain of Responsibility)模式的:

  責(zé)任鏈模式是一種對象的行為模式。在責(zé)任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發(fā)出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織和分配責(zé)任。

從擊鼓傳花談起

  擊鼓傳花是一種熱鬧而又緊張的飲酒游戲。在酒宴上賓客依次坐定位置,由一人擊鼓,擊鼓的地方與傳花的地方是分開的,以示公正。開始擊鼓時,花束就開始依次傳遞,鼓聲一落,如果花束在某人手中,則該人就得飲酒。

  比如說,賈母、賈赦、賈政、賈寶玉和賈環(huán)是五個參加擊鼓傳花游戲的傳花者,他們組成一個環(huán)鏈。擊鼓者將花傳給賈母,開始傳花游戲?;ㄓ少Z母傳給賈赦,由賈赦傳給賈政,由賈政傳給賈寶玉,又賈寶玉傳給賈環(huán),由賈環(huán)傳回給賈母,如此往復(fù),如下圖所示。當(dāng)鼓聲停止時,手中有花的人就得執(zhí)行酒令。

  擊鼓傳花便是責(zé)任鏈模式的應(yīng)用。責(zé)任鏈可能是一條直線、一個環(huán)鏈或者一個樹結(jié)構(gòu)的一部分。

責(zé)任鏈模式的結(jié)構(gòu)

  下面使用了一個責(zé)任鏈模式的最簡單的實現(xiàn)。

責(zé)任鏈模式涉及到的角色如下所示:

  ●  抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義 出一個方法以設(shè)定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實現(xiàn)。上圖中Handler類的聚合關(guān)系給出了具體子類對下家的引用,抽象方法handleRequest()規(guī)范了子類處理請求的操作。
  ●  具體處理者(ConcreteHandler)角色:具體處理者接到請求后,可以選擇將請求處理掉,或者將請求傳給下家。由于具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。

源代碼

  抽象處理者角色

復(fù)制代碼 代碼如下:

public abstract class Handler {
   
    /**
     * 持有后繼的責(zé)任對象
     */
    protected Handler successor;
    /**
     * 示意處理請求的方法,雖然這個示意方法是沒有傳入?yún)?shù)的
     * 但實際是可以傳入?yún)?shù)的,根據(jù)具體需要來選擇是否傳遞參數(shù)
     */
    public abstract void handleRequest();
    /**
     * 取值方法
     */
    public Handler getSuccessor() {
        return successor;
    }
    /**
     * 賦值方法,設(shè)置后繼的責(zé)任對象
     */
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
   
}

具體處理者角色

復(fù)制代碼 代碼如下:

public class ConcreteHandler extends Handler {
    /**
     * 處理方法,調(diào)用此方法處理請求
     */
    @Override
    public void handleRequest() {
        /**
         * 判斷是否有后繼的責(zé)任對象
         * 如果有,就轉(zhuǎn)發(fā)請求給后繼的責(zé)任對象
         * 如果沒有,則處理請求
         */
        if(getSuccessor() != null)
        {           
            System.out.println("放過請求");
            getSuccessor().handleRequest();           
        }else
        {           
            System.out.println("處理請求");
        }
    }

}


客戶端類
復(fù)制代碼 代碼如下:

public class Client {

    public static void main(String[] args) {
        //組裝責(zé)任鏈
        Handler handler1 = new ConcreteHandler();
        Handler handler2 = new ConcreteHandler();
        handler1.setSuccessor(handler2);
        //提交請求
        handler1.handleRequest();
    }

}

  可以看出,客戶端創(chuàng)建了兩個處理者對象,并指定第一個處理者對象的下家是第二個處理者對象,而第二個處理者對象沒有下家。然后客戶端將請求傳遞給第一個處理者對象。

  由于本示例的傳遞邏輯非常簡單:只要有下家,就傳給下家處理;如果沒有下家,就自行處理。因此,第一個處理者對象接到請求后,會將請求傳遞給第二個處理者對象。由于第二個處理者對象沒有下家,于是自行處理請求?;顒訒r序圖如下所示。

使用場景

  來考慮這樣一個功能:申請聚餐費(fèi)用的管理。

  很多公司都是這樣的福利,就是項目組或者是部門可以向公司申請一些聚餐費(fèi)用,用于組織項目組成員或者是部門成員進(jìn)行聚餐活動。

  申請聚餐費(fèi)用的大致流程一般是:由申請人先填寫申請單,然后交給領(lǐng)導(dǎo)審批,如果申請批準(zhǔn)下來,領(lǐng)導(dǎo)會通知申請人審批通過,然后申請人去財務(wù)領(lǐng)取費(fèi)用,如果沒有批準(zhǔn)下來,領(lǐng)導(dǎo)會通知申請人審批未通過,此事也就此作罷。

  不同級別的領(lǐng)導(dǎo),對于審批的額度是不一樣的,比如,項目經(jīng)理只能審批500元以內(nèi)的申請;部門經(jīng)理能審批1000元以內(nèi)的申請;而總經(jīng)理可以審核任意額度的申請。

  也就是說,當(dāng)某人提出聚餐費(fèi)用申請的請求后,該請求會經(jīng)由項目經(jīng)理、部門經(jīng)理、總經(jīng)理之中的某一位領(lǐng)導(dǎo)來進(jìn)行相應(yīng)的處理,但是提出申請的人并不知道最終會由誰來處理他的請求,一般申請人是把自己的申請?zhí)峤唤o項目經(jīng)理,或許最后是由總經(jīng)理來處理他的請求。

  

  可以使用責(zé)任鏈模式來實現(xiàn)上述功能:當(dāng)某人提出聚餐費(fèi)用申請的請求后,該請求會在 項目經(jīng)理—〉部門經(jīng)理—〉總經(jīng)理 這樣一條領(lǐng)導(dǎo)處理鏈上進(jìn)行傳遞,發(fā)出請求的人并不知道誰會來處理他的請求,每個領(lǐng)導(dǎo)會根據(jù)自己的職責(zé)范圍,來判斷是處理請求還是把請求交給更高級別的領(lǐng)導(dǎo),只要有領(lǐng)導(dǎo)處理了,傳遞就結(jié)束了。

  需要把每位領(lǐng)導(dǎo)的處理獨(dú)立出來,實現(xiàn)成單獨(dú)的職責(zé)處理對象,然后為它們提供一個公共的、抽象的父職責(zé)對象,這樣就可以在客戶端來動態(tài)地組合職責(zé)鏈,實現(xiàn)不同的功能要求了。

源代碼

  抽象處理者角色類

復(fù)制代碼 代碼如下:

public abstract class Handler {
    /**
     * 持有下一個處理請求的對象
     */
    protected Handler successor = null;
    /**
     * 取值方法
     */
    public Handler getSuccessor() {
        return successor;
    }
    /**
     * 設(shè)置下一個處理請求的對象
     */
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    /**
     * 處理聚餐費(fèi)用的申請
     * @param user    申請人
     * @param fee    申請的錢數(shù)
     * @return        成功或失敗的具體通知
     */
    public abstract String handleFeeRequest(String user , double fee);
}

具體處理者角色

復(fù)制代碼 代碼如下:

public class ProjectManager extends Handler {

    @Override
    public String handleFeeRequest(String user, double fee) {
       
        String str = "";
        //項目經(jīng)理權(quán)限比較小,只能在500以內(nèi)
        if(fee < 500)
        {
            //為了測試,簡單點(diǎn),只同意張三的請求
            if("張三".equals(user))
            {
                str = "成功:項目經(jīng)理同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";   
            }else
            {
                //其他人一律不同意
                str = "失?。喉椖拷?jīng)理不同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
            }
        }else
        {
            //超過500,繼續(xù)傳遞給級別更高的人處理
            if(getSuccessor() != null)
            {
                return getSuccessor().handleFeeRequest(user, fee);
            }
        }
        return str;
    }

}

復(fù)制代碼 代碼如下:

public class DeptManager extends Handler {

    @Override
    public String handleFeeRequest(String user, double fee) {
       
        String str = "";
        //部門經(jīng)理的權(quán)限只能在1000以內(nèi)
        if(fee < 1000)
        {
            //為了測試,簡單點(diǎn),只同意張三的請求
            if("張三".equals(user))
            {
                str = "成功:部門經(jīng)理同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";   
            }else
            {
                //其他人一律不同意
                str = "失?。翰块T經(jīng)理不同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
            }
        }else
        {
            //超過1000,繼續(xù)傳遞給級別更高的人處理
            if(getSuccessor() != null)
            {
                return getSuccessor().handleFeeRequest(user, fee);
            }
        }
        return str;
    }

}

復(fù)制代碼 代碼如下:

public class GeneralManager extends Handler {

    @Override
    public String handleFeeRequest(String user, double fee) {
       
        String str = "";
        //總經(jīng)理的權(quán)限很大,只要請求到了這里,他都可以處理
        if(fee >= 1000)
        {
            //為了測試,簡單點(diǎn),只同意張三的請求
            if("張三".equals(user))
            {
                str = "成功:總經(jīng)理同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";   
            }else
            {
                //其他人一律不同意
                str = "失敗:總經(jīng)理不同意【" + user + "】的聚餐費(fèi)用,金額為" + fee + "元";
            }
        }else
        {
            //如果還有后繼的處理對象,繼續(xù)傳遞
            if(getSuccessor() != null)
            {
                return getSuccessor().handleFeeRequest(user, fee);
            }
        }
        return str;
    }

}

客戶端類

復(fù)制代碼 代碼如下:

public class Client {

    public static void main(String[] args) {
        //先要組裝責(zé)任鏈
        Handler h1 = new GeneralManager();
        Handler h2 = new DeptManager();
        Handler h3 = new ProjectManager();
        h3.setSuccessor(h2);
        h2.setSuccessor(h1);
       
        //開始測試
        String test1 = h3.handleFeeRequest("張三", 300);
        System.out.println("test1 = " + test1);
        String test2 = h3.handleFeeRequest("李四", 300);
        System.out.println("test2 = " + test2);
        System.out.println("---------------------------------------");
       
        String test3 = h3.handleFeeRequest("張三", 700);
        System.out.println("test3 = " + test3);
        String test4 = h3.handleFeeRequest("李四", 700);
        System.out.println("test4 = " + test4);
        System.out.println("---------------------------------------");
       
        String test5 = h3.handleFeeRequest("張三", 1500);
        System.out.println("test5 = " + test5);
        String test6 = h3.handleFeeRequest("李四", 1500);
        System.out.println("test6 = " + test6);
    }

}


運(yùn)行結(jié)果如下所示:

純的與不純的責(zé)任鏈模式

  一個純的責(zé)任鏈模式要求一個具體的處理者對象只能在兩個行為中選擇一個:一是承擔(dān)責(zé)任,而是把責(zé)任推給下家。不允許出現(xiàn)某一個具體處理者對象在承擔(dān)了一部分責(zé)任后又 把責(zé)任向下傳的情況。

  在一個純的責(zé)任鏈模式里面,一個請求必須被某一個處理者對象所接收;在一個不純的責(zé)任鏈模式里面,一個請求可以最終不被任何接收端對象所接收。

  純的責(zé)任鏈模式的實際例子很難找到,一般看到的例子均是不純的責(zé)任鏈模式的實現(xiàn)。有些人認(rèn)為不純的責(zé)任鏈根本不是責(zé)任鏈模式,這也許是有道理的。但是在實際的系統(tǒng)里,純的責(zé)任鏈很難找到。如果堅持責(zé)任鏈不純便不是責(zé)任鏈模式,那么責(zé)任鏈模式便不會有太大意義了。

責(zé)任鏈模式在Tomcat中的應(yīng)用

  眾所周知Tomcat中的Filter就是使用了責(zé)任鏈模式,創(chuàng)建一個Filter除了要在web.xml文件中做相應(yīng)配置外,還需要實現(xiàn)javax.servlet.Filter接口。

復(fù)制代碼 代碼如下:

public class TestFilter implements Filter{

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
       
        chain.doFilter(request, response);
    }

    public void destroy() {
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }

}

 使用DEBUG模式所看到的結(jié)果如下

其實在真正執(zhí)行到TestFilter類之前,會經(jīng)過很多Tomcat內(nèi)部的類。順帶提一下其實Tomcat的容器設(shè)置也是責(zé)任鏈模式,注意被紅色方框所圈中的類,從Engine到Host再到Context一直到Wrapper都是通過一個鏈傳遞請求。被綠色方框所圈中的地方有一個名為ApplicationFilterChain的類,ApplicationFilterChain類所扮演的就是抽象處理者角色,而具體處理者角色由各個Filter扮演。

  第一個疑問是ApplicationFilterChain將所有的Filter存放在哪里?

  答案是保存在ApplicationFilterChain類中的一個ApplicationFilterConfig對象的數(shù)組中。

復(fù)制代碼 代碼如下:

/**
     * Filters.
     */
    private ApplicationFilterConfig[] filters =
        new ApplicationFilterConfig[0];

 那ApplicationFilterConfig對象又是什么呢?

    ApplicationFilterConfig是一個Filter容器。以下是ApplicationFilterConfig類的聲明:

復(fù)制代碼 代碼如下:

/**
 * Implementation of a <code>javax.servlet.FilterConfig</code> useful in
 * managing the filter instances instantiated when a web application
 * is first started.
 *
 * @author Craig R. McClanahan
 * @version $Id: ApplicationFilterConfig.java 1201569 2011-11-14 01:36:07Z kkolinko $
 */

當(dāng)一個web應(yīng)用首次啟動時ApplicationFilterConfig會自動實例化,它會從該web應(yīng)用的web.xml文件中讀取配置的Filter的信息,然后裝進(jìn)該容器。

  剛剛看到在ApplicationFilterChain類中所創(chuàng)建的ApplicationFilterConfig數(shù)組長度為零,那它是在什么時候被重新賦值的呢?

復(fù)制代碼 代碼如下:

private ApplicationFilterConfig[] filters =
        new ApplicationFilterConfig[0];

是在調(diào)用ApplicationFilterChain類的addFilter()方法時。

復(fù)制代碼 代碼如下:

  /**
     * The int which gives the current number of filters in the chain.
     */
    private int n = 0;
 public static final int INCREMENT = 10;


復(fù)制代碼 代碼如下:

void addFilter(ApplicationFilterConfig filterConfig) {

        // Prevent the same filter being added multiple times
        for(ApplicationFilterConfig filter:filters)
            if(filter==filterConfig)
                return;

        if (n == filters.length) {
            ApplicationFilterConfig[] newFilters =
                new ApplicationFilterConfig[n + INCREMENT];
            System.arraycopy(filters, 0, newFilters, 0, n);
            filters = newFilters;
        }
        filters[n++] = filterConfig;

    }

變量n用來記錄當(dāng)前過濾器鏈里面擁有的過濾器數(shù)目,默認(rèn)情況下n等于0,ApplicationFilterConfig對象數(shù)組的長度也等于0,所以當(dāng)?shù)谝淮握{(diào)用addFilter()方法時,if (n == filters.length)的條件成立,ApplicationFilterConfig數(shù)組長度被改變。之后filters[n++] = filterConfig;將變量filterConfig放入ApplicationFilterConfig數(shù)組中并將當(dāng)前過濾器鏈里面擁有的過濾器數(shù)目+1。

  那ApplicationFilterChain的addFilter()方法又是在什么地方被調(diào)用的呢?

  是在ApplicationFilterFactory類的createFilterChain()方法中。

復(fù)制代碼 代碼如下:

public ApplicationFilterChain createFilterChain
        (ServletRequest request, Wrapper wrapper, Servlet servlet) {

        // get the dispatcher type
        DispatcherType dispatcher = null;
        if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
            dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR);
        }
        String requestPath = null;
        Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
       
        if (attribute != null){
            requestPath = attribute.toString();
        }
       
        // If there is no servlet to execute, return null
        if (servlet == null)
            return (null);

        boolean comet = false;
       
        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            comet = req.isComet();
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
                if (comet) {
                    req.setFilterChain(filterChain);
                }
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);

        filterChain.setSupport
            (((StandardWrapper)wrapper).getInstanceSupport());

        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);

        // Acquire the information we will need to match filter mappings
        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                    Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
                    ExceptionUtils.handleThrowable(t);
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                filterChain.addFilter(filterConfig);
            }
        }

        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            boolean isCometFilter = false;
            if (comet) {
                try {
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;
                } catch (Exception e) {
                    // Note: The try catch is there because getFilter has a lot of
                    // declared exceptions. However, the filter is allocated much
                    // earlier
                }
                if (isCometFilter) {
                    filterChain.addFilter(filterConfig);
                }
            } else {
                filterChain.addFilter(filterConfig);
            }
        }

        // Return the completed filter chain
        return (filterChain);

    }

可以將如上代碼分為兩段,51行之前為第一段,51行之后為第二段。

  第一段的主要目的是創(chuàng)建ApplicationFilterChain對象以及一些參數(shù)設(shè)置。

  第二段的主要目的是從上下文中獲取所有Filter信息,之后使用for循環(huán)遍歷并調(diào)用filterChain.addFilter(filterConfig);將filterConfig放入ApplicationFilterChain對象的ApplicationFilterConfig數(shù)組中。

  那ApplicationFilterFactory類的createFilterChain()方法又是在什么地方被調(diào)用的呢?

是在StandardWrapperValue類的invoke()方法中被調(diào)用的。

  由于invoke()方法較長,所以將很多地方省略。

復(fù)制代碼 代碼如下:

public final void invoke(Request request, Response response)
        throws IOException, ServletException {
   ...省略中間代碼
     // Create the filter chain for this request
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);
  ...省略中間代碼
         filterChain.doFilter(request.getRequest(), response.getResponse());
  ...省略中間代碼
    }

 那正常的流程應(yīng)該是這樣的:

  在StandardWrapperValue類的invoke()方法中調(diào)用ApplicationFilterChai類的createFilterChain()方法———>在ApplicationFilterChai類的createFilterChain()方法中調(diào)用ApplicationFilterChain類的addFilter()方法———>在ApplicationFilterChain類的addFilter()方法中給ApplicationFilterConfig數(shù)組賦值。

根據(jù)上面的代碼可以看出StandardWrapperValue類的invoke()方法在執(zhí)行完createFilterChain()方法后,會繼續(xù)執(zhí)行ApplicationFilterChain類的doFilter()方法,然后在doFilter()方法中會調(diào)用internalDoFilter()方法。

  以下是internalDoFilter()方法的部分代碼

復(fù)制代碼 代碼如下:

// Call the next filter if there is one
        if (pos < n) {
       //拿到下一個Filter,將指針向下移動一位
            //pos它來標(biāo)識當(dāng)前ApplicationFilterChain(當(dāng)前過濾器鏈)執(zhí)行到哪個過濾器
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
          //獲取當(dāng)前指向的Filter的實例
                filter = filterConfig.getFilter();
                support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
                                          filter, request, response);
               
                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                            Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege
                        ("doFilter", filter, classType, args, principal);
                   
                } else {
            //調(diào)用Filter的doFilter()方法 
                    filter.doFilter(request, response, this);
                }

這里的filter.doFilter(request, response, this);就是調(diào)用我們前面創(chuàng)建的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法會繼續(xù)調(diào)用chain.doFilter(request, response);方法,而這個chain其實就是ApplicationFilterChain,所以調(diào)用過程又回到了上面調(diào)用dofilter和調(diào)用internalDoFilter方法,這樣執(zhí)行直到里面的過濾器全部執(zhí)行。

  如果定義兩個過濾器,則Debug結(jié)果如下:

相關(guān)文章

  • Java深入淺出掌握SpringBoot之MVC自動配置原理篇

    Java深入淺出掌握SpringBoot之MVC自動配置原理篇

    在進(jìn)行項目編寫前,我們還需要知道一個東西,就是SpringBoot對我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們在之后使用才會更加得心應(yīng)手
    2021-10-10
  • 使用jsoup解析html的table中的文本信息實例

    使用jsoup解析html的table中的文本信息實例

    今天小編就為大家分享一篇使用jsoup解析html的table中的文本信息實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 幾句話說清session,cookie和token的區(qū)別及說明

    幾句話說清session,cookie和token的區(qū)別及說明

    這篇文章主要介紹了幾句話說清session,cookie和token的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 通過簡易例子講解Java回調(diào)機(jī)制

    通過簡易例子講解Java回調(diào)機(jī)制

    這篇文章主要介紹了通過簡易例子講解Java回調(diào)機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • 詳解spring boot rest例子

    詳解spring boot rest例子

    這篇文章主要介紹了詳解spring boot rest例子,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • Java中的RASP機(jī)制實現(xiàn)詳解

    Java中的RASP機(jī)制實現(xiàn)詳解

    這篇文章主要介紹了Java中的RASP實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • 詳解elasticsearch實現(xiàn)基于拼音搜索

    詳解elasticsearch實現(xiàn)基于拼音搜索

    這篇文章主要為大家介紹了詳解elasticsearch實現(xiàn)基于拼音搜索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • java實現(xiàn)乘地鐵方案的最優(yōu)選擇(票價,距離)

    java實現(xiàn)乘地鐵方案的最優(yōu)選擇(票價,距離)

    這篇文章主要介紹了java實現(xiàn)乘地鐵方案的最優(yōu)選擇(票價,距離),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • Java判斷變量是否為空問題的方法總結(jié)

    Java判斷變量是否為空問題的方法總結(jié)

    項目中經(jīng)常遇到對象判空,下面這篇文章主要給大家介紹了關(guān)于Java判斷變量是否為空問題的方法,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-04-04
  • 淺析Java中SimpleDateFormat為什么是線程不安全的

    淺析Java中SimpleDateFormat為什么是線程不安全的

    SimpleDateFormat是Java中用于日期時間格式化的一個類,它提供了對日期的解析和格式化能力,本文主要來和大家一起探討一下SimpleDateFormat為什么是線程不安全的,感興趣的可以了解下
    2024-02-02

最新評論