Spring源码-SpringMVC

启动流程

一般使用SpringMVC都需要在Web.xml中配置这几个参数,下面来追踪一下具体的代码逻辑;
Spring源码-SpringMVC_第1张图片

ContextLoaderListener

由于web项目的启动是由Tomcat启动的, 不清楚会先调用这个类的哪一个方法,所以我找了一个看起来重要的方法打断点,再追踪它的调用栈, 可以发现在启动时由tomcat调用了contextInitialized()方法;我们主要关注initWebApplicationContext()方法;
Spring源码-SpringMVC_第2张图片
在ContextLoaderListener的static{}代码块中发现有这么一部分代码;
ContextLoader.properties 中包含这样的一个配置,由此我们可以猜测使用的Spring容器就是
XmlWebApplicationContext

Spring源码-SpringMVC_第3张图片
在这里插入图片描述
以下的代码是经过我整理之后的,只保留了主要的业务逻辑; 可以发现主要做了两件事情;
1.创建ApplicationContext;
2.初始化,完成Spring的生命周期;

	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			// 创建Spring上下文
			if (this.context == null) {
//			默认情况下	context=XmlWebApplicationContext;
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//完成Spring的生命周期
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
	}

继续追踪configureAndRefreshWebApplicationContext(cwac, servletContext); 同样,我对代码进行了整理;
1.获得在Web.xml中配置的属性;
2.定制上下文,我们可以自己实现ApplicationContextInitializer接口,对appcontext做功能定制;
使用方法:在web.xml中配置
在这里插入图片描述

3.调用refrsh()方法;----Spring的主要方法;不属于SpringMVC 这里不做分析;

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		wac.setServletContext(sc);
		//获取web.xml中配置的 contextConfigLocation 属性;
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);
		}
		//定制上下文
		customizeContext(sc, wac);
		wac.refresh();
	}

至此ContextLoaderListener启动完成; 在启动过程就会解析 classpath:applicationContext.xml 完成Spring容器初始化;

DispatcherServlet

DispatcherServlet 也是由Tomcat调用的,不知道会调用其中哪一个方法; 找一个看似主要的方法打断点;
可以发现由tomcat调用了init() --> initServletBean()方法;
Spring源码-SpringMVC_第4张图片
Spring源码-SpringMVC_第5张图片
再来追踪initWebApplicationContext; 这里有很多的判断,我只留下了主流程,更容易梳理逻辑;

	protected WebApplicationContext initWebApplicationContext() {
		//从ServletContext 中拿到 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 在ContextLoaderListener中加入的;
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			// 创建ApplicationContext
			wac = createWebApplicationContext(rootContext);
		}


		return wac;
	}

createWebApplicationContext(rootContext);方法中主要是重新创建了一个ApplicationContext 并把之前创建的 作为parent;

	protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		Class<?> contextClass = getContextClass();
		
		//创建父子容器;
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		String configLocation = getContextConfigLocation();
		if (configLocation != null) {
			wac.setConfigLocation(configLocation);
		}
		//执行context.refresh()
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

configureAndRefreshWebApplicationContext(wac); 设置一些属性; 着重注意红圈标注的;添加了一个ContextRefresh 事件监听器,在Spring容器启动完成后会调用!
Spring源码-SpringMVC_第6张图片
Spring容器启动完成后, 如果你对Spring的事件有所了解,最后执行到这里; 完成SpringMVC重要组件的初始化; 这里我们主要关注一个 处理器映射器, 处理器适配器的初始化;

	/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * 

