欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用springmvc運(yùn)行流程分析,手寫spring框架嘗試

 更新時(shí)間:2021年10月30日 11:27:31   作者:JdbcUtils  
這篇文章主要介紹了使用springmvc運(yùn)行流程分析,手寫spring框架嘗試,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

該文章主要是分析Springmvc啟動(dòng)的流程(配置階段、初始化階段和運(yùn)行階段),可以讓自己對(duì)spring框架有更深一層的理解。對(duì)框架比較感興趣的朋友都可以了解閱讀下,對(duì)于我所描述的內(nèi)容有錯(cuò)誤的還望能不吝指出。

對(duì)于springmvc中的整個(gè)流程我個(gè)人把他分為這幾個(gè)階段,包括個(gè)人手寫的spring也是參照此按階段實(shí)現(xiàn):

1.配置階段

根據(jù)web.xml ,先定義DispatcherServlet并且定義該sevlet傳入的參數(shù)和路徑。

2.初始化階段

初始化階段中又可以分為IOC、DI和MVC階段:

  • (1)IOC:初始化配置文件和IOC容器,掃描配置的包下的類,通過反射機(jī)制將需要實(shí)例化的類放入IOC容器,既將帶有spring注解的類進(jìn)行實(shí)例化后存放到 IOC 容器中。IOC容器的實(shí)質(zhì)就是一個(gè)集合;
  • (2)DI:DI階段(其實(shí)就是依賴注入)。對(duì)需要賦值的實(shí)例屬性進(jìn)行賦值(一般較多都是處理帶有注解的@Autowrized的屬性)
  • (3)MVC:構(gòu)造出HandlerMapping集合,主要作用就是用于存放對(duì)外公開的API和Method之間的關(guān)系,一個(gè)API一般會(huì)對(duì)應(yīng)一個(gè)可執(zhí)行的Method.

3.運(yùn)行階段

運(yùn)行階段中,當(dāng)接受到一個(gè)url后,會(huì)到HandleMapping集合中,找到對(duì)應(yīng)Method、通過反射機(jī)制去執(zhí)行invoker,再返回結(jié)果給調(diào)用方。

這樣就大體完成了springmvc整個(gè)運(yùn)行階段,所描述的都僅為個(gè)人觀點(diǎn),如果有誤請(qǐng)?jiān)谠u(píng)論中指出。

其整體流程可以參照下圖:

接下來就來嘗試手寫一個(gè)類似springmvc的框架了,這個(gè)手寫的過程還是相當(dāng)有成就感的!

1.創(chuàng)建一個(gè)空的JavaWeb工程,引入依賴,其實(shí)因?yàn)槲覀兪且謱憇pring,所以基本不需要什么外部的依賴工具,只需要導(dǎo)入servlet-api即可,如下:

<dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
 </dependency>

2.根據(jù)上述的流程描述,接下來就是對(duì)web.xml進(jìn)行配置:

     <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>com.wangcw.cwframework.sevlet.CwDispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/*</url-pattern>
     </servlet-mapping>

對(duì)于配置中的CwDispatcherServlet其實(shí)就是個(gè)人自定義一個(gè)作用與spring中DispatcherServlet相同的Servlet,此處先創(chuàng)建一個(gè)空的CwDispatcherServlet,繼承 javax.servlet.http.HttpServlet即可,具體實(shí)現(xiàn)后面會(huì)描述。

此處因?yàn)槭鞘謱憇pring的部分功能,所以配置也不用寫太多,此處僅拿一個(gè)包掃描的配置(scanPackage),各位少俠可自行拓展。

CwDispatcherServlet中初始化的配置文件application.properties內(nèi)容如下:

 scanPackage=com.wangcw

3.相信spring中又一部分注解都是大家比較熟悉的,接下來我們先從這幾個(gè)注解著手吧。(此處就不指出各個(gè)注解的作用了,相信百度上已經(jīng)很多了)

spring注解 自定義注解
@Controller @CwController
@Autowired @CwAutowired
@RequestMapping @CwRequestMapping
@RequestParam @CwRequestParam
@Service @CwService

然后實(shí)現(xiàn)下各個(gè)自定義的注解,直接貼代碼:

/*
* 創(chuàng)建一個(gè)類似@Controller作用的注解類
*/
 
