1. 程式人生 > >Servlet3.0——整合SpringMVC

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的配置資訊