06.Spring Framework 源码解析之 web 启动原理

1. 环境搭建

代码已经上传至 https://github.com/masteryourself/spring-framework,工程是 tutorial-spring-web

2. 源码解析

详细的源码注释可参考 https://github.com/masteryourself/spring-framework

2.1 Spring 父子容器构造
2.1.1 流程分析

Spring 父子容器构造

基于 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 父容器初始化流程分析

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值