一、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
```java
public 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)
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
/**
* 配置视图解析器
*
* @return
*/
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/**
* <!-- 配置静态资源用WEB容器默认的servlet来处理 -->
* <mvc:default-servlet-handler/>
* @param configurer
*/
@Override
public 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 {
@Override
public 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 {
//实例化 WebApplicationInitializer
initializers.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) {
//调用 WebApplicationInitializer
initializer.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();
}
}