springmvc源碼深度解析

springmvc源碼深度解析

工程代碼

建議先精讀springmvc零配置原理
pom文件 依賴

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.8.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>8.5.5</version>
    </dependency>

項目啓動類-啓動tomcat,回調onStartup初始化spring及mvc環境

public class MySpringApplication {
    public static void main(String[] args) throws Exception {
        MySpringApplication.run();
    }
    public static void run() throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(80);
        //addContext ,不是web項目,不會回調SpringServletContainerInitializer的onStartup方法
        Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
        //添加LifecycleListener回調SpringServletContainerInitializer的onStartup方法
        context.addLifecycleListener((LifecycleListener)
        Class.forName(tomcat.getHost().getConfigClass()).newInstance());
        tomcat.start();
        tomcat.getServer().await();
    }
}

初始化spring及mvc環境

public class LryWebApplicationInitializer implements WebApplicationInitializer {
    //啓動tomcat的時候會調用這裏
    @Override
    public void onStartup(ServletContext servletCxt) {
		//spring環境
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        //DispatcherServlet,mvc環境
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

AppConfig配置文件

@Configuration 加了這個註解是spring全配置類,會被cglib代理,爲什麼會被代理我猜測是他要對這個類做邏輯增強防止某些非法事件發生,見下例
@ComponentScan("com.lry")
public class AppConfig  {
	
}

解釋@Configuration 爲什麼要被cglib代理

public class A {
    public A(){
        System.out.println("A");
    }
}

public class B {
    public B(){
        System.out.println("B");
    }
}

@Configuration
@ComponentScan("com.lry")
public class AppConfig {

    @Bean
    public A getA(){
        return new A();
    }
    @Bean
    public B getB(){
        getA();
        return new B();
    }
}

在加上@Configuration的情況下A,B分別打印一次(一次A被cglib過濾了)
當你不加@Configuration,A會被打印兩次

controller文件–實現controller的三種方式

方式一,@Controller註解

@Controller
public class HelloController {

    @GetMapping("hello")
    @ResponseBody
    public String hello(){
        return "hello SpringMVC";
    }
}

方式二,實現Controller接口
訪問/implementsController這個路徑時會調用handleRequest方法,一個類只能處理一個請求,和原始servlet一樣

@Component("/implementsController")
public class C1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        return null;
    }
}

方式三,實現HttpRequestHandler接口
訪問/implementsHttpRequestHandler這個路徑時會調用handleRequest方法,一個類只能處理一個請求,和原始servlet一樣

@Component("/implementsHttpRequestHandler")
public class C2 implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
        return;
    }
}

方式二和方式三都過於笨重,目前基本不用

入口及重要方法

入口:
FrameworkServlet#doGet或者doPost---->processRequest---->DispatcherServlet#doService---->doDispatch
所有的請求都會走到DispatcherServlet的doDispatch方法

doDispatch方法核心代碼如下

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        
        HttpServletRequest processedRequest = request;
        //拿到對應的處理器
        //大致可以這樣理解,比如訪問/hello,mappedHandler就是HelloController類和hello方法封裝的一個對象
        HandlerExecutionChain  mappedHandler = this.getHandler(processedRequest);
        
        //拿到處理器對應的處理器適配器,爲什麼要拿適配器,直接處理不好嗎?
        //我們知道有三種類型的controller,不同的controller處理方式都不同,按照我們常規寫法就是
        //if(@Controller的controller){處理邏輯,反射調用hello方法}
        //else if(實現Controller接口的controller){處理邏輯,回調接口的handleRequest方法}
        //這種寫法很明顯的弊端就是違反了開閉原則,當我們要處理第四種controller的時候必須新增一個elseif
        //mvc採取適配器模式解決了這種問題,稍後會詳細講解並一個媒體播放的例子說明適配器模式            
        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
        
		//真正的去調用hello方法或handleRequest方法,不同的適配器處理不同的controller
        ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    }

getHandler方法

getHandler核心代碼如下

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//this.handlerMappings裏有兩個HandlerMapping
		//一個是RequestMappingHandlerMapping,處理@Controller這種controller
		//另一個是BeanNameUrlHandlerMapping,處理實現Controller和HttpRequestHandler接口的controller
		//可以發現這裏沒有處理靜態資源的HandlerMapping,spring boot就是實現HandlerMapping接口擴展了類來實現靜態資源的讀取
		//從spring boot的擴展可以看出這裏的擴展性是很強的,如果是常規代碼,肯定又是
		//if(@Controller的controller){}  else if(實現Controller||實現HttpRequestHandler){}
		//可以看出spring非常遵循開閉原則,可擴展性極強
		//還有一個問題:this.handlerMappings是哪裏來的,我們沒有看到過這個東西在哪裏add的
		for(HandlerMapping hm:this.handlerMappings){
			//遍歷所有的handlerMapping,找到具體的controller(HelloController的hello||handlerRequest方法)
			HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
		}
        return null;
    }

