传统 XML 配置
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>WEB-INF/spring.xml</param-value></context-param><servlet><servlet-name>mvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>WEB-INF/web.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>mvc</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping></web-app>
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><mvc:annotation-driven/></beans>
为什么 Spring MVC 可以做到 0-XML 配置
因为 Servlet 3.0 出了一个规范,所有容器,再启动的时候,需要调用一个实现了 **ServletContainerInitializer 接口 下的 onStartup **方法,具体的实现类,需要再 META-INF.services 下新建一个 javax.servlet.ServletContainerInitializer **文件,内容为实现了 ServletContainerInitializer 接口的**完整类名,同时容器会扫描该类名上的 @HandlesTypes(XXX.class),并扫描 XXX.class 的所有实现类,并传递给 onStartup 方法,同时还会给 onStartup 方法传递一个容器上下文对象

public interface ServletContainerInitializer {public void onStartup(Set<Class<?>> c, ServletContext ctx)throws ServletException;}
利用这一点,我们就可以当容器启动时调用 onStartup 方法的时候,完成 Spring 容器的初始化步骤,同时配置 DispathServlet 对象
Spring-web 的实现
@HandlesTypes(WebApplicationInitializer.class)public class SpringServletContainerInitializer implements ServletContainerInitializer {@Overridepublic 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);}}}
所以,我们只需要实现 WebApplicationInitializer 接口就可以完成原来 web.xml 中的配置了
实现 Spring MVC 0-XML 配置 - web.xml
public class MyWebApplicationInitializer implements WebApplicationInitializer {/*** Spring MVC 0-XML 配置的原理:** 因为 servlet 3.0 的一个新规范(ServletContainerInitializer),* 而 tomcat 也遵守了了这个规范,所以会调用 实现了 WebApplicationInitializer 的 onStartup 方法** Spring 定义了一个 org.springframework.web.SpringServletContainerInitializer 类,实现了 servlet 3.0 的这个新规范** Spring 实现方式* spring-web\META-INF\services\javax.servlet.ServletContainerInitializer,是指需要实现的接口* javax.servlet.ServletContainerInitializer 文件里面的 org.springframework.web.SpringServletContainerInitializer 是实现类* @HandlesTypes(WebApplicationInitializer.class) 会由容器进行解析里面定义的接口,找出所有实现类,并回调给 Set<Class<?>> webAppInitializerClasses* Spring 再对 webAppInitializerClasses 的子类进行遍历,分别调用其 onStartup 的方法** @param servletContext Web上下文对象* @throws ServletException*/public void onStartup(ServletContext servletContext) throws ServletException {System.out.println("-------onStartup-------");// Load Spring web application configurationAnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();ac.register(AppConfig.class);// ac.refresh();// Create and register the DispatcherServlet// DispatcherServlet 的 init 方法DispatcherServlet servlet = new DispatcherServlet(ac);ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);registration.setLoadOnStartup(1);registration.addMapping("*.do");}}
实现 spring.xml 的 Java Configuration
通过 @EnableWebMvc 就可以完美的替代 spring.xml 中的
@Configuration@ComponentScan("org.wesoft.mvc")@EnableWebMvc // 相当于 XML 中的 <mvc:annotation-driven/>public class AppConfig implements WebMvcConfigurer {/*Apache Commons FileUploadTo use Apache Commons FileUpload,you can configure a bean of type CommonsMultipartResolver with a name of multipartResolver.You also need to have commons-fileupload as a dependency on your classpath.*/@Beanpublic CommonsMultipartResolver multipartResolver() {CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();return commonsMultipartResolver;}// 会在 Spring mvc 环境初始化完成之后,加载这个方法@Autowiredpublic void iniArgumentsResolvers(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(requestMappingHandlerAdapter.getArgumentResolvers());List<HandlerMethodArgumentResolver> customArgumentResolvers = requestMappingHandlerAdapter.getCustomArgumentResolvers();argumentResolvers.removeAll(customArgumentResolvers);argumentResolvers.addAll(0, customArgumentResolvers);requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);}/*** 视图处理** @param registry*/@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {registry.jsp("/page/", ".html");}/*** 添加消息解析器** @param converters*/@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();converters.add(fastJsonHttpMessageConverter);}/*** 添加参数解析器** @param resolvers initially an empty list*/@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {// 这里其实是传过来一个空集合,对应着 customArgumentResolvers// 也就是我们其实是在 customArgumentResolvers 里添加自定义方法// 然后 spring mvc 会将这个集合合并到 argumentResolvers 中,所以并不能保证执行顺序resolvers.add(new UserParamResolver());}}
内嵌 Tomcat
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.56'Xcompile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '8.5.56'
public class App {public static void main(String[] args) throws Exception {Tomcat tomcat = new Tomcat();tomcat.setPort(8080);// Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));// context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());String sourcePath = App.class.getResource("/").getPath();Context ctx = tomcat.addWebapp("/", new File("research-webmvc/src/webapp").getAbsolutePath());WebResourceRoot resources = new StandardRoot(ctx);resources.addPreResources(new DirResourceSet(resources, "/production/classes", sourcePath, "/"));ctx.setResources(resources);tomcat.start();tomcat.getServer().await();}}
