SpringMVC 入口及父子容器源码解析
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
1 工程简介
描述:web工程
1.1 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<groupId>org.example</groupId>
<artifactId>rosh-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.8.RELEASE</spring.version>
</properties>
<dependencies>
<!--上下文-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!--事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring-mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 日志相关依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
</project>
1.2 配置文件
public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 父容器配置文件
*/
@Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("RoshWebInitializer invoke getRootConfigClasses");
return new Class<?>[]{SpringRootConfig.class};
}
/**
* 子容器配置
*/
@Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("RoshWebInitializer invoke getServletConfigClasses");
return new Class<?>[]{ SpringServletConfig.class};
}
/**
*拦截请求(静态资源、js、css.......)
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
/**
* 父容器配置文件,只扫描service
*/
@Configuration
@ComponentScan(value = "com.rosh.service")
public class SpringRootConfig {
}
/**
* 子容器配置文件,仅仅扫描@Controller、@RestController
*/
@Configuration
@ComponentScan(value="com.rosh",includeFilters={
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
@EnableWebMvc
public class SpringServletConfig {
}
1.3 HelloController
@RestController
@RequestMapping("/hello")
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("")
public String printHello() {
return helloService.printHello();
}
}
1.4 HelloService
@Service
public class HelloService {
public String printHello() {
return "Hello World";
}
}
1.5 启动tomcat
2 servlet3.0规范
在web容器启动时为提供给第三方组件机会做一些初始化的工作,servlet规范(JSR356)中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
2.1 spring-web包
/**
*
* servlet 初始化时,会加载实现WebApplicationInitializer接口的所有类,调取onStartup方法。
*
*/
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
/**
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
* implementations present on the application classpath.
* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
* Servlet 3.0+ containers will automatically scan the classpath for implementations
* of Spring's {@code WebApplicationInitializer} interface and provide the set of all
* such types to the {@code webAppInitializerClasses} parameter of this method.
* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
* this method is effectively a no-op. An INFO-level log message will be issued notifying
* the user that the {@code ServletContainerInitializer} has indeed been invoked but that
* no {@code WebApplicationInitializer} implementations were found.
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
* they will be instantiated (and <em>sorted</em> if the @{@link
* org.springframework.core.annotation.Order @Order} annotation is present or
* the {@link org.springframework.core.Ordered Ordered} interface has been
* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
* method will be invoked on each instance, delegating the {@code ServletContext} such
* that each instance may register and configure servlets such as Spring's
* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
* or any other Servlet API componentry such as filters.
* @param webAppInitializerClasses all implementations of
* {@link WebApplicationInitializer} found on the application classpath
* @param servletContext the servlet context to be initialized
* @see WebApplicationInitializer#onStartup(ServletContext)
* @see AnnotationAwareOrderComparator
*/
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
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 {
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);
}
}
}
2.2 RoshWebInitializer
描述:查看RoshWebInitializer类图,发现该类实现了WebApplicationInitializer接口,所以当servlet容器启动时,会加载该配置文件,并且执行onStartup方法。
public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 父容器配置文件
*/
@Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("RoshWebInitializer invoke getRootConfigClasses");
return new Class<?>[]{SpringRootConfig.class};
}
/**
* 子容器配置
*/
@Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("RoshWebInitializer invoke getServletConfigClasses");
return new Class<?>[]{ SpringServletConfig.class};
}
/**
*拦截请求(静态资源、js、css.......)
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
3 父容器源码解析
描述:打断点,debug。
3.1 创建父容器
描述:创建父容器(AnnotationConfigWebApplicationContext)、创建监听器。
protected void registerContextLoaderListener(ServletContext servletContext) {
/**
* 创建上下文
*/
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
/**
*创建监听器
*/
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
/**
* 把listener加入到上下文
*/
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
描述:创建父容器,直接new AnnotationConfigWebApplicationContext,加载父容器配置文件,然后返回。
@Override
@Nullable
protected WebApplicationContext createRootApplicationContext() {
/**
* 获取父容器配置文件
*/
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
/**
* 创建父容器设置父容器配置文件
*/
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
3.2 父容器初始化
3.2.1 监听器
描述:ContextLoaderListener 监听器,实现ServletContextListener接口。ServletContextListener接口包含两个方法,一个是contextInitialized()方法,用来监听ServletContext的启动和初始化;一个是contextDestroyed()方法,用来监听ServletContext的销毁。
3.2 ContextLoaderListener
描述:在servletContext容器初始化时,调用方法contextInitialized
描述:ioc容器初始化,可以看前面几篇文章。
/**
* 该方法是Spring容器初始化核心方法,采用模板设计模式。根据不同的上下文对象会调用不同对象子类方法。
*
* 核心上下文子类:
* AbstractXmlApplicationContext(XML上下文)
* AnnotationConfigApplicationContext(注解上下文)
*
*
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//容器初始化
prepareRefresh();
/**
* AbstractXmlApplicationContext:
*
* (1) 创建BeanFacotry
* (2) 解析Xml
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 给beanFactory设置一些值
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//完成对BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口的调用
invokeBeanFactoryPostProcessors(beanFactory);
//实现了BeanPostProcessor接口的实例化,并且加入到BeanFactory中
registerBeanPostProcessors(beanFactory);
// 国际化
initMessageSource();
// Initialize event multicaster for this context.
//初始化事件管理器
initApplicationEventMulticaster();
// 钩子方法
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/**
*
* bean的实例化、IOC
*
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
/**
* publicsh event 事件
*/
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
3.3 子容器初始化
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
/**
* 【1】 创建mvc上下文
*/
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
/**
* 【2】 创建DispatcherServlet对象,把springmvc上下文设置到dispatcherServlet中
*/
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
/**
* 【3】 把DispatcherServlet加入到servlet上下文中
*/
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);
}
3.3.1 创建子容器
描述:和父容器一样,获取子容器配置文件,创建AnnotationConfigWebApplicationContext,设置配置文件,返回。
3.3.2 子容器初始化
结论:DispatcherServlet继承了HttpServlet,子容器的初始化在servlet init方法中
3.3.3 设置父子容器
protected WebApplicationContext initWebApplicationContext() {
/**
* 【1】 从servletContext中获取父容器
*/
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
/**
* 【2】 获取springMvc 容器,子容器
*/
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
/**
*【3】 设置父子关系
*/
cwac.setParent(rootContext);
}
/**
* 【4】 启动容器
*/
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
3.3.4 初始化自容器其它属性
描述:当子容器完成bean的创建后,会触发publish事件初始化spring mvc 相关属性。
————————————————
版权声明:本文为CSDN博主「你携秋月揽星河丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/qq_34125999/article/details/115273102
粉丝福利:Java从入门到入土学习路线图
👇👇👇
👆长按上方微信二维码 2 秒
感谢点赞支持下哈