说明
spring mvc 入口是通过配置Servlte来作为框架入口。servlte可以配置多个,每个Servlte都会初始化一个WebApplicationContext parent为root
注:httpServlet的init是 第一次访问的时候调用
配置例子
<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认 </init-param> --> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>spring2</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认 </init-param> --> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*/.springDo</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>spring2</servlet-name> <url-pattern>*/.spring2Do</url-pattern> </servlet-mapping>
类图
- GenericServlet 抽象类 提供简单的getInitParameter,getInitParameterNames,getServletContext等几方法内部都是通过config获取 同时将servlet的有参init重写 提供一个无参init给子类实现
- HttpServlet 抽象类重写了service方法 根据http协议根据请求方式路由到对应的业务处理方法 是抽象的需要子类实现
- HttpServletBean ,负责将 ServletConfig 设置到当前 Servlet 对象中。
- FrameworkServlet ,负责初始化 Spring Servlet WebApplicationContext 容器。
- DispatcherServlet ,负责初始化 Spring MVC 的各个组件,以及处理客户端的请求
- ApplicationContextAware, EnvironmentAware 是spring接口 我们实现这2个接口 如果是spring初始化这2个类 就会注入容器和环境变量信息
HttpServletBean
org.springframework.web.servlet.HttpServletBean#init
这里是重写的GenericServlet提供的init方法 而不是servlet接口的
public final void init() throws ServletException { if (this.logger.isDebugEnabled()) { this.logger.debug("Initializing servlet '" + this.getServletName() + "'"); } try { /** * <1> * 将DispatcherServlet配置的 <init-param> 封装到PropertyValues */ PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); /** * <2>这里this 是DispatcherServlet 将他转为BeanWrapper 对象 * beanWrapper是 spring框架内部操作java Bean的代理对象 实现依赖注入 */ BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext()); //注册属性编辑器 暂时不知道干嘛的 需要读了spring源码 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment())); //空实现 模板方法 子类可以实现做一些初始化操作 this.initBeanWrapper(bw); //将pvs注入到BeanWrapper 这个也是代理的原因 bw.setPropertyValues(pvs, true); } catch (BeansException var4) { this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4); throw var4; } //<311>空实现 需要子类实现 进行后续初始化操作 this.initServletBean(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully"); } }
<2>beanwapper的简单使用方式
public class User { String userName; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } public class BeanWrapperTest { public static void main(String[] args) { User user = new User(); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user); bw.setPropertyValue("userName", "张三"); System.out.println(user.getUserName()); PropertyValue value = new PropertyValue("userName", "李四"); bw.setPropertyValue(value); System.out.println(user.getUserName()); } }
FrameworkServlet
<311>initServletBean
org.springframework.web.servlet.HttpServletBean#init
->
org.springframework.web.servlet.FrameworkServlet#initServletBean
protected final void initServletBean() throws ServletException { this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //<3> 初始化webApplication context this.webApplicationContext = this.initWebApplicationContext(); //<4>模板方法 子类实现 framework并没有具体实现 是个空方法 this.initFrameworkServlet(); } catch (ServletException var5) { this.logger.error("Context initialization failed", var5); throw var5; } catch (RuntimeException var6) { this.logger.error("Context initialization failed", var6); throw var6; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
<3>initWebApplicationContext
org.springframework.web.servlet.HttpServletBean#init
->
org.springframework.web.servlet.FrameworkServlet#initServletBean
->
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() { //获得 rootContext 内部是获取ServletContext的attribute key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; //<5>防止重复初始化 不等于空时表示构造函数直接传入 if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac; //是否激活 if (!cwac.isActive()) { if (cwac.getParent() == null) { //设置父容器 cwac.setParent(rootContext); } //<6>servlte解析配置的spring xml执行容器初始化 this.configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { /** * <7>这里主要通过servlteContext获取指定key的context */ wac = this.findWebApplicationContext(); } if (wac == null) { /** * <8>这里主要获取FrameworkServlet contextClass属性的值 默认是 XmlWebApplicationContext.class */ wac = this.createWebApplicationContext(rootContext); } /** * <9>未触发刷新事件则触发 模板方法子类实现 dispatcher 做组件初始化动作 */ if (!this.refreshEventReceived) { this.onRefresh(wac); } /** * <10> 将 context 设置到 ServletContext 中 */ if (this.publishContext) { String attrName = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
<6>configureAndRefreshWebApplicationContext
/** * <6>先留一个标记 后面看完spring源码再回来 * @param wac */ protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { if (this.contextId != null) { wac.setId(this.contextId); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + "/" + this.getServletName()); } } wac.setServletContext(this.getServletContext()); wac.setServletConfig(this.getServletConfig()); wac.setNamespace(this.getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig()); } this.postProcessWebApplicationContext(wac); this.applyInitializers(wac); wac.refresh(); }
<7>findWebApplicationContext
org.springframework.web.servlet.HttpServletBean#init
->
org.springframework.web.servlet.FrameworkServlet#initServletBean
->
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
->
org.springframework.web.servlet.FrameworkServlet#findWebApplicationContext
protected WebApplicationContext findWebApplicationContext() { /** * 获得指定key 默认是空 * <11>签名使用beanWarpper注入配置的属性 我们可以通过配置文件配置 */ String attrName = this.getContextAttribute(); if (attrName == null) { return null; } else { //根据key从ServleContext的Attribute查找 WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } else { return wac; } } } }
<11>使用例子
<servlet> <servlet-name>wemall</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:application-mvc.xml </param-value> </init-param> <init-param> <param-name>contextAttribute</param-name> <param-value>contextKey</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
<8>createWebApplicationContext
org.springframework.web.servlet.HttpServletBean#init
->
org.springframework.web.servlet.FrameworkServlet#initServletBean
->
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
->
org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { /** * <12>从ContextClass 属性获取 容器的class 默认XMLWebApplicationContext */ Class<?> contextClass = this.getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } //判断是否是ConfigurableWebApplicationContext类型 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } else { //反射创建 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); wac.setParent(parent); wac.setConfigLocation(this.getContextConfigLocation()); //<6>执行容器初始化操作 this.configureAndRefreshWebApplicationContext(wac); return wac; } }
<12>例子
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; ..... public FrameworkServlet() { this.contextClass = DEFAULT_CONTEXT_CLASS; ..... } }
DispatcherServlet
<9>onRefresh
org.springframework.web.servlet.HttpServletBean#init
->
org.springframework.web.servlet.FrameworkServlet#initServletBean
->
org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
->
org.springframework.web.servlet.DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) { this.initStrategies(context); } /** * 初始化spring mvc组件 * @param context */ protected void initStrategies(ApplicationContext context) { //初始化上传文件Resolver this.initMultipartResolver(context); //初始化国际化Resolver 不同区域显示不同视图 this.initLocaleResolver(context); //初始化控制页面主题的Resolver this.initThemeResolver(context); //初始化url映射 mapping this.initHandlerMappings(context); //初始化Handler适配器 this.initHandlerAdapters(context); //初始化全局异常处理HandlerExceptionResolver this.initHandlerExceptionResolvers(context); this.initRequestToViewNameTranslator(context); this.initViewResolvers(context); this.initFlashMapManager(context); }
总结
1.服务器启动会调用servlet的init方法 对应入口就是HttpServlte
2.HttpServlteBean会负责将配置的变量通过BeanWarpper注入到DispactcherServlet
3.然后交给FrameworkServlet 做容器webApplication容器的创建
4.最后交给DispacherServlet做组件初始化