import java.lang.annotation.*;
 
@Target({ElementType.TYPE})         
@Retention(RetentionPolicy.RUNTIME)  
@Documented
public @interface CwController {
 
    String value() default "";
}
/*
* 創(chuàng)建一個(gè)類似@Autowried作用的注解類
*/
 
import java.lang.annotation.*;
 
@Target({ElementType.FIELD})        
@Retention(RetentionPolicy.RUNTIME)  
@Documented
public @interface CwAutowried {
 
    String value() default "";
}
/*
* 創(chuàng)建一個(gè)類似@RequestMapping作用的注解類
*/
 
import java.lang.annotation.*;
 
@Target({ElementType.TYPE, ElementType.METHOD})          
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface CwRequestMapping {
 
    String value() default "";
}
/*
* 創(chuàng)建一個(gè)類似@RequsetParam作用的注解類
*/
 
import java.lang.annotation.*;
 
@Target({ElementType.PARAMETER})        
@Retention(RetentionPolicy.RUNTIME)  
@Documented
public @interface CwRequestParam {
 
    String value() default "";
}
/*
* 創(chuàng)建一個(gè)類似@Service作用的注解類
*/
 
import java.lang.annotation.*;
 
@Target({ElementType.TYPE})         
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface CwService {
 
    String value() default "";
}

4.創(chuàng)建一個(gè)簡(jiǎn)單的控制層和業(yè)務(wù)層交互 Demo,加上自定的注解,具體注解的功能,后面贅述。

Controller.java

@CwController  
@CwRequestMapping("/demo")
public class Controller {  
  
    @CwAutowried
    private Service service;  
  
   @CwRequestMapping("/query")
    public void query(HttpServletRequest req, HttpServletResponse resp,@CwRequestParam("name") String name) throws IOException {  
            resp.getWriter().write(service.query(name));  
    }  
  
    @CwRequestMapping("/add") 
    public void add(HttpServletRequest req, HttpServletResponse resp, @CwRequestParam("a") Integer a, @CwRequestParam("b") Integer b) throws IOException {  
        resp.getWriter().write("a+b="+(a+b));  
    }  
}  

Service.java

public interface Service {
 
         String query(String name);
 
}

ServiceImpl.java

@CwService
public class ServiceImpl implements Service{  
  
    @Override  
    public String query(String name) {  
  
        return "I am "+name;  
    }  
}  

5.上面的controller層和service層已經(jīng)把我們上述的自定注解都使用上去了,接下來我們開始手寫spring的核心功能了,也就是實(shí)現(xiàn)CwDispatcherServlet.java這個(gè)HttpServlet的子類。

首先需要重寫父類中的init方法,因?yàn)槲覀円贗nit過程中實(shí)現(xiàn)出跟spring一樣的效果。

理一理init()過程中都需要做哪些事情呢?整理了一下init()中主要需要以下幾步操作

 @Override
    public void init(ServletConfig config)  {
 
        /* 1.加載配置文件*/
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
  
        /* 2.掃描scanPackage配置的路徑下所有相關(guān)的類*/
        doScanner(contextConfig.getProperty("scanPackage"));
  
        /* 3.初始化所有相關(guān)聯(lián)的實(shí)例,放入IOC容器中*/
        doInstance();
  
        /*4.實(shí)現(xiàn)自動(dòng)依賴注入 DI*/
        doAutowired();
  
        /*5.初始化HandlerMapping  */
        initHandlerMapping();
 
    }

第一步很簡(jiǎn)單,在類中定義一個(gè)Properties實(shí)例,用于存放Servlet初始化的配置文件。導(dǎo)入配置代碼略過,IO常規(guī)讀寫即可。

private Properties contextConfig = new Properties();

