1. 环境搭建
代码已经上传至 https://github.com/masteryourself/spring-framework,工程是
tutorial-spring-web
2. 源码解析
详细的源码注释可参考 https://github.com/masteryourself/spring-framework
2.1 Spring 父子容器构造
2.1.1 流程分析
基于 servlet3.0 新特性,web 应用时会启动查找当前应用里每一个 jar 包里面的 ServletContainerInitializer 实例,然后调用它们的 onStartup()
方法
ServletContainerInitializer 的实现放在 jar 包的 META-INF/services
文件夹下,有一个名为 javax.servlet.ServletContainerInitializer
的文件,内容就是 ServletContainerInitializer 的实现类的全类名
在 spring-web-xxx.RELEASE.jar
里,META-INF/services/javax.servlet.ServletContainerInitializer
所对应的内容是 org.springframework.web.SpringServletContainerInitializer
,所以应用启动时会调用这个类的 onStartup()
方法
2.1.2 核心代码剖析
1. org.springframework.web.SpringServletContainerInitializer#onStartup
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...
// 如果此类不是接口,不是抽象类,加入到 initializers
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");
// 给 initializers 排序
AnnotationAwareOrderComparator.sort(initializers);
// 此时 initializers 里只有一个类,就是我们自定义的【MyWebAppInitializer】
for (WebApplicationInitializer initializer : initializers) {
// 调用每个类的 onStartup 方法
initializer.onStartup(servletContext);
}
}
2. org.springframework.web.context.AbstractContextLoaderInitializer#registerContextLoaderListener
protected void registerContextLoaderListener(ServletContext servletContext) {
// 创建 Spring 父容器
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
// 将【ContextLoaderListener】作为 listener 组件注入【servletContext】中,同时 ContextLoaderListener 保存了上面创建出来的【WebApplicationContext】
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
3. org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#registerDispatcherServlet
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
// 创建 Spring MVC 容器
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
// 创建 DispatcherServlet,其中关联了刚刚创建出来的【WebApplicationContext】,即 Spring MVC 子容器
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
// 将创建的 DispatcherServlet 添加到 ServletContext 中
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);
}
2.2 Spring 父子容器初始化
2.2.1 Spring 父容器初始化流程分析
2.2.2 核心代码剖析
1. org.springframework.web.context.ContextLoader#initWebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// servletContext 保存了一个属性,key 是 [org.springframework.web.context.WebApplicationContext.ROOT],value 即【WebApplicationContext】容器对象
// 这里首先判断有没有初始化过 Spring 容器,初始化完毕后这个属性不为空,就会抛出异常,防止重复初始化
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
...
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
// 判断有没有父容器,这里的 Spring 容器肯定没有父容器,下面的 loadParentContext() 方法也是空的
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 配置和刷新 Spring 容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 上面 Spring 容器已经刷新完毕了,给 servletContext 赋值,表示父容器已经初始化了
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
...
return this.context;
}
...
}
2. org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// 获取 Spring 容器,即从【ServletContext】中获取
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
// 当前的容器是 Spring MVC 容器
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
// 把 Spring 容器作为当前容器(Spring MVC)的父类,至此 Spring 父子容器关系建立
cwac.setParent(rootContext);
}
// 配置和刷新 Spring MVC 容器
configureAndRefreshWebApplicationContext(cwac);
}
}
}
...
return wac;
}
3. org.springframework.context.support.AbstractApplicationContext#refresh
参考这里的注释,详细的展示了 Spring 父子容器中的 bean 区别
/**
* spring 父子容器刷新问题
* 如果项目中有使用父子容器刷新,这个方法会进入两次,初始化两个完全不同的 beanFactory
* 仔细观察这两个容器中的 bean 对象,Spring 父容器中包含了【springConfig】、【demoService】
* 而 Spring MVC 子容器包含了【springMvcConfig】、【demoController】,从而职责分离
* <p>
* Spring 父容器中的 bean [singletonObjects]
* singletonObjects = {ConcurrentHashMap@3407} size = 17
* "springConfig" -> {SpringConfig@3444}
* "org.springframework.context.annotation.internalConfigurationAnnotationProcessor" -> {ConfigurationClassPostProcessor@3446}
* "contextAttributes" -> {Collections$UnmodifiableMap@3448} size = 9
* "org.springframework.context.event.internalEventListenerFactory" -> {DefaultEventListenerFactory@3450}
* "systemEnvironment" -> {Collections$UnmodifiableMap@3452} size = 64
* "org.springframework.context.event.internalEventListenerProcessor" -> {EventListenerMethodProcessor@3454}
* "lifecycleProcessor" -> {DefaultLifecycleProcessor@3490}
* "demoService" -> {DemoService@3456}
* "org.springframework.context.annotation.internalAutowiredAnnotationProcessor" -> {AutowiredAnnotationBeanPostProcessor@3458}
* "org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry" -> {ConfigurationClassParser$ImportStack@3460} size = 0
* "applicationEventMulticaster" -> {SimpleApplicationEventMulticaster@3334}
* "environment" -> {StandardServletEnvironment@2909} "StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[StubPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, JndiPropertySource {name='jndiProperties'}, MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}"
* "org.springframework.context.annotation.internalCommonAnnotationProcessor" -> {CommonAnnotationBeanPostProcessor@3464}
* "servletContext" -> {StandardContext$NoPluggabilityServletContext@2828}
* "contextParameters" -> {Collections$UnmodifiableMap@3467} size = 0
* "systemProperties" -> {Properties@3469} size = 82
* "messageSource" -> {DelegatingMessageSource@3324} "Empty MessageSource"
* <p>
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* <p>
* Spring MVC 子容器中的 bean
* singletonObjects = {ConcurrentHashMap@4486} size = 33
* "defaultServletHandlerMapping" -> {SimpleUrlHandlerMapping@4505}
* "org.springframework.context.annotation.internalConfigurationAnnotationProcessor" -> {ConfigurationClassPostProcessor@4941}
* "org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration" -> {DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$2e2e4cbb@4942}
* "org.springframework.context.event.internalEventListenerFactory" -> {DefaultEventListenerFactory@4943}
* "mvcUrlPathHelper" -> {UrlPathHelper@4944}
* "springMvcConfig" -> {SpringMvcConfig@4945}
* "systemEnvironment" -> {Collections$UnmodifiableMap@4946} size = 64
* "org.springframework.context.event.internalEventListenerProcessor" -> {EventListenerMethodProcessor@4947}
* "servletConfig" -> {StandardWrapperFacade@3387}
* "requestMappingHandlerMapping" -> {RequestMappingHandlerMapping@4503}
* "lifecycleProcessor" -> {DefaultLifecycleProcessor@4421}
* "requestMappingHandlerAdapter" -> {RequestMappingHandlerAdapter@4507}
* "org.springframework.context.annotation.internalAutowiredAnnotationProcessor" -> {AutowiredAnnotationBeanPostProcessor@4578}
* "org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry" -> {ConfigurationClassParser$ImportStack@4948} size = 0
* "applicationEventMulticaster" -> {SimpleApplicationEventMulticaster@3779}
* "mvcContentNegotiationManager" -> {ContentNegotiationManager@4949}
* "environment" -> {StandardServletEnvironment@3462} "StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ServletConfigPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, JndiPropertySource {name='jndiProperties'}, MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}"
* "httpRequestHandlerAdapter" -> {HttpRequestHandlerAdapter@4508}
* "beanNameHandlerMapping" -> {BeanNameUrlHandlerMapping@4504}
* "org.springframework.context.annotation.internalCommonAnnotationProcessor" -> {CommonAnnotationBeanPostProcessor@4577}
* "resourceHandlerMapping" -> {NullBean@4950} "null"
* "simpleControllerHandlerAdapter" -> {SimpleControllerHandlerAdapter@4509}
* "demoController" -> {DemoController@4951}
* "mvcValidator" -> {WebMvcConfigurationSupport$NoOpValidator@4952}
* "mvcResourceUrlProvider" -> {ResourceUrlProvider@4751}
* "viewControllerHandlerMapping" -> {NullBean@4953} "null"
* "systemProperties" -> {Properties@4954} size = 82
* "mvcConversionService" -> {DefaultFormattingConversionService@4955} "ConversionService converters =\n\t@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1ead6442,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4e7fd921\n\t@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@58b3b940,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@52d95c85\n\t@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@58b3b940,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAcces"
* "mvcPathMatcher" -> {AntPathMatcher@4956}
* "handlerExceptionResolver" -> {HandlerExceptionResolverComposite@4511}
* "mvcViewResolver" -> {ViewResolverComposite@4513}
* "mvcUriComponentsContributor" -> {CompositeUriComponentsContributor@4957}
* "messageSource" -> {DelegatingMessageSource@3771} "Empty MessageSource"
*
* @throws BeansException
* @throws IllegalStateException
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 1. 刷新前的预处理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2. 获取 beanFactory,即前面创建的【DefaultListableBeanFactory】
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3. 预处理 beanFactory,向容器中添加一些组件
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 4. 子类通过重写这个方法可以在 BeanFactory 创建并与准备完成以后做进一步的设置
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 5. 执行 BeanFactoryPostProcessor 方法,beanFactory 后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 6. 注册 BeanPostProcessors,bean 后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 7. 初始化 MessageSource 组件(做国际化功能;消息绑定,消息解析)
initMessageSource();
// Initialize event multicaster for this context.
// 8. 初始化事件派发器,在注册监听器时会用到
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 9. 留给子容器(子类),子类重写这个方法,在容器刷新的时候可以自定义逻辑,web 场景下会使用
onRefresh();
// Check for listener beans and register them.
// 10. 注册监听器,派发之前步骤产生的一些事件(可能没有)
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 11. 初始化所有的非单实例 bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 12. 发布容器刷新完成事件
finishRefresh();
}
...
}
}
2.3 Spring 容器获取 bean
这里不做细致分析,我的其他博客有详细分析 getBean()
过程
1. org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveBean
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
// 先从自己的 beanFactory 容器中获取 bean, 如果没有找到,再从父容器查找
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
// 获取当前的 parentBeanFactory,然后从 parentBeanFactory 工厂中获取 bean 对象
BeanFactory parent = getParentBeanFactory();
if (parent instanceof DefaultListableBeanFactory) {
// 再次调用 resolveBean() 方法获取 bean 实例
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
}
else if (parent != null) {
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
if (args != null) {
return parentProvider.getObject(args);
}
else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
}
}
return null;
}