Spring MVC源码分析
spring MVC启动步骤及内容:
- 首先启动mvc需要tomcat加三个配置文件web.xml、applicationContext.xml和springmvc.xml文件
- 在web.xml中需要分别配置上<servlet>和<listenter>节点,分别映射着springmvc.xml文件和applicationContext.xml
<servlet>节点说明
示例:
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- tomcat服务器在启动时首先会创建一个DispatcherServlet服务,这个服务有着它自己特定的URL访问符号和映射符号。可理解为一个servlet容器或服务。
- 在servlet容器中根据springmvc.xml文件创建spring子容器ServletApplicationContext。
- servlet可以建多个。
<listener>节点说明
示例:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
- tomcat启动时便会开启listener监听,并根据applicationContext.xml创建spring父容器listenerApplicationContext。
- listener可不建。
一般在springmvc.xml中的注解扫描一般只扫描controller层,而applicationContext.xml中的注解扫描则是除controller层的所有层。为什么?
- ServletApplicationContext是与web浏览器请求地址相绑定的,ServletApplicationContext会根据前端请求地址去spring容器中去寻找对应的被@requestMapping标注的方法。而applicationContext.xml不用做到这一步,listenerApplicationContext只是需要跟普通spring容器一样根据具体业务提供对应的bean而已。
- 也就是说springmvc.xml中的bean是web层的,而applicationContext.xml中的bean是业务层的。
- 但spring MVC是web项目,所以controller层必须要,其他层不是必须品。且每个servlet都是独立的即每个web请求之间在不同的映射下是独立的。
- 所以根据tomcat的规范,将业务层的applicationContext.xml配置在listener节点中并作为所有servlet都可以调用的父容器,将springmvc.xml配置在独立的servlet中。所以servlet可以建多个,listener可不建。
- 得有一个其他的机制去启动父容器将servlet区分开。
上述关系图解:
摒弃所有配置文件(包括web.xml)的spring MVC:
1.只需实现WebApplicationInitializer接口并重写onStartup方法,并用配置类的方式指定父子容器(controller层和业务层)即可。
2.原理:
- tomcat是一个符合servlet规范的web容器。
- org.springframework.web.SpringServletContainerInitializer实现了javax.servlet.ServletContainerInitializer规范接口,所以tomcat在启动时会启动SpringServletContainerInitializer中的onStartup方法,并且将所有实现了WebApplicationInitializer接口的类当作入参传给SpringServletContainerInitializer中的onStartup方法执行。
注解:参考servlet标准规范:
如果在web程序中提供了/META-INF/services/org.springframework.web.SpringServletContainerInitializer这样三个层级的文件且文件中提供了实现了javax.servlet.ServletContainerInitializer的类,那么就必须执行这个类的onStartup方法。
所以org.springframework.web.SpringServletContainerInitializer会被执行。
3.WebApplicationInitializer源码示例:
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import springMVC.Configuration.ControllerConfig;
import springMVC.Configuration.ParentConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
//此处创建一个父容器,此容器中的对象无需web请求匹配,为所有容器共享
AnnotationConfigWebApplicationContext parent = new AnnotationConfigWebApplicationContext();
parent.setParent(parent);
//配置父容器的扫描层,即除开Controller层之外的业务层
parent.register(ParentConfig.class);
// 往下五行便对应web.xml中的一个servlet
// 此处为一个xml形式的的web spring容器,继承自AbstractApplicationContext
XmlWebApplicationContext xmpAppContext = new XmlWebApplicationContext();
xmpAppContext.setParent(parent);
xmpAppContext.setConfigLocation(".../.../...");
//创建servlet同时将spring容器添加至servlet中
DispatcherServlet xmlServlet = new DispatcherServlet(xmpAppContext);
ServletRegistration.Dynamic xmlRegistration = servletContext.addServlet("appXml", xmlServlet);
xmlRegistration.addMapping("/appXml/*");
//此处为Annotation形式的web spring容器,继承自AbstractApplicationContext
AnnotationConfigWebApplicationContext annotationAppContext = new AnnotationConfigWebApplicationContext();
annotationAppContext.setParent(parent);
//设置容器扫描的包,注解方式就是用java代码实现
annotationAppContext.register(ControllerConfig.class);
//创建servlet同时将spring容器添加至servlet中
DispatcherServlet annotationServlet = new DispatcherServlet(annotationAppContext);
ServletRegistration.Dynamic annotationRegistration = servletContext.addServlet("appAnnotation", annotationServlet);
annotationRegistration.addMapping("/appAnnotation/*");
}
}