Servlet3.0——整合SpringMVC
1、建立一個maven工程,打包方式為war:由於工程中沒有web.xml,而以war的形式打包工程時pom.xml檔案會檢查web.xml檔案是否存在,不存在則會報錯,此時需要加一個構建外掛maven-war-plugin:設定為false即可
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bdm</groupId> <artifactId>springmvc-anno</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.6</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project>
2、匯入所有的依賴:匯入spring-webmvc時會匯入依賴的spring-context、spring-bean、spring-core等核心包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<!-- 由於tomcat已經有了servlet-api,因此設定為provided,表示目標環境已經有該jar包,在打war包時不會將此包打入,避免衝突 -->
<scope>provided</scope>
</dependency>
</dependencies>
3、在匯入的jar包中有這樣一個檔案:
內容為:
org.springframework.web.SpringServletContainerInitializer
web容器在啟動的時候,會掃描每個jar包下的META-INF/services/javax.servlet.ServletContainerInitializer,並可以利用它去註冊一些web元件,SpringServletContainerInitializer的程式碼:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
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);
}
}
}
可以看出在SpringServletContainerInitializer中會將WebApplicationInitializer介面的所有實現類(非抽象類)例項化,並依次執行其例項的onStartup(servletContext)方法,因此我們可以通過實現WebApplicationInitializer介面或者繼承其子類,並在其子類中註冊Spring容器配置類和SpringMVC配置類的方式來達到實現web工程的目的。
4、spring-mvc中自帶了一些WebApplicationInitializer介面的抽象實現類,所以只需要實現這些抽象類即可
1)、AbstractContextLoaderInitializer:建立根容器,createRootApplicationContext()是一個抽象方法,供子類實現
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
@Nullable
protected abstract WebApplicationContext createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
①建立一個web的ioc容器:createServletApplicationContext(),抽象方法,供子類實現
②建立DispatcherServlet:createDispatcherServlet()
③將建立的DispatcherServlet新增到ServletContext中
④getServletMappings();
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
protected abstract WebApplicationContext createServletApplicationContext();
3)、AbstractAnnotationConfigDispatcherServletInitializer:子類通過實現其抽象方法返回註解方式的根容器和Servlet子容器,從而實現註解方式配置DispatcherServlet初始化器(SpringMVC的容器)和根容器(Spring的IOC容器)
建立根容器:createRootApplicationContext(),子實現類通過實現getRootConfigClasses();方法返回一個配置類
建立web的IOC容器: createServletApplicationContext();子實現類通過實現getServletConfigClasses();返回一個web的IOC容器配置類
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
@Override
@Nullable
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
context.register(configClasses);
}
return context;
}
@Nullable
protected abstract Class<?>[] getRootConfigClasses();
@Nullable
protected abstract Class<?>[] getServletConfigClasses();
}
其實是註冊了兩個互相獨立的IOC容器:一個用來裝載service、repository等元件,一個用來裝載controller元件;以註解方式來啟動SpringMVC,只需要繼承AbstractAnnotationConfigDispatcherServletInitializer,實現其抽象方法來指定DispatcherServlet的配置資訊