文章一直更新中......
众所周知,Spring mvc 将所有请求都在 DispatcherServlet 中处理,并且配置有一个监听器
在源码了解之前,我们需要知道的是服务器启动时候,listener filter servlet 执行顺序:---.大家可以自己去测试
1.listener 2.filter 3.servlet
(一).所以我们第一步先看一看监听器listener 做了什么事情?
1.实现了 ServletContextListener 接口 ,用监听servletcontext的动作
2.继承了ContextLoader ,执行根应用程序的上下文实际的初始化工作被 ContextLoaderListener(我们的监听器)所调用
所以在服务器启动时候,率先进行了 invoke initWebApplicationContext() method;
我们点进去看一看它的实现
因为是初始化--先进行了判断保证我们的servletcontext 是一个干净的servletcontext
执行流程:
1.private WebApplicationContext context;
初始化是null;
2.在createWebApplicationCotext()方法中 成员变量 context 被实例化为 class org.springframework.web.context.support.XmlWebApplicationContext 类型,这个类型实现了 ConfigurableWebApplicationContext 接口,也是必须要实现的;
ConfigurableWebApplicationContext 接口的描述:
*Interface to be implemented by configurable web application contexts.
* Supported by {@link ContextLoader} and
* {@link org.springframework.web.servlet.FrameworkServlet}.
*该接口由可配置的web 上下文所实现,对contextloader(监听器) frameworkservlet(servlet 容器)有支持,可以得知该接口专门为web 打造的;
3.isActive() 方法: 翻译看源码,我们并没有进行刷新的操作,所以返回默认值false
/** * Determine whether this application context is active, that is, * whether it has been refreshed at least once and has not been closed yet. * @return whether the context is still active * @see #refresh() * @see #close() * @see #getBeanFactory() */
确定是否这个应用上下文处于活动状态,也就是说它至少被刷新过一次,并尚未关闭
boolean isActive();
4.configureAndRefreshWebApplicationContext():完成bean 的解析,加载,初始化,很重要的操作;
只一步主要就是 refresh() 操作: 源码对此方法对解释 /** * Load or refresh the persistent representation of the configuration, * which might an XML file, properties file, or relational database schema. * <p>As this is a startup method, it should destroy already created singletons * if it fails, to avoid dangling resources. In other words, after invocation * of that method, either all or no singletons at all should be instantiated. * @throws BeansException if the bean factory could not be initialized * @throws IllegalStateException if already initialized and multiple refresh * attempts are not supported */
可以持久化的加载或者刷新配置文件,可能是XML 文件,也可能是properties 文件,或者是关系型数据库的模式
作为启动的方法,如果启动失败,他应该销毁已经创建的单例,去避免挂起资源; 换句话说,在调用这个方法后,所有实例化的和不实例化的
都应该被实例化
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
其中主要就是第2部和第11部:
obtainFreshBeanFactory():进行解析我们的xml 以及我们自定义的xml 标签,每一个<bean> 都是 BeanDefinitions对象,里面维护了bean的一些详细信息,方便实例化
注意,这一步并没有实例化bean ,只是解析XML ,将所有的eanDefinitions对象都放入了 beanDefinitionMap中;我们看它的实现
(-)使用默认的bean工厂DefaultListableBeanFactory, loadBeanDefinitions(beanFactory)
(二).使用XmlBean解析器去解析xml bean definition
(三). 注册bean
(四).这一步很重要了,开始干活了,解析xml
(五).循环遍历查找使用哪一种解析器,像一些默认的标签bean,alias,import,beans 会进入第一个方法;
像context ,aspect,dubbo 这种标签是属于自定义标签,只要符合Spring 规则就可以加载进行加载;下面我会说;
我们先看parseDefaultElement(ele,delegate)方法:
是不是提供了默认的标签bean,alias,import,beans的解析:
我们看它解析bean 的实现;源码多,具体需要自己去看,就是拿到,ID class 等信息:
bdholder 是一个装饰类,里面拿到了bean的所有信息
注册bean
注册bean:
点进去可以看到:其实就是将bean 的信息对象注册到我们DefaultListableBeanFactory 工厂维护的beanDefinitionMap中;名字放入了List<String> beanDefinitionNames 中
这里bean 的注册就完成了;
(六)关于自定义的XML 标签解析,也是Spring很有魅力的一个地方,像一些aop 标签,dubbo 标签都是通过自定义解析器去实现的,当然要遵循Spring 解析配置规则
才会被Spring扫描解析到,那么如何让才能让Spring解析我们自定义的标签呢?
以Spring-aop 包为例: 1.定义spring.handlers :键值对,里面存放的是我们组册解析我们自定义XML的类,有我们自己实现,会被反射实例化执行里面的方法,
2.spring.schemas :我们XML 所遵循的schemas,如果schemas地址是无效的,请META-INF目录下放入自己的xsd文件
看Spring 解析方法:handler 就是在Spring.handlers 对应的被反射实例化的对象,parse 方法就是对自定义标签的解析
实例化并调用init ,init 方法是将标签的属性进行注册存放到map 中,由parse 方法去获取并对应解析
自定义解析请看link:https://www.cnblogs.com/iscys/p/9905608.html
1.spring 加载的时候,使用的bean 工厂 是默认的 DefaultListableBeanFactory 对象
2.工厂内部维护着 Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);beanmap
3.解析XML 时候,会分别识别bean 标签,import标签,alias 标签,beans 标签并做不同的判断 注册bean时候调用了registerBeanDefinition()将bean 注册到 beanDefinitionMap中
beanDefinitionMap.put(beanName, beanDefinition);
关于第11步,是很重要的,会完成bean 的实例化,以及自定义InitializingBean aware 的接口实现的调用,
我单独写了文章;link:https://www.cnblogs.com/iscys/p/9903766.html
5.将 this.context 即XmlWebApplicationContext 放入servletContext上下文中,servlet 可以进行共享此applicattioncontext,以此达到共享bean 的目的
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
扩展:我们可以通过得知监听器给我们创建的applicationcontext 的类型
WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
System.out.println(rootContext.getClass());
(二).Servlet 的实现
我们看一看它的类层次结构:DispatcherServlet---> FrameworkServlet--> HttpServletBean ---> HttpServlet
可以得知底层其实实现了HttpServlet,其实就是一个servlet
给大家翻译一下 DispatcherServlet 文档:
/** * Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service * exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception * handling facilities.
处理http 请求处理器和控制器的中央调度器,例如,用于web ui 控制器或者基于http的远程;调度注册的处理程序为web 请求
提供便利的映射和异常处理;
* <p>This servlet is very flexible: It can be used with just about any workflow, with the installation of the * appropriate adapter classes. It offers the following functionality that distinguishes it from other request-driven * web MVC frameworks:
这个servlet 是非常精巧的:它几乎可以被任何的工作流所使用,只要安装了合适的适配器类,它提供了如下功能使之区别其他请求驱动的web mvc 框架
* <ul> <li>It is based around a JavaBeans configuration mechanism.
它是基于java bean 的配置机制的 * * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided as part of an application - to * control the routing of requests to handler objects. Default is {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} * and {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}. HandlerMapping objects * can be defined as beans in the servlet's application context, implementing the HandlerMapping interface, overriding * the default HandlerMapping if present. HandlerMappings can be given any bean name (they are tested by type).
它可以使用基于handlermapping 的实现,提前构建或者提供作为应用程序的一部分,来控制请求到处理对象之间的路由;默认提供了beannameurlhandlermapping
以及default annotationmapping ;处理器映射器对象可以被定义作为servlet 上下问对象的bean ;实现handlermapping 接口,覆盖默认的handlempping
handlermapping 会被赋予一个bean 名字;
* <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface. Default adapters are {@link * org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter}, {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter}, * for Spring's {@link org.springframework.web.HttpRequestHandler} and {@link org.springframework.web.servlet.mvc.Controller} * interfaces, respectively. A default {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} * will be registered as well. HandlerAdapter objects can be added as beans in the application context, overriding the * default HandlerAdapters. Like HandlerMappings, HandlerAdapters can be given any bean name (they are tested by type). 它可以使用一些处理器适配器;它允许使用任何处理接口
默认的适配器是 HttpRequestHandlerAdapter,simplecontrollerhandleradapter分别对应着 HttpRequestHandler,Controller handler接口
默认的annotationhandler也会被注册 处理器适配器对象也会在application 上下文加载进来为bean
* <li>The dispatcher's exception resolution strategy can be specified via a {@link HandlerExceptionResolver}, for * example mapping certain exceptions to error pages. Default are * {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver}, * {@link org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver}, and * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}. These HandlerExceptionResolvers can be overridden * through the application context. HandlerExceptionResolver can be given any bean name (they are tested by type). * * <li>Its view resolution strategy can be specified via a {@link ViewResolver} implementation, resolving symbolic view * names into View objects. Default is {@link org.springframework.web.servlet.view.InternalResourceViewResolver}. * ViewResolver objects can be added as beans in the application context, overriding the default ViewResolver. * ViewResolvers can be given any bean name (they are tested by type). * * <li>If a {@link View} or view name is not supplied by the user, then the configured {@link * RequestToViewNameTranslator} will translate the current request into a view name. The corresponding bean name is * "viewNameTranslator"; the default is {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}. * * <li>The dispatcher's strategy for resolving multipart requests is determined by a {@link * org.springframework.web.multipart.MultipartResolver} implementation. Implementations for Jakarta Commons FileUpload * and Jason Hunter's COS are included; the typical choise is {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}. * The MultipartResolver bean name is "multipartResolver"; default is none. * * <li>Its locale resolution strategy is determined by a {@link LocaleResolver}. Out-of-the-box implementations work via * HTTP accept header, cookie, or session. The LocaleResolver bean name is "localeResolver"; default is {@link * org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}. * * <li>Its theme resolution strategy is determined by a {@link ThemeResolver}. Implementations for a fixed theme and for * cookie and session storage are included. The ThemeResolver bean name is "themeResolver"; default is {@link * org.springframework.web.servlet.theme.FixedThemeResolver}. </ul> * * <p><b>NOTE: The {@code @RequestMapping} annotation will only be processed if a corresponding * {@code HandlerMapping} (for type level annotations) and/or {@code HandlerAdapter} (for method level * annotations) is present in the dispatcher.</b> This is the case by default. However, if you are defining custom * {@code HandlerMappings} or {@code HandlerAdapters}, then you need to make sure that a corresponding custom * {@code DefaultAnnotationHandlerMapping} and/or {@code AnnotationMethodHandlerAdapter} is defined as well - * provided that you intend to use {@code @RequestMapping}. * * <p><b>A web application can define any number of DispatcherServlets.</b> Each servlet will operate in its own * namespace, loading its own application context with mappings, handlers, etc. Only the root application context as * loaded by {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared. * * <p>As of Spring 3.1, {@code DispatcherServlet} may now be injected with a web * application context, rather than creating its own internally. This is useful in Servlet * 3.0+ environments, which support programmatic registration of servlet instances. See * {@link #DispatcherServlet(WebApplicationContext)} Javadoc for details. *
其实根据上一篇对servlet 详解,我们知道对于servlet 的初始化动作一定放在init 方法中,设置start 等级,这样就可以服务器启动,初始化servlet
/** * Map config parameters onto bean properties of this servlet, and * invoke subclass initialization. * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. */
将文件配置参数映射到servlet 的bean 属性上,并调用子类初始化;
@Override public final void init() throws ServletException {
关于我们的请求是经过什么的过程呢?下面这张图就是它的执行流程,网上一堆,那么我一个一个的给大家解析跟随源代码;你就不会迷茫了;
1.发送请求,这个就不用说了,哈哈,地址栏回车;接下来就是我们的DispatcherServlet操作了
2.映射处理器:熟悉servlet 都知道,请求第一步进入doService(request,response) 方法 ,就是这个方法。
里面会有一个doDispatch() ;这个我们可以理解为方法入口,进行分发 ;
所有的方法执行都在doDispatch()中;我们看一看获取HandlerMapping,就是这个方法,通过request 请求得到mappedHandler,是个 HandlerExecutionChain
执行链:
确定一个handler通过我们的request请求;
我们看一看getHandler()方法:会遍历我们注册过的handlermapping(handlerMapping是在初始化注册的) ;
执行getHandler(request);往后面点,可以看到它会根据地址去我们的urlMap中去找HandlerMethod ,就是执行这个url
所匹配的类名和方法;后期会被反射调用;
去看有没有拦截器之类的,并返回执行链:
其实可以得出,这个执行链就是一些需要执行方法的链路,就是一些类方法组成的;如下:
HandlerExecutionChain with handler [public void com.thirdparty.controller.gzh.WxConnectionController.wxConnection(javax.servlet.http.HttpServletResponse,java.lang.String,java.lang.String,java.lang.String,java.lang.String) throws java.lang.Exception] and 1 interceptor
到这里handlerMapping 就结束了并返回执行链;
3.开始获得处理器适配器; 得到执行链中的需要执行的methodhandler来匹配HandlerAdapter;
得到HandlerAdapter ,我们就可以通过这个适配器 去执行里面的方法;看第四步;
4.关键的方法调用就来了,通过反射调用执行的方法,返回ModelAndView
方法调用:
Java 反射:
这样,其实方法就执行完了;
5.视图解析