Spring bean上下文环境在web环境的初始化概述

阅读更多
 
 
spring web服务在 tomcat容器中启动的流程
 
 
在介绍 spring启动流程之前,先介绍 2个概念 servlet ServletContext
servlet java web服务的核心组件,可以简单理解为 servlet是能接收并处理 web请求的服务(典型的就是 http请求)
ServletContext 是生成并维护 servlet的上下文,存在于特定容器之中,如 tomcatjettyresin等等
 
容器启动过程中解析 web.xml 文件,它描述了一个 web服务的关键信息,一般情况下包括 servlet filterlistener
 
容器启动时将通知相关的 listener,一般情况下 spring环境就是这个时候引入的。
执行相关 Servlet(如果设置为容器启动时执行)
filter初始化并执行 init方法
filterservlet接收请求过程中之前都会执行
 
 
下面介绍 spring 是如何在 web环境中被初始化的
 
StandardContext.listenerStart() 启动 web.xml 中配置的监听器(这个监听器在容器初始化时执行)
这里的 StandardContext就是 tomcat容器中的 servlet环境
 
 
 
 
 
 
 
 
 
 
 
从上面可以看出,启动过程为, tomcat容器读取 web.xml 初始化 servlet容器 StandardContext,配置 listenerfilterservlet,初始化成功后触发相应的监听器, servlet(如果设置为 load-on-startup
 
 
 
现在看集成 springweb项目一般的 web.xml示例:
 
xml version = "1.0" encoding = "UTF-8" ?>
< web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns = "http://java.sun.com/xml/ns/javaee" xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version = "3.0" >
< context-param >
< param-name > webAppRootKey param-name >
< param-value > zhixiao.root param-value >
context-param >
< session-config >
< session-timeout > 10 session-timeout >
session-config >
< welcome-file-list >
< welcome-file > index.jsp welcome-file >
welcome-file-list >
< display-name > parking Created Web Application display-name >
< context-param >
< param-name > contextConfigLocation param-name >
< param-value > /WEB-INF/spring/*. xml param-value >
context-param >
< listener >
< listener-class > org.springframework.web.util.Log4jConfigListener listener-class >
listener >
< listener >
< listener-class > org.springframework.web.context.ContextLoaderListener listener-class >
listener >
org.springframework.web.context.request.RequestContextListener
< listener >
< listener-class > org.springframework.web.util.IntrospectorCleanupListener listener-class >
listener >
< filter >
< filter-name > encodingFilter filter-name >
< filter-class > org.springframework.web.filter.CharacterEncodingFilter filter-class >
< init-param >
< param-name > encoding param-name >
< param-value > UTF-8 param-value >
init-param >
< init-param >
< param-name > forceEncoding param-name >
< param-value > true param-value >
init-param >
filter >
< filter-mapping >
< filter-name > encodingFilter filter-name >
< url-pattern > /* url-pattern >
filter-mapping >
< filter >
< display-name > RedisSessionFilter display-name >
< filter-name > RedisSessionFilter filter-name >
< filter-class > com.qhyu.zhixiao.session.RedisSessionFilter filter-class >
filter >
< filter-mapping >
< filter-name > RedisSessionFilter filter-name >
< url-pattern > /* url-pattern >
< dispatcher > REQUEST dispatcher >
< dispatcher > FORWARD dispatcher >
filter-mapping >
< filter >
< filter-name > ipFilter filter-name >
< filter-class > com.qhyu.zhixiao.filter.IpFilter filter-class >
filter >
< filter-mapping >
< filter-name > ipFilter filter-name >
< url-pattern > /* url-pattern >
< dispatcher > REQUEST dispatcher >
< dispatcher > FORWARD dispatcher >
filter-mapping >
 
< servlet >
< servlet-name > springmvc servlet-name >
< servlet-class > org.springframework.web.servlet.DispatcherServlet servlet-class >
< load-on-startup > 1 load-on-startup >
servlet >
< servlet-mapping >
< servlet-name > springmvc servlet-name >
< url-pattern > / url-pattern >
servlet-mapping >
< servlet-mapping >
< servlet-name > default servlet-name >
< url-pattern > *. css url-pattern >
servlet-mapping >
web-app >
 
 
 
下面重点看 ContextLoaderListener
 
这个 listenertomcat容器启动时执行
 
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {}
public ContextLoaderListener(WebApplicationContext context ) {
super ( context );
}
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event ) {
initWebApplicationContext( event .getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event ) {
closeWebApplicationContext( event .getServletContext());
ContextCleanupListener. cleanupAttributes ( event .getServletContext());
}
}
 
initWebApplicationContext 方法就是初始化 spring 的入口,参数就是 tomcat 容器初始化的 servlet
 
 
进入
initWebApplicationContext 方法主要干了两件事
1. 初始化 WebApplicationContext
WebApplicationContext context = null;
if ( this . context == null ) {
this . context = createWebApplicationContext( servletContext );
}
通过 java 反射机制生成 ConfigurableWebApplicationContext 类对象,它就是 spring bean 生成运行环境( BeanFactory 容器
 
2. 刷新初始化 webApplication
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this . context ;
configureAndRefreshWebApplicationContext( cwac , servletContext );
 
 
 
 
生成好 spring 上下文对象后,进入 configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac , ServletContext sc ) 方法 , 主要执行代码如下:
 
wac .setServletContext( sc );
String configLocationParam = sc .getInitParameter( CONFIG_LOCATION_PARAM );
if ( configLocationParam != null ) {
wac .setConfigLocation( configLocationParam );// spring 上下文设置 spring bean 配置文件路径
}
 
ConfigurableEnvironment env = wac .getEnvironment();
if ( env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env ).initPropertySources( sc , null );
}
customizeContext( sc , wac );
wac .refresh();
 
 
这里有一个注意的地方, ConfigurableEnvironment 生成的地方,
wac .setConfigLocation( configLocationParam ); 时根据 configLocationParam 设置配置参数路径时就会初始化 StandardServletEnvironment ConfigurableEnvironment 的子类 )
 
 
进入 AbstractApplicationContext. refresh() 方法
 
public void refresh() throws BeansException, IllegalStateException {
synchronized ( this . startupShutdownMonitor ) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory( beanFactory );
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory( beanFactory );
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors( beanFactory );
// Register bean processors that intercept bean creation.
registerBeanPostProcessors( beanFactory );
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization( beanFactory );
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException 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();
}
}
}
 
 
 
(1)prepareRefresh()
// Initialize any placeholder property sources in the context environment
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
 
(2) obtainFreshBeanFactory(); 此方法负责创建 beanFactory 并根据 spring xml 配置文件初始化得到 BeanDefintions
 
 
 
 
这里主要看 XmlWebApplicationContext 类的 loadBeanDefinitions 方法
从上图看一看出,是如何一步一步的解析 xml 文件的
主要代码在 NamespaceHandler ,根据命名空间得到相应的处理器来解析相关配置文件中的标签 tag
实例 : namespaceUri= http://www.springframework.org/schema/context
得到的 handler 为: org.springframework.context.config.ContextNamespaceHandler
其实就是从 spring jar 包配置文件中获取的
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
 
@Override
public void init() {
registerBeanDefinitionParser( "property-placeholder" , new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser( "property-override" , new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser( "annotation-config" , new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser( "component-scan" , new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser( "load-time-weaver" , new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser( "spring-configured" , new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser( "mbean-export" , new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser( "mbean-server" , new MBeanServerBeanDefinitionParser());
}
 
}
 
应该可以明显看出, xml 中的标签就是通过这些 handler 来处理的
通过 handler 中注册的 parser 来解析 tag
 
下面再来看下是如何解析的
通过 tag 名称 ,得到相应的解析器并做相应的处理
 
 
 
上面实例就是解析 property-placeholder 标签的 parser
 
 
 
 
回到 refresh() 方法,可以看到 obtainFreshBeanFactory(); 其实已经干了大部分工作了,创建 spring bean 上下文 beanFactory 并解析 xml 得到 beanDefintion 初始化上下文。
 
下一步执行 prepareBeanFactory( beanFactory ); 方法
 
这个方法主要是对 beanFactory 做一些初始化工作
1 )设置 classLoader
2 )设置 SpEL 表达式解析
3 )设置 ResourceEditorRegistrar (在获取 bean 中设置 value
4 )设置前置处理器
beanFactory .addBeanPostProcessor( new ApplicationContextAwareProcessor( this ));
5 )设置一些不需要 autowired bean
beanFactory .ignoreDependencyInterface(ApplicationContextAware. class );
ApplicationContextAware 子类不通过 autowired 方式注入
6 )设置和环境相关的 beans
 
 
prepareBeanFactory 之后执行 postProcessBeanFactory
主要设置了一些前置处理器。
 
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors( beanFactory );
 
后续就是注册资源文件,监听器,实例化一些非延迟加载的 bean ,最后广播相关事件 over
 
 
 
总结 :
spring 家族中所有的部件都是基于 spring bean 容器的,了解 beanFactory 上下文的结构和原理对理解 spring 十分重要,本文主要从整体上描述了 spring 如何在 web 环境中初始化启动,从源码上给出了一个简要的流程描述,希望读者仔细阅读后能对 spring bean 容器有一个系统的了解。
 
 
 
 
参考:
1. http://www.ibm.com/developerworks/cn/java/j-lo-servlet/
2.http://www.cnblogs.com/RunForLove/p/5688731.html

你可能感兴趣的:(spring,servlet,tomcat,web,java)