1、创建一个maven工程,打包方式为war:由于工程中没有web.xml,而以war的形式打包工程时pom.xml文件会检查web.xml文件是否存在,不存在则会报错,此时需要加一个构建插件maven-war-plugin:设置为false即可
4.0.0
com.bdm
springmvc-anno
0.0.1-SNAPSHOT
war
org.apache.maven.plugins
maven-war-plugin
2.6
false
2、导入所有的依赖:导入spring-webmvc时会导入依赖的spring-context、spring-bean、spring-core等核心包
org.springframework
spring-webmvc
5.0.7.RELEASE
javax.servlet
javax.servlet-api
4.0.1
provided
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> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List 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的配置信息