this.handlerMappings是哪裏來的
this.handlerMappings是從DispatcherServlet#initStrategies這裏來的,initStrategies就不繼續往上追了,否則要追到tomcat源碼,這就偏離了本文主題
initStrategies如下----省略部分非主要的內容

 protected void initStrategies(ApplicationContext context) {
        this.initHandlerMappings(context);//初始化this.handlerMappings
        this.initHandlerAdapters(context);//初始化this.handlerAdapters
    }

下面我們主要看看 this.initHandlerMappings(context);是怎樣初始化this.handlerMappings的

 private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        //detectAllHandlerMappings默認是true,是否查找所有的HandlerMapping
        if (this.detectAllHandlerMappings) {
        	//從spring容器拿出所有類型爲HandlerMapping的bean,但是一般情況都是返回null
        	//因爲spring沒有把RequestMappingHandlerMapping和BeanNameUrlHandlerMapping放到spring容器,
        	//除非我們自己實現HandlerMapping寫一個擴展類並@Component放入spring容器
        	//但是這種擴展方式有問題吧?這樣一來handlerMappings就不是null了,
        	//找RequestMappingHandlerMapping和BeanNameUrlHandlerMapping的邏輯也就不會執行了
            Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        } else {
        //只查找beanName爲handlerMapping的HandlerMapping
            try {
                HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            } catch (NoSuchBeanDefinitionException var3) {
                ;
            }
        }
		//上面的代碼可以先忽略,核心在下面
        if (this.handlerMappings == null) {
	        //找到RequestMappingHandlerMapping和BeanNameUrlHandlerMapping
            this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
        }
    }

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        String key = strategyInterface.getName();//拿到HandlerMapping的全路徑名
        //defaultStrategies是一個Properties文件,在DsipatcherServlet的靜態塊裏初始化
        String value = defaultStrategies.getProperty(key);
        //省略
        }
    }

 static {
 		ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }

DispatcherServlet.properties關於HandlerMapping的配置如下:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

hm.getHandler(request)
核心代碼如下:

 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request);//核心方法
        if (handler == null) {
            handler = this.getDefaultHandler();
        }
		
        if (handler == null) {
            return null;
        } else {
        	//把handler封裝成HandlerExecutionChain
            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(, request);
            return executionChain;
        }
    }

this.getHandlerInternal(request)
這個方法要看this是誰,如果this是BeanNameUrlHandlerMapping,則調用的是AbstractUrlHandlerMapping的getHandlerInternal,如果this是RequestMappingHandlerMapping,則調用的是AbstractHandlerMethodMapping的getHandlerInternal。
在本篇博客我只分析後者也就是@Controller方式

核心代碼如下:

 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        this.mappingRegistry.acquireReadLock();

        HandlerMethod var4;
        try {
        	//核心代碼
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
			
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
        return var4;
    }

this.lookupHandlerMethod(lookupPath, request)
核心代碼如下

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
        //你可以這樣理解mappingRegistry,他就是存了url到controller之間映射的map
        //<"/hello",HelloController#hello>
        //this.mappingRegistry什麼時候初始化的,什麼時候把我們的映射對添加進去的?
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);//核心代碼
        if (directPathMatches != null) {
        	//把directPathMatches加到matches裏
            this.addMatchingMappings(, matches, request);
        }
        if (!matches.isEmpty()) {
            AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            return bestMatch.handlerMethod;
        } 
    }

解釋mappingRegistry什麼時候初始化
AbstractAutowireCapableBeanFactory#doCreateBean
AbstractAutowireCapableBeanFactory#initializeBean
AbstractAutowireCapableBeanFactory#invokeInitMethods
RequestMappingHandlerMapping#afterPropertiesSet
AbstractHandlerMethodMapping#afterPropertiesSet
AbstractHandlerMethodMapping#initHandlerMethods
AbstractHandlerMethodMapping#detectHandlerMethods
AbstractHandlerMethodMapping#registerHandlerMethod
MappingRegistry#register
照着這些跟,其實也就是populateBean之後的InitializeBean的afterPropertiesSet做的處理

getHandlerAdapter方法及適配器模式

在說這個例子之前我想向大家先介紹一個demo,媒體播放

public interface MediaPlayer {
    /**
     * 支持哪種格式
     * @param format 格式
     * @return
     */
    boolean supports(String format);
    
    void play();

}

public class Mp3Player implements MediaPlayer {
    @Override
    public boolean supports(String format) {
        if(".mp3".equals(format))
            return true;
        return false;
    }

    @Override
    public void play() {
        System.out.println("mp3播放");
    }
}

public class Mp4Player implements MediaPlayer {
    @Override
    public boolean supports(String format) {
        if(".mp4".equals(format))
            return true;
        return false;
    }

    @Override
    public void play() {
        System.out.println("mp4播放");
    }
}

public class Main {
    public static List<MediaPlayer> handlerAdapters = new ArrayList<>();
    static {
        handlerAdapters.add(new Mp3Player());
        handlerAdapters.add(new Mp4Player());
    }

