【SpringMVC源码解析】 ServletContainerInitializer && SpringMVC源码入口解析
1 ServletContainerInitializer在web容器启动时为提供给第三方组件机会做一些初始化的工作,servlet规范(JSR356)中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.S.
1 ServletContainerInitializer
在web容器启动时为提供给第三方组件机会做一些初始化的工作,servlet规范(JSR356)中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
(1) 创建JAVA-WEB项目
(2) 创建接口及接口相关类
public interface ZrsService {
}
public interface ZrsServiceExtend extends ZrsService {
}
public abstract class AbstractZrsService implements ZrsService {
}
public class ZrsServicImpl implements ZrsService {
}
(3) 创建ServletContainerInitializer接口实现类
/**
* @Description: HandlesTypes,获取当前接口所有相关的类
* @Auther: zhurongsheng
* @Date: 2020/3/5 15:14
*/
@HandlesTypes(value = {ZrsService.class})
public class ZrsServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> classs, ServletContext servletContext) throws ServletException {
System.out.println("################################");
for (Class clazz:classs){
System.out.println(clazz);
}
}
}
(4) 在META-INF/services 下创建 javax.servlet.ServletContainerInitializer
com.servlet.ZrsServletContainerInitializer
(5) 工程结构
(6) 启动tomcat,发现调用了ZrsServletContainerInitializer onStartup方法
2 SpringMVC环境准备
(1) 创建maven 工程
(2) 删除webapp下所有*.xml文件
(3) 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<extdirs>${basedir}/src/main/webapp/WEB-INF/lib</extdirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
3 查看spring-web包
4 初步分析SpringServletContainerInitializer
(1) 源码分析,初始化WebApplicationInitializer接口的bean,并且调用WebApplicationInitializer实现类的onStartup方法
/**
* 获取WebApplicationInitializer接口的所有类型
*/
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
//1 创建存储WebApplicationInitializer的List
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 {
//2 用反射机制创建WebApplicationInitializer实现类并且添加到List中
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);
//3 调用WebApplicationInitializerde的onStartup方法
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
(2)WebApplicationInitializer接口实现类分析 AbstractContextLoaderInitializer
(3) WebApplicationInitializer接口实现类分析 AbstractDispatcherServletInitializer
(4) WebApplicationInitializer接口实现类分析 AbstractAnnotationConfigDispatcherServletInitializer
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
/**
* 创建RootApplicationContext
*/
@Override
@Nullable
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
/**
* 创建ServletApplicationContext
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
context.register(configClasses);
}
return context;
}
/**
* 获取RootApplicationContext 配置类
*/
@Nullable
protected abstract Class<?>[] getRootConfigClasses();
/**
* 获取ServletApplicationContext配置类
*/
@Nullable
protected abstract Class<?>[] getServletConfigClasses();
}
(5) ServletApplicationContext和RootApplicationContext区别如下图:
ServletApplicationContext:负责Controller 视图相关的…
RootApplicationContext:负责Service、数据库相关的…
5 AbstractAnnotationConfigDispatcherServletInitializer作用解析
5.1 给ServletWebApplicationContext添加ServletConfig配置文件
(1) 创建配置文件
/**
* @Description: ServletConfig配置文件,ServleWebApplicationContext仅包含Controller方面的注解
* @Auther: zhurongsheng
* @Date: 2020/3/6 14:44
*/
@ComponentScan(value="com.spring",includeFilters={
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
public class ZrsServletConfig {
}
(2) 创建Controller
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping("")
public String hello(){
return "hello spring mvc";
}
}
(3) ZrsWebInitializer
/**
* @Description: web容器启动创建时的对象
* @Auther: zhurongsheng
* @Date: 2020/3/5 23:20
*/
public class ZrsWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* RootApplicationContext 配置文件
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{};
}
/**
* ServletApplicationContext 配置文件
*/
@Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("ZrsWebInitializer invoke getServletConfigClasses");
return new Class<?>[]{ ZrsServletConfig.class};
}
/**
*拦截请求(静态资源、js、css.......)
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
(4) 启动tomcat
(5) 访问URL
http://localhost:8080/spring_web_war/hello
5.2 @EnableWebMvc注解,等于在xml中配置
<!-- 拦截请求,看是否是静态资源,如果不是,交给前端控制器 -->
<mvc:default-servlet-handler/>
<!-- 提供controller,json,静态资源 -->
<mvc:annotation-driven></mvc:annotation-driven>
5.3 WebMvcConfigurer接口代替了springmvc在xml中的配置
(1) 视图解析器
修改ZrsServletConfig配置文件
@ComponentScan(value="com.spring",includeFilters={
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
@EnableWebMvc
public class ZrsServletConfig implements WebMvcConfigurer {
/**
* 配置视图解析器
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/pages/",".jsp");
}
}
修改controller
@Controller
@RequestMapping("/hello")
public class HelloController {
@GetMapping("")
public String hello(){
return "hello spring mvc";
}
@GetMapping("/page")
public String page(){
return "helloworld";
}
}
创建jsp
启动tomcat
(2) 配置静态资源
@ComponentScan(value="com.spring",includeFilters={
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
@EnableWebMvc
public class ZrsServletConfig implements WebMvcConfigurer {
/**
* 配置视图解析器
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/pages/",".jsp");
}
/**
* 静态资源开关
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
创建jsp、css:
启动tomcat:
更多推荐
所有评论(0)