Spring MVC笔记(二) -- 启动

xiaoxiao2021-03-01  18

servlet相关知识可参考:Java Web(一) Servlet详解!!

一、ContextLoaderListener

Spring MVC的web.xml部分配置如下:

<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

servlet容器在启动的使用会调用ServletContextListener接口的contextInitialized方法。ContextLoaderListener就实现了该接口

看ContextLoaderListener类的contextInitialized

public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); }

initWebApplicationContext方法在父类ContextLoader中

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { //不能重复设置容器 } ... long startTime = System.currentTimeMillis(); try { if (this.context == null) { //创建容器 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { ... //刷新容器 configureAndRefreshWebApplicationContext(cwac, servletContext); } } ... //设置容器 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ... return this.context; } catch... } protected WebApplicationContext createWebApplicationContext(ServletContext sc) { //默认返回XmlWebApplicationContext Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { //抛异常 } //反射生成实例 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }

ContextLoaderListener想做的事情是创建一个IOC容器,首先会先检查servletContext是否已包含IOC容器,没有就新创建一个,默认会读取ContextLoader.properties配置文件,得到是XmlWebApplicationContext的实例容器。 ContextLoader.properties内容如下:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

容器创建完成后就开始初始化刷新容器

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { //获取contextId属性 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { //使用默认的ID wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } wac.setServletContext(sc); //获取配置文件路径,对应属性contextConfigLocation,即上面的classpath*:applicationContext.xml String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { //设置配置文件 wac.setConfigLocation(configLocationParam); } ... //这里开始读取配置文件,初始化bean等等 wac.refresh(); }

二、DispatcherServlet

<servlet> <servlet-name>season</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--Spring MVC容器--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>

servlet的生命周期由servlet的容器来控制,分3个阶段:初始化、运行、销毁。初始化会调用servlet的init(ServletConfig config)方法

DispatcherServlet实现了Servlet接口,其init方法在父类HttpServletBean中

public final void init() throws ServletException { //获取并验证属性 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); //注册自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //留给子类实现 initBeanWrapper(bw); //属性注入,例如contextConfigLocation属性 bw.setPropertyValues(pvs, true); } catch... } //留给子类扩展 initServletBean(); }

FrameworkServlet类

protected final void initServletBean() throws ServletException { ... long startTime = System.currentTimeMillis(); try { //初始化容器 this.webApplicationContext = initWebApplicationContext(); //留给子类实现 initFrameworkServlet(); } catch... } protected WebApplicationContext initWebApplicationContext() { //拿到在ContextLoaderListener那里实例化的容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; 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); } //刷新容器(解析配置文件,bean初始化等) configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { //查找容器 wac = findWebApplicationContext(); } if (wac == null) { //创建容器 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { //刷新容器(解析配置文件,bean初始化等) onRefresh(wac); } if (this.publishContext) { // 保存到ServletContext String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }

FrameworkServlet类覆盖了HttpServletBean中的initServletBean()方法,来完成Spring MVC容器的初始化,并把容器的父容器设置为ContextLoaderListener实例化的容器。

创建容器方法如下:

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); } protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //默认是XmlWebApplicationContext类 Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw... } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); //拿到contextConfigLocation属性 String configLocation = getContextConfigLocation(); if (configLocation != null) { wac.setConfigLocation(configLocation); } //刷新容器 configureAndRefreshWebApplicationContext(wac); return wac; }

SpringMVC会存在两个容器,一个是在ContextLoaderListener那实例化,一个在DispatcherServlet实例化

DispatcherServlet实例化容器的时候会调用configureAndRefreshWebApplicationContext方法

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { if (this.contextId != null) { wac.setId(this.contextId); } else { // 默认id (ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); //添加事件监听 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); //加载配置文件及整合parent到wac wac.refresh(); }

其中添加的刷新的监听事件如下

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { FrameworkServlet.this.onApplicationEvent(event); } } public void onApplicationEvent(ContextRefreshedEvent event) { this.refreshEventReceived = true; onRefresh(event.getApplicationContext()); }

其中DispatcherServlet重写了onRefresh方法

protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { //初始化MultipartResolver(文件处理) initMultipartResolver(context); //初始化LocaleResolver(语言处理) initLocaleResolver(context); //初始化ThemeResolver(主题处理) initThemeResolver(context); //初始化HandlerMappings initHandlerMappings(context); //初始化HandlerAdapters initHandlerAdapters(context); //初始化HandlerExceptionResolvers(异常处理) initHandlerExceptionResolvers(context); //初始化RequestToViewNameTranslator initRequestToViewNameTranslator(context); //初始化ViewResolvers(视图解析) initViewResolvers(context); //初始化FlashMapManager(重定向,参数处理) initFlashMapManager(context); }

DispatcherServlet会在容器加载完配置文件和bean之后,在刷新的时候初始化好各个组件

转载请注明原文地址: https://www.6miu.com/read-3849989.html

最新回复(0)