个人笔记,因有道需要会员,就用CSDN了,本人菜鸟,欢迎批评。
web.xml最简单配置
<!--1.1-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>
<!--1.2-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/dispatcher-servlet.xml</param-value>-->
<param-value>classpath*:dispatcher-servlet.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>
...
- 配置文件1.1处
listener标签添加了一个ContextLoaderListener,该职责在于提供WEB应用程序加载顶层的WebApplicationContext,像我们使用的数据源(dataSource),数据访问对象(Dao),数据服务对象(service)都注册在其中,即父容器。默认配置文件路径为/WEB-INF/applicationContext.xml,当然可以自定义配置文件路径,则采用标签 - 配置文件1.2处
DispatcherServlet则是用来加载springMVC在请求过程中涉及到的各个组件,例如:HandlerMapping,Controller,HandlerAdpater,ViewResolver等,默认加载文件路径为/WEB-INF/servlet-name-serblet.xml,servlet-name即为配置的,当然也可以自己定义,通过来指定。
认识WebApplicationInitializer,先来看一段代码
<!--这里也可以继承WebApplicationInitializer它的子类AbstractDispatcherServletInitializer-->
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
上面这一段代码就可以将web.xml替换掉了,但是dispatcher-config.xml仍然是基于xml配置的,这里可以使用AnnotationConfigWebApplicationContext替换XmlWebApplicationContext来达到目的。
<!--也可以通过继承AbstractAnnotationConfigDispatcherServletInitializer-->
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(DispatcherConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
虽然可以使用代码替换xml配置,但是这两者不是互斥的,可以同时使用,xml配置一个servlet,代码配置另外一个,只是有一点必须要保证,就是servler的版本是3.0或者更高,但是这样做没有必要。
实现细节:
在servelt3.0环境中,容器会在类路径中查找实现ServletContainerInitializer接口的类,如果发现了,就用它来配置servlet容器。spring提供了一个这个接口的实现类为SpringServletContainerInitializer,我们看一下这块代码,可以发现配置初始化是交给WebApplicationInitializer来完成的。
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
// 初始化
initializer.onStartup(servletContext);
}
}
我们来看一下AbstractAnnotationConfigDispatcherServletInitializer类继承结构
这里我追踪一下调用链路看一下
AbstractAnnotationConfigDispatcherServletInitializer并没有复写onStartUp()方法,所以我们查看它的父类AbstractDispatcherServletInitializer
super.onStartup会调用AbstractContextLoaderInitializer.onStartup的方法
代码思路很简单,我们如果自定义ApplicationContextInitializer只需要继承AbstractAnnotationConfigDispatcherServletInitializer,然后实现getRootConfigClasses,getServletConfigClasses以及getServletMappings即可, 这样就完全将web.xml以及dispatcher-servlet.xml全部替换掉了。
demo
public Class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
protected String[] getServletMappings(){
return new String[]{"/"};
}
protected Class<?>[] getRootConfigClasses(){
return new String[]{RootConfig.class};
}
protected Class<?>[] getServletConfigClasses(){
return new String[]{WebConfig.class};
}
}
@Configuration
@ComponentScan(basePackages={""},excludeFilters={@Filter(type=FilterType.ANNOTATION,value=EnableWebMvc.class)})
public Class RootConfig{
//TODO
}
@Configuration
@EnableWebMvc
@ComponentScan("")
public Class WebConfig{
//TODO
}
同时它也开放了一些方法留给子类覆盖实现,包括:
- protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers()
- protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers()
- protected Filter[] getServletFilters()
- protected void customizeRegistration(ServletRegistration.Dynamic registration)
第一个和第二个得到的ApplicationContextInitializer我后面再看源码学写下,第3个获取我们自定义的Filter,第4个我们可以进行重载对DispatcherServlet进行额外的配置。比如文件上传时设置临时路径,我们可以这么做:
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement(""));
}