May be overridden in subclasses in order to initialize further strategy objects. * 使用事件机制,在Spring 发布 ContextRefreshedEvent事件时执行; */ protected void initStrategies(ApplicationContext context) { //初始化文件上传解析 initMultipartResolver(context); //初始化国际化相关 initLocaleResolver(context); //初始化主题相关 initThemeResolver(context); //初始化处理器映射器 initHandlerMappings(context); //初始化处理器适配器 initHandlerAdapters(context); //初始化异常解析 initHandlerExceptionResolvers(context); // initRequestToViewNameTranslator(context); //视图解析器 initViewResolvers(context); // initFlashMapManager(context); }

处理器映射器初始化

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

getDefaultStrategies(context, HandlerMapping.class); 逻辑并不复杂,
1.从配置文件中获取所有的ClassName
2.加载Class
3.ApplicationContext.createBean(Class);
4.添加到lsit

	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			//按照, 分割,获取所有的ClassName
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);

			List<T> strategies = new ArrayList<>(classNames.length);

			for (String className : classNames) {
				try {
					//加载类
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					//通过ApplicationContext创建Bean
					Object strategy = createDefaultStrategy(context, clazz);
					//添加并返回
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<>();
		}
	}

String value = defaultStrategies.getProperty(key); 从这属性中获取value; 这个属性是什么时候赋值的呢? 可以发现在DispatcherServlet 静态代码块中加载了DispatcherServlet.properties文件; 而这个文件就配置了HandlerMapping, HandlerAdapter 的各种实现类;
Spring源码-SpringMVC_第7张图片
Spring源码-SpringMVC_第8张图片

Spring中可以作为处理器的实现方式:
1.beanName = 以/开头, 实现Controller接口
2.beanName = 以/开头,实现 HttpRequestHandler
3.beanName = 以/开头,实现 HandlerFunction
4.添加@Controller注解

处理器适配器初始化

实例化流程与处理器映射器相同,自行追踪;

到了这里ContextLoaderListener,DispatcherServlet 都已经实例化结束了; 觉得还缺点什么嘛?
1.我们正常使用加了@Controller注解的类 是怎么被执行到的?
2.执行的时候是怎么处理@RequestParam 等注解的?
3.执行成功后是怎么处理@ResponBody等注解的?

客户端访问,处理请求

处理请求是调用的 DispatcherServlet.doDispatch()方法;有以下几个主要流程:
1.processedRequest = checkMultipart(request); 检查请求是否为一个文件上传
2.mappedHandler = getHandler(processedRequest); 获得处理器执行链
3.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 获得adapter
4.mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 执行adapter.handler
5.processDispatchResult(); 处理返回结果

getHandler(processedRequest); 从所有的handlerMappings中遍历去获得handler; 还记得在什么时候初始化的handlerMappings吗? 我们取其中一个 BeanNameUrlHandlerMapping 举例子;
追踪 BeanNameUrlHandlerMapping.getHandler()方法;
Spring源码-SpringMVC_第9张图片
会调用到其父类的方法
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
Spring源码-SpringMVC_第10张图片
继续追踪发现是一个抽象方法,那么追踪子类的实现;
在这里插入图片描述
Spring源码-SpringMVC_第11张图片
这个方法从handlerMap 中获取,可以发现 handlerMap中存的就是我们自己写的 / 开头的 Controller;
有没有很疑惑? 在哪里解析的 / 开头的名字? 又在哪里添加进去的? 如果想知道这个流程,那我们得回到实例化HandlerMapping的时候; 这里就以 BeanNameUrlHandlerMapping 举例说明;
Spring源码-SpringMVC_第12张图片
Spring源码-SpringMVC_第13张图片
Spring源码-SpringMVC_第14张图片

BeanNameUrlHandlerMapping实例化

通过继承关系图,我们可以发现实现了很多Aware接口, 结合对Spring的了解;在Spring容器初始化过程中会调用各个实现了Aware接口的方法;我们尝试一个个追踪;
Spring源码-SpringMVC_第15张图片

ApplicationContextAware 接口有setApplicationContext 方法; 我们在BeanNameUrlMapping 类中搜索这个方法的具体实现;最终在其父类发现
org.springframework.context.support.ApplicationObjectSupport#setApplicationContext
经过之前的套路,大家知道应该主要看哪个方法吗?
initApplicationContext(context);

	@Override
	public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
		if (context == null && !isContextRequired()) {
			// Reset internal context state.
			this.applicationContext = null;
			this.messageSourceAccessor = null;
		}
		else if (this.applicationContext == null) {
			// Initialize with passed-in context.
			if (!requiredContextClass().isInstance(context)) {
				throw new ApplicationContextException(
						"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
			}
			this.applicationContext = context;
			this.messageSourceAccessor = new MessageSourceAccessor(context);
			initApplicationContext(context);
		}
		else {
			// Ignore reinitialization if same context passed in.
			if (this.applicationContext != context) {
				throw new ApplicationContextException(
						"Cannot reinitialize with different application context: current one is [" +
						this.applicationContext + "], passed-in one is [" + context + "]");
			}
		}
	}

