SpringMVC 入口及父子容器源碼解析
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
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};
}
/**
*攔截請求(靜態(tài)資源、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規(guī)范
在web容器啟動時為提供給第三方組件機會做一些初始化的工作,servlet規(guī)范(JSR356)中通過ServletContainerInitializer實現(xiàn)此功能。每個框架要使用ServletContainerInitializer就必須在對應的jar包的META-INF/services 目錄創(chuàng)建一個名為javax.servlet.ServletContainerInitializer的文件,文件內(nèi)容指定具體的ServletContainerInitializer實現(xiàn)類,那么,當web容器啟動時就會運行這個初始化器做一些組件內(nèi)的初始化工作。
2.1 spring-web包

/**
*
* servlet 初始化時,會加載實現(xiàn)WebApplicationInitializer接口的所有類,調(diào)取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類圖,發(fā)現(xiàn)該類實現(xiàn)了WebApplicationInitializer接口,所以當servlet容器啟動時,會加載該配置文件,并且執(zhí)行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};
}
/**
*攔截請求(靜態(tài)資源、js、css.......)
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
3 父容器源碼解析
描述:打斷點,debug。


3.1 創(chuàng)建父容器
描述:創(chuàng)建父容器(AnnotationConfigWebApplicationContext)、創(chuàng)建監(jiān)聽器。
protected void registerContextLoaderListener(ServletContext servletContext) {
/**
* 創(chuàng)建上下文
*/
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
/**
*創(chuàng)建監(jiān)聽器
*/
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");
}
}

描述:創(chuàng)建父容器,直接new AnnotationConfigWebApplicationContext,加載父容器配置文件,然后返回。

@Override
@Nullable
protected WebApplicationContext createRootApplicationContext() {
/**
* 獲取父容器配置文件
*/
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
/**
* 創(chuàng)建父容器設置父容器配置文件
*/
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
3.2 父容器初始化
3.2.1 監(jiān)聽器
描述:ContextLoaderListener 監(jiān)聽器,實現(xiàn)ServletContextListener接口。ServletContextListener接口包含兩個方法,一個是contextInitialized()方法,用來監(jiān)聽ServletContext的啟動和初始化;一個是contextDestroyed()方法,用來監(jiān)聽ServletContext的銷毀。

3.2 ContextLoaderListener
描述:在servletContext容器初始化時,調(diào)用方法contextInitialized



描述:ioc容器初始化,可以看前面幾篇文章。

/**
* 該方法是Spring容器初始化核心方法,采用模板設計模式。根據(jù)不同的上下文對象會調(diào)用不同對象子類方法。
*
* 核心上下文子類:
* AbstractXmlApplicationContext(XML上下文)
* AnnotationConfigApplicationContext(注解上下文)
*
*
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//容器初始化
prepareRefresh();
/**
* AbstractXmlApplicationContext:
*
* (1) 創(chuàng)建BeanFacotry
* (2) 解析Xml
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 給beanFactory設置一些值
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//完成對BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口的調(diào)用
invokeBeanFactoryPostProcessors(beanFactory);
//實現(xiàn)了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】 創(chuàng)建mvc上下文
*/
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
/**
* 【2】 創(chuàng)建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 創(chuàng)建子容器
描述:和父容器一樣,獲取子容器配置文件,創(chuàng)建AnnotationConfigWebApplicationContext,設置配置文件,返回。


3.3.2 子容器初始化
結(jié)論: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的創(chuàng)建后,會觸發(fā)publish事件初始化spring mvc 相關屬性。



————————————————
版權聲明:本文為CSDN博主「你攜秋月攬星河丶」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/qq_34125999/article/details/115273102
粉絲福利:Java從入門到入土學習路線圖
??????

??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