    public static void main(String[] args) {
        MediaPlayer mediaPlayer = getHandlerAdapter(".mp3");
        mediaPlayer.play();

        mediaPlayer = getHandlerAdapter(".mp4");
        mediaPlayer.play();

        mediaPlayer = getHandlerAdapter(".mp4ds");
        mediaPlayer.play();
    }

    public static MediaPlayer getHandlerAdapter(String format){
        for (MediaPlayer mediaPlayer:handlerAdapters){
            if(mediaPlayer.supports(format)){
                return mediaPlayer;
            }
        }
        throw new RuntimeException("沒有找到合適的適配器");
    }
}

看完這個例子,看源碼秒懂
核心代碼如下:

 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 		//this.handlerAdapters有三個,分別是
 		//HttpRequestHandlerAdapter處理實現HttpRequestHandler的controller
 		//SimpleControllerHandlerAdapter處理實現Controller的controller
 		//RequestMappingHandlerAdapter處理@Controller的controller
 		///this.handlerAdapters何時初始化就不說了,和handlerMapping幾乎一樣
		 for(HandlerAdapter ha:this.handlerAdapters){
   			if (ha.supports(handler)) {
               return ha;
        	}
 		}
        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

三種adapter的support如下:

實現HttpRequestHandler的controller
public boolean supports(Object handler) {
        return handler instanceof HttpRequestHandler;
   }
   
實現Controller的controller
public boolean supports(Object handler) {
        return handler instanceof Controller;
   }
@Controller的controller
public final boolean supports(Object handler) {
        return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
    }

handle方法

本小節只介紹handle方法,即真正去處理邏輯的代碼,視圖解析就不說了,因爲現在都是基於rest的風格開發,試圖解析幾乎沒用了。
上一小姐說到了HandlerAdapter,不同的Adapter處理不同的controller
ha.handle有四個實現,如下

AbstractHandlerMethodAdapter 處理@Controller實現的controller
HttpRequestHandlerAdapter 處理實現HttpRequestHandler接口的controller
SimpleControllerHandlerAdapter 處理實現Controller接口的 controller
SimpleServletHandlerAdapter 處理實現Servlet接口的 controller(猜想,並沒有驗證)
後三者都是實現接口,都是回調接口方法,即可,沒什麼好說的,下面我們着重說AbstractHandlerMethodAdapter 
DispatcherServlet # ha.handle(processedRequest, response, mappedHandler.getHandler());---->

AbstractHandlerMethodAdapter # handle---->

RequestMappingHandlerAdapter # handleInternal(){
	//核心方法只有這行
	ModelAndView mav = this.invokeHandlerMethod(request, response, handlerMethod);
}---->

RequestMappingHandlerAdapter # invokeHandlerMethod(){
	//核心方法只有這行
	invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
}---->

ServletInvocableHandlerMethod # invokeAndHandle(){
	//invoke反射調用我們controller的mapping方法
	Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
	//用response響應數據(@ResponseBody)
	this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
}---->

InvocableHandlerMethod # invokeForRequest(){
	//處理controller方法的參數,和自定義ArgumentResovler,稍後舉例說明
	Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
	//拿到參數反射調用方法
	Object returnValue = this.doInvoke(args);
	return returnValue;
}---->

InvocableHandlerMethod # getMethodArgumentValues(){
	MethodParameter[] parameters = this.getMethodParameters();
        Object[] args = new Object[parameters.length];

        for(int i = 0; i < parameters.length; ++i) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = this.resolveProvidedArgument(parameter, providedArgs);
            if (args[i] == null) {
            	//又是一個support,mvc相當喜歡用這個設計模式,支持才做某某事,可以少些很多if else,並且擴展性也是大大提高,看來讀源碼確實非常提升內功,這些細小方法就不扣了,有興趣自己看
                if (this.argumentResolvers.supportsParameter(parameter)) {
					args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                }
            }
        }
        return args;
}

自定義ArgumentResolver實例
pom文件增加fastjson依賴

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.47</version>
    </dependency>

@User

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.PARAMETER})
public @interface User {

}

LoginUser實體

public class LoginUser {
    private int id;
    private String name;
    public LoginUser(int id,String name){
        this.id = id;
        this.name = name;
    }
	//get set toString 省略
}

MyArgumentResolver
支持supportsParameter才做resolveArgument

@Component
public class MyArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(LoginUser.class)
                &&
                parameter.hasParameterAnnotation(User.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        return new LoginUser(1,"lry");
    }
}

AppConfig

@Configuration
@ComponentScan("com.lry")
public class AppConfig extends WebMvcConfigurationSupport{
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new MyArgumentResolver());
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new FastJsonHttpMessageConverter());
    }
}

HelloController

	@RequestMapping("user")
    @ResponseBody
    //MyArgumentResolver 的resolveArgument方法的返回值會給user這個參數
    public LoginUser user(@User LoginUser user){
        return user;
    }

總結

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章