org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping#initApplicationContext
先调用super;
Spring源码-SpringMVC_第16张图片
处理了实现 MappedInterceptor 接口的;
Spring源码-SpringMVC_第17张图片
找到了,在这里拿到Spring中所有的bean,判断如果beanName 以 / 开头,就保存起来;
逻辑并不复杂,大家自己追踪一下吧;
Spring源码-SpringMVC_第18张图片

RequestMappingHandlerMapping实例化

老套路,追踪aware的实现方法;
Spring源码-SpringMVC_第19张图片
追踪 org.springframework.context.ApplicationContextAware#setApplicationContext 没发现特别的逻辑;

追踪 org.springframework.web.context.ServletContextAware#setServletContext 也没啥特别的;

追踪 org.springframework.beans.factory.BeanNameAware#setBeanName 也没啥??

org.springframework.context.EmbeddedValueResolverAware#setEmbeddedValueResolver 也没有…

继续观察集成关系图,发现在Spring生命周期中,还有一个InitializingBean接口; 追踪看看;
发现了好东西;
Spring源码-SpringMVC_第20张图片
继续追踪会发现以下方法:
Spring源码-SpringMVC_第21张图片
processCandidateBean(beanName);
Spring源码-SpringMVC_第22张图片
加了@Controller 或者 @RequestMapping注解的类,才会处理;
Spring源码-SpringMVC_第23张图片
下面这个方法使用了函数接口,递归 不容易读. 可以简单理解成: 获取beanType的所有父类,循环所有的方法, 筛选出添加了@RequstMapping ,@GetMapping 等注解的方法,包装成新的类型; 并添加到集合中;
Spring源码-SpringMVC_第24张图片HandlerMapping的实例化完成, 再接上前面处理请求时, 获得HandlerChain的逻辑;

getHandlerAdapter(mappedHandler.getHandler());

获取到HandlerExecutionChain 后,要去获得Adapter; 逻辑差不多,调用各个adapter.supports()方法;匹配就返回;
Spring源码-SpringMVC_第25张图片
默认的Adapter有以下几种类型:这里主要介绍两个
1.SimpleControllerHandlerAdapter
2.RequestMappingHandlerAdapter

在这里插入图片描述
SimpleControllerHandlerAdapter 逻辑很简单,判断handler是否实现了Controller
Spring源码-SpringMVC_第26张图片
RequestMappingHandlerAdapter 比较复杂,下面是类继承关系图;
Spring源码-SpringMVC_第27张图片
追踪一下这些方法做了什么事情;
1.处理了@ControllerAdvice

2.初始化了参数解析器; 在前端传来参数时,可以解析@RequestParam,@RequestBody,@RequestHeader等注解

3…不知道

4.初始化了返回结果处理器; 在执行完方法返回时;可以处理@ResponBody等注解;

Spring源码-SpringMVC_第28张图片
5.我们经常使用的返回json数据,当然也是在这里初始化的; 当我们实例化RequestMappingHandlerAdapter时,在构造方法中初始化了MessageConverter;并且会依据我们是否加入
某些jar包,添加更多的消息转换器;
Spring源码-SpringMVC_第29张图片
Spring源码-SpringMVC_第30张图片
Spring源码-SpringMVC_第31张图片

Spring真的是一个非常优秀的框架, 在学习源码的过程中 切身体会到了Spring的工程师 让我们普通程序员少做了多少工作;

ha.handle(); 执行适配器方法

主要跟踪RequestMappingHandlerAdapter 的执行逻辑,其他的Adapter就是执行固定的接口方法;

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal()

你可能感兴趣的:(源码,日常学习,spring)