一、Servlet 3 扩展接口
1.1、ServletContainerInitializer 容器初始化接口
主要用于在容器启动阶段通过编程风格注册Filter、Servlet以及Listener,以取代通过web.xml配置注册
public interface ServletContainerInitializer {public void onStartup(Set<Class<?>> c, ServletContext ctx)throws ServletException;}
1.2、指定初始化类的注解 HandlesTypes
```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface HandlesTypes {
//指定初始化的类 Class<?>[] value(); }
<a name="P3LyO"></a>### 1.3、原理- Tomcat 启动时,通过SPI 机制,找到在路径META-INF/services/javax.servlet.ServletContainerInitializer下定义的初始化类,并实例化- 通过注解 @HandlesTypes 获取到自定义的初始化类- 执行调用 ServletContainerInitializer#onStartup- 执行链路
StandardContext #startInternal()
—>
StandardContext父类LifecycleBase#fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
—>
//LifecycleListener 实现ContextConfig
ContextConfig#lifecycleEvent(event)
—>
ContextConfig #configureStart();
—>
ContextConfig #webConfig();
—>
ContextConfig #processServletContainerInitializers
—>
//加载完,保存到 StandardContext#initializers中
WebappServiceLoader#load(Class serviceType)
—>
遍历StandardContext#initializers,执行ServletContainerInitializer
<a name="w7VJY"></a>## 二、纯 Java SpringMVC 配置<a name="icPB1"></a>### 2.1、实现WebApplicationInitializer```javapublic class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {/*** Spring Root上下文配置** @return*/protected Class<?>[] getRootConfigClasses() {return new Class[]{RootConfig.class};}/*** SpringMVC 配置** @return*/protected Class<?>[] getServletConfigClasses() {return new Class[]{WebMvcConfig.class};}/*** MVC 映射路径** @return*/protected String[] getServletMappings() {return new String[]{"/*"};}}
2.2、Root配置
@Configuration@ComponentScan(value = "cn.hdj.mvc", excludeFilters = {//排除扫描 controller@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})public class RootConfig {}
2.3、MVC 配置
@Configuration@ComponentScan(value = "cn.hdj.mvc.modules",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}, useDefaultFilters = false)@EnableWebMvcpublic class WebMvcConfig extends WebMvcConfigurerAdapter {/*** 配置视图解析器** @return*/@Beanpublic ViewResolver viewResolver() {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/view/");viewResolver.setSuffix(".jsp");return viewResolver;}/*** <!-- 配置静态资源用WEB容器默认的servlet来处理 -->* <mvc:default-servlet-handler/>* @param configurer*/@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}}
2.4、Spring 的ServletContainerInitializer 实现
2.4.1、在 mvc jar包中定义ServletContainerInitializer
- 文件路径 ``` META-INF\services\javax.servlet.ServletContainerInitializer
//文件内容 org.springframework.web.SpringServletContainerInitializer
- SpringServletContainerInitializer```java@HandlesTypes(WebApplicationInitializer.class)public class SpringServletContainerInitializer implements ServletContainerInitializer {@Overridepublic void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();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 {//实例化 WebApplicationInitializerinitializers.add((WebApplicationInitializer) 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) {//调用 WebApplicationInitializerinitializer.onStartup(servletContext);}}}
三、SpringMVC Java Config 启动整体过程
- Tomcat 启动时,通过SPI 机制,找到在路径META-INF/services/javax.servlet.ServletContainerInitializer下定义的初始化类,并实例化
- 通过注解 @HandlesTypes 获取到自定义的初始化类
- 执行调用 ServletContainerInitializer#onStartup
- 在 SpringServletContainerInitializer#onStartup中创建 ContextLoaderListener,并设置到ServletContext中
- 执行 ContextLoaderListener 监听器,初始化 Spring 上下文
四、简单内嵌Tomcat启动
4.1、添加依赖
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>8.5.81</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId><version>8.5.81</version></dependency>
4.2、启动
public class MvcJavaConfigApplication {public static void main(String[] args) throws LifecycleException {Tomcat tomcat = new Tomcat();Server server = tomcat.getServer();Service service = tomcat.getService();service.setName("Tomcat-embbeded");Connector connector = new Connector("HTTP/1.1");connector.setPort(8080);service.addConnector(connector);//应用目录tomcat.addWebapp("", System.getProperty("user.dir") + File.separator);server.start();System.out.println("tomcat服务器启动成功..");System.out.println("http://127.0.0.1:8080");server.await();}}