第二步通過上面獲取到的配置,取到需要掃描的包路徑,然后在根據(jù)路徑找到對(duì)應(yīng)文件夾,做一個(gè)遞歸掃描即可。將掃描到的文件名去除后綴,保存到一個(gè)集合中,那么該集合就存放了包下所有類的類名。

String  scanFileDir = contextConfig.getProperty("scanPackage");
 /* 用于存放掃描到的類 */
    private List<String> classNames = new ArrayList<String>();
  /*掃描獲取到對(duì)應(yīng)的class名,便于后面反射使用*/
                String  className = scanPackage + "." + file.getName().replace(".class", "");
                classNames.add(className);

第三步就是IOC階段,簡(jiǎn)而言之就是對(duì)上面集合中所有的類進(jìn)行遍歷,并且創(chuàng)建一個(gè)IOC容器,將帶有@CwController和@CwService的類置于容器內(nèi)。

 /* 創(chuàng)建一個(gè)IOC容器 */
    private Map<String, Object> IOC = new HashMap<String, Object>();
 for (String classNme : classNames){
                if( 對(duì)加了 @CwController 注解的類進(jìn)行初始化){
                       /* 對(duì)于初始化的類還需要放入IOC容器,
                          對(duì)于存入IOC的實(shí)例,key值是有一定規(guī)則的,默認(rèn)類名首字母小寫;*/    
   /* toLowerFirstCase是自定義的一個(gè)工具方法,用于將傳入的字符串首字母小寫 */
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    IOC.put(beanName, clazz.newInstance());
                } else if (對(duì)加了 @CwService 注解的類進(jìn)行初始化){
                    /* 對(duì)于存入IOC的實(shí)例,key值是有一定規(guī)則的,而Service層的規(guī)則相對(duì)上面更復(fù)雜一些,因?yàn)樽⒔饪梢杂凶远x實(shí)例名,并且可能是接口實(shí)現(xiàn)類 */
      IOC.put(beanName, instance);
                    
                } else {
                    //對(duì)于掃描到的沒有注解的類,忽略初始化行為
                    continue;
                }
            }

第四步是DI操作,將IOC容器中需要賦值的實(shí)例屬性進(jìn)行賦值,即帶有Autowired注解的實(shí)例屬性。偽代碼如下:

  /*遍歷IOC中的所有實(shí)例*/
        for(Map.Entry<String, Object> entry : IOC.entrySet()){
           /* 使用getDeclaredFields暴力反射 */
           Field [] fields = entry.getValue().getClass().getDeclaredFields();
           for (Field field : fields){
               /*1.判斷屬性是否有加注解@CwAutowried.對(duì)于有注解的屬性才需要賦值*/
    ....
    
               /*屬性授權(quán)*/
               field.setAccessible(true);
      field.set(entry.getValue(), IOC.get(beanName));
           }
        }

第五步要處理Controller層的Method與請(qǐng)求url的匹配關(guān)系,讓請(qǐng)求能準(zhǔn)確的請(qǐng)求到對(duì)應(yīng)的url。篇幅問題,此處還是上傳偽代碼。

 /* 創(chuàng)建HandlerMapping存放url,method的匹配關(guān)系 
  其中類Handler是我自己定義的一個(gè)利用正則去匹配url和method,
  只要用戶傳入url,Handler就可以響應(yīng)出其對(duì)應(yīng)的method*/
  private List<Handler> handlerMapping = new ArrayList<Handler>();
 
  /* 遍歷IOC容器 */
  for (Map.Entry<String, Object> entry : IOC.entrySet()){
      Class<?> clazz = entry.getValue().getClass();
      /* 只對(duì)帶有CwController注解的類進(jìn)行處理 */
     
     定義一個(gè)url,由帶有CwController的實(shí)例類上的@CwRequestMapping注解的值和Method上@CwRequestMapping注解的值組成
     
        /* (1).判斷類上是否有CwRequestMapping注解 ,進(jìn)行拼接 url*/
      
 
        /* (2).遍歷實(shí)例下每個(gè)Method,并且需要判斷該方法是否有【 @CwRequestMapping 】注解,拼接url*/
   
     
 
       /* 最后將匹配關(guān)系以正則的形式,放到HandlerMapping集合中 */
       String regex = (url);
       Pattern pattern = Pattern.compile(regex);
       handlerMapping.add(new Handler(pattern,method));
      }

到這里就基本完成了springmvc的初始化階段,之后的工作就是重寫一下CwDispatcherServlet.java父類的doGet()/doPost()方法。根據(jù)request中的URI和參數(shù)來執(zhí)行對(duì)應(yīng)的Method,并且響應(yīng)結(jié)果。

/* 利用反射執(zhí)行其所匹配的方法 */
        handler.method.invoke(handler.controller, paramValues);

到此整個(gè)步驟就完成了,此時(shí)可以愉快的啟動(dòng)項(xiàng)目,并訪問對(duì)應(yīng)的url進(jìn)行測(cè)試了。

根據(jù)上面Controller定義的方法可以知道其匹配的url為 : /demo/query 和 /demo/add,并且有使用@CwRequestParam注解定義了其各個(gè)參數(shù)的名稱。

測(cè)試結(jié)果如下:

http://localhost:8080/spring/demo/query?name=James

http://localhost:8080/spring/demo/add?a=222222&b=444444

再來測(cè)試個(gè)url,是controller中沒有聲明出@CwRequestMapping注解的,看看結(jié)果。

http://localhost:8080/spring/demo/testNoUrl

springMVC介紹以及執(zhí)行流程

什么是SpringMVC?

SpringMVC是一個(gè)實(shí)現(xiàn)了MVC設(shè)計(jì)模式的輕量級(jí)web層框架,使用起來簡(jiǎn)單方便。

SpringMVC的優(yōu)勢(shì)是什么?

1、清晰的角色劃分:

  • 前端控制器(DispatcherServlet)
  • 請(qǐng)求到處理器映射(HandlerMapping)
  • 處理器適配器(HandlerAdapter)
  • 視圖解析器(ViewResolver)
  • 處理器或頁面控制器(Controller)
  • 驗(yàn)證器( Validator)
  • 命令對(duì)象(Command 請(qǐng)求參數(shù)綁定到的對(duì)象就叫命令對(duì)象)
  • 表單對(duì)象(Form Object 提供給表單展示和提交到的對(duì)象就叫表單對(duì)象)。

2、分工明確,而且擴(kuò)展點(diǎn)相當(dāng)靈活,可以很容易擴(kuò)展,雖然幾乎不需要。

3、由于命令對(duì)象就是一個(gè) POJO,無需繼承框架特定 API,可以使用命令對(duì)象直接作為業(yè)務(wù)對(duì)象。

4、和 Spring 其他框架無縫集成,是其它 Web 框架所不具備的。

5、可適配,通過 HandlerAdapter 可以支持任意的類作為處理器。

6、可定制性, HandlerMapping、 ViewResolver 等能夠非常簡(jiǎn)單的定制。

7、功能強(qiáng)大的數(shù)據(jù)驗(yàn)證、格式化、綁定機(jī)制。

8、利用 Spring 提供的 Mock 對(duì)象能夠非常簡(jiǎn)單的進(jìn)行 Web 層單元測(cè)試。

9、本地化、主題的解析的支持,使我們更容易進(jìn)行國(guó)際化和主題的切換。

10、強(qiáng)大的 JSP 標(biāo)簽庫,使 JSP 編寫更容易。

還有比如RESTful風(fēng)格的支持、簡(jiǎn)單的文件上傳、約定大于配置的契約式編程支持、基于注解的零配置支持等等。

與Struts2的對(duì)比:

共同點(diǎn):都是基于MVC設(shè)計(jì)模式的表現(xiàn)層框架,底層實(shí)現(xiàn)都離不開原始的Servlet,處理請(qǐng)求的機(jī)制都是一個(gè)核心控制器;

區(qū)別:Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter

Spring MVC 是基于方法設(shè)計(jì)的,而 Struts2 是基于類, Struts2 每次執(zhí)行都會(huì)創(chuàng)建一個(gè)動(dòng)作類。所以 Spring MVC 會(huì)稍微比 Struts2 快些。

相比來說,SpringMVC已經(jīng)全面超越了Struts2。

執(zhí)行流程:

DispatcherServlet: 是整個(gè)springmvc框架的核心。

前端控制器/核心控制器:所有的請(qǐng)求和響應(yīng)都由此控制器進(jìn)行分配。

前端控制器的所有工作都是基于組件完成的:

三大組件:

  • HandlerMapping: 它負(fù)責(zé)根據(jù)客戶端的請(qǐng)求尋找對(duì)應(yīng)的hadler,找到以后把尋找的handler返回給DispatcherServlet;
  • HandlerAdapter:它負(fù)責(zé)執(zhí)行尋找到的Handler的方法,方法執(zhí)行完后將返回值給HandlerAdapter, HandlerAdapter將返回值傳給DispatcherServlet;
  • ViewResolver:它根據(jù)DispatcherServlet指定的返回結(jié)果尋找對(duì)應(yīng)的頁面,找到后將結(jié)果返回給DispatcherServlet。
  • DispatcherServlet負(fù)責(zé)最終的響應(yīng),默認(rèn)是轉(zhuǎn)發(fā)的操作。

執(zhí)行流程圖:

在這里插入圖片描述

圖畫的有些靈魂,但是大致的流程就是這樣,大家可以參考著理解下。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java繼承構(gòu)造器使用過程解析

    Java繼承構(gòu)造器使用過程解析

    這篇文章主要介紹了Java繼承構(gòu)造器使用過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • JAVA Static關(guān)鍵字的用法

    JAVA Static關(guān)鍵字的用法

    這篇文章主要介紹了JAVA Static關(guān)鍵字的用法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • java開發(fā)環(huán)境的完整搭建過程

    java開發(fā)環(huán)境的完整搭建過程

    這篇文章主要給大家介紹了關(guān)于java開發(fā)環(huán)境的完整搭建過程,文中介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Spring?Boot?+?Spring?Batch?實(shí)現(xiàn)批處理任務(wù)的詳細(xì)教程

    Spring?Boot?+?Spring?Batch?實(shí)現(xiàn)批處理任務(wù)的詳細(xì)教程

    這篇文章主要介紹了Spring?Boot+Spring?Batch實(shí)現(xiàn)批處理任務(wù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • 詳解SpringBoot之集成Spring AOP

    詳解SpringBoot之集成Spring AOP

    本篇文章主要介紹了詳解SpringBoot之集成Spring AOP,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • 詳解springMVC之與json數(shù)據(jù)交互方法

    詳解springMVC之與json數(shù)據(jù)交互方法

    本篇文章主要介紹了詳解springMVC之與json數(shù)據(jù)交互方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 搭建MyBatis-Plus框架并進(jìn)行數(shù)據(jù)庫增刪改查功能

    搭建MyBatis-Plus框架并進(jìn)行數(shù)據(jù)庫增刪改查功能

    這篇文章主要介紹了搭建MyBatis-Plus框架并進(jìn)行數(shù)據(jù)庫增刪改查,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • mybatis如何設(shè)置useGeneratedKeys=true

    mybatis如何設(shè)置useGeneratedKeys=true

    這篇文章主要介紹了mybatis如何設(shè)置useGeneratedKeys=true,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。
    2022-01-01
  • springBoot的事件機(jī)制GenericApplicationListener用法解析

    springBoot的事件機(jī)制GenericApplicationListener用法解析

    這篇文章主要介紹了springBoot的事件機(jī)制GenericApplicationListener用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值的相關(guān)資料
    2019-09-09
  • Java對(duì)象轉(zhuǎn)json JsonFormat注解

    Java對(duì)象轉(zhuǎn)json JsonFormat注解

    這篇文章主要介紹了Java對(duì)象轉(zhuǎn)json JsonFormat注解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05

最新評(píng)論