您的位置:首页 > 编程语言 > Java开发

springMVC源码分析--容器初始化(一)ContextLoaderListener

2017-02-24 15:47 706 查看
此文为转载,地址:http://blog.csdn.net/qq924862077/

spring Web中,需要初始化IOC容器,用于存放我们注入的各种对象。当tomcat启动时首先会初始化一个web对应的IOC容器,用于初始化和注入各种我们在web运行过程中需要的对象。当tomcat启动的时候是如何初始化IOC容器的,我们先看一下在web.xml中经常看到的配置:

[html] view
plain copy

print?





<context-param>

<param-name>contextCon</param-name>

<param-value>

classpath:applicationContext.xml

</param-value>

</context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

ContextLoaderListener是一个监听器,其实现了ServletContextListener接口,其用来监听Servlet,当tomcat启动时会初始化一个Servlet容器,这样ContextLoaderListener会监听到Servlet的初始化,这样在Servlet初始化之后我们就可以在ContextLoaderListener中也进行一些初始化操作。看下面的ServletContextListener的源码也是比较简单的,ServletContextListener实现了ServletContextListener接口,所以会有两个方法contextInitialized和contextDestroyed。web容器初始化时会调用方法contextInitialized,web容器销毁时会调用方法contextDestroyed。

[java] view
plain copy

print?





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) {

//在父类ContextLoader中实现

initWebApplicationContext(event.getServletContext());

}

/**

* Close the root web application context.

*/

@Override

public void contextDestroyed(ServletContextEvent event) {

closeWebApplicationContext(event.getServletContext());

ContextCleanupListener.cleanupAttributes(event.getServletContext());

}

}

ContextLoaderListener的方法contextInitialized()的默认实现是在他的父类ContextLoader的initWebApplicationContext方法中实现的,意思就是初始化web应用上下文。他的主要流程就是创建一个IOC容器,并将创建的IOC容器存到servletContext中,ContextLoader的核心实现如下:

initWebApplicationContext函数:

[java] view
plain copy

print?





//初始化WebApplicationContext,IOC容器

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

//判断web容器中是否已经有WebApplicationContext

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!");

}

Log logger = LogFactory.getLog(ContextLoader.class);

servletContext.log("Initializing Spring root WebApplicationContext");

if (logger.isInfoEnabled()) {

logger.info("Root WebApplicationContext: initialization started");

}

long startTime = System.currentTimeMillis();

try {

// Store context in local instance variable, to guarantee that

// it is available on ServletContext shutdown.

//创建WebApplicationContext

if (this.context == null) {

//最终得到的是XmlWebApplicationContext

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

//判断是否已经有父容器

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);

}

//设置并刷新WebApplicationContext容器

configureAndRefreshWebApplicationContext(cwac, servletContext);

}

}

//将初始化的WebApplicationContext设置到servletContext中

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();

if (ccl == ContextLoader.class.getClassLoader()) {

currentContext = this.context;

}

else if (ccl != null) {

currentContextPerThread.put(ccl, this.context);

}

if (logger.isDebugEnabled()) {

logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");

}

if (logger.isInfoEnabled()) {

long elapsedTime = System.currentTimeMillis() - startTime;

logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");

}

//初始化 WebApplicationContext完成并返回

return this.context;

}

catch (RuntimeException ex) {

logger.error("Context initialization failed", ex);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);

throw ex;

}

catch (Error err) {

logger.error("Context initialization failed", err);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);

throw err;

}

}

springMVC父容器初始化流程图(盗图)如下:



完整的ContextLoader源码如下:

[java] view
plain copy

print?





/**

* 用于初始化ApplicationContext

*

*/

public class ContextLoader {

//WebApplicationContext id

public static final String CONTEXT_ID_PARAM = "contextId";

//配置文件

public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";

public static final String CONTEXT_CLASS_PARAM = "contextClass";

public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";

public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";

public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";

public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";

private static final String INIT_PARAM_DELIMITERS = ",; \t\n";

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

private static final Properties defaultStrategies;

static {

// Load default strategy implementations from properties file.

// This is currently strictly internal and not meant to be customized

// by application developers.

try {

ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);

defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);

}

catch (IOException ex) {

throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());

}

}

private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread =

new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);

private static volatile WebApplicationContext currentContext;

private WebApplicationContext context;

private BeanFactoryReference parentContextRef;

private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =

new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();

public ContextLoader() {

}

public ContextLoader(WebApplicationContext context) {

this.context = context;

}

@SuppressWarnings("unchecked")

public void setContextInitializers(ApplicationContextInitializer<?>... initializers) {

if (initializers != null) {

for (ApplicationContextInitializer<?> initializer : initializers) {

this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);

}

}

}

//初始化WebApplicationContext,IOC容器

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

//判断web容器中是否已经有WebApplicationContext

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!");

}

Log logger = LogFactory.getLog(ContextLoader.class);

servletContext.log("Initializing Spring root WebApplicationContext");

if (logger.isInfoEnabled()) {

logger.info("Root WebApplicationContext: initialization started");

}

long startTime = System.currentTimeMillis();

try {

// Store context in local instance variable, to guarantee that

// it is available on ServletContext shutdown.

//创建WebApplicationContext

if (this.context == null) {

//最终得到的是XmlWebApplicationContext

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

//判断是否已经有父容器

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);

}

//设置并刷新WebApplicationContext容器

configureAndRefreshWebApplicationContext(cwac, servletContext);

}

}

//将初始化的WebApplicationContext设置到servletContext中

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();

if (ccl == ContextLoader.class.getClassLoader()) {

currentContext = this.context;

}

else if (ccl != null) {

currentContextPerThread.put(ccl, this.context);

}

if (logger.isDebugEnabled()) {

logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");

}

if (logger.isInfoEnabled()) {

long elapsedTime = System.currentTimeMillis() - startTime;

logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");

}

//初始化 WebApplicationContext完成并返回

return this.context;

}

catch (RuntimeException ex) {

logger.error("Context initialization failed", ex);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);

throw ex;

}

catch (Error err) {

logger.error("Context initialization failed", err);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);

throw err;

}

}

//在web容器加载的时候初始化根WebApplicationContext

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {

//通过反射获得上下文类

Class<?> contextClass = determineContextClass(sc);

if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {

throw new ApplicationContextException("Custom context class [" + contextClass.getName() +

"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");

}

//初始化WebApplicationContext,是其实现类XmlWebApplicationContext

return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

}

protected Class<?> determineContextClass(ServletContext servletContext) {

//查看servlet是否已经存在应用上下文类名

String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

if (contextClassName != null) {

try {

//类名不为空则反射获得类

return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());

}

catch (ClassNotFoundException ex) {

throw new ApplicationContextException(

"Failed to load custom context class [" + contextClassName + "]", ex);

}

}

else {

//如果应用上下文类名为空,则获得默认的context上下文名

contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

try {

return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());

}

catch (ClassNotFoundException ex) {

throw new ApplicationContextException(

"Failed to load default context class [" + contextClassName + "]", ex);

}

}

}

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

// The application context id is still set to its original default value

// -> assign a more useful id based on available information

String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);

//设置应用上下文id,在web.xml中可以进行配置

if (idParam != null) {

wac.setId(idParam);

}

else {

// Generate default id...

//如果没有配置则设置默认名称

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

ObjectUtils.getDisplayString(sc.getContextPath()));

}

}

//设置ServletContext到容器中

wac.setServletContext(sc);

//获得web.xml中配置的contextConfigLocation的值,一般是classpath:applicationContext.xml

String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);

if (configLocationParam != null) {

//设置加载文件地址

wac.setConfigLocation(configLocationParam);

}

// The wac environment's #initPropertySources will be called in any case when the context

// is refreshed; do it eagerly here to ensure servlet property sources are in place for

// use in any post-processing or initialization that occurs below prior to #refresh

ConfigurableEnvironment env = wac.getEnvironment();

if (env instanceof ConfigurableWebEnvironment) {

((ConfigurableWebEnvironment) env).initPropertySources(sc, null);

}

customizeContext(sc, wac);

//这一步是特别关键的,这样application.xml中的所有配置都会被初始化到XMLWebApplication中

wac.refresh();

}

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {

List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =

determineContextInitializerClasses(sc);

for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {

Class<?> initializerContextClass =

GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);

if (initializerContextClass != null) {

Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(

"Could not add context initializer [%s] since its generic parameter [%s] " +

"is not assignable from the type of application context used by this " +

"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),

wac.getClass().getName()));

}

this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));

}

AnnotationAwareOrderComparator.sort(this.contextInitializers);

for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {

initializer.initialize(wac);

}

}

protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>

determineContextInitializerClasses(ServletContext servletContext) {

List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =

new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();

String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);

if (globalClassNames != null) {

for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {

classes.add(loadInitializerClass(className));

}

}

String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);

if (localClassNames != null) {

for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {

classes.add(loadInitializerClass(className));

}

}

return classes;

}

@SuppressWarnings("unchecked")

private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {

try {

Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());

Assert.isAssignable(ApplicationContextInitializer.class, clazz);

return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;

}

catch (ClassNotFoundException ex) {

throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);

}

}

//从ServletContext中获取父容器

protected ApplicationContext loadParentContext(ServletContext servletContext) {

ApplicationContext parentContext = null;

String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);

String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

if (parentContextKey != null) {

// locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"

BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

Log logger = LogFactory.getLog(ContextLoader.class);

if (logger.isDebugEnabled()) {

logger.debug("Getting parent context definition: using parent context key of '" +

parentContextKey + "' with BeanFactoryLocator");

}

this.parentContextRef = locator.useBeanFactory(parentContextKey);

parentContext = (ApplicationContext) this.parentContextRef.getFactory();

}

return parentContext;

}

//通过给定的ServletContext来关闭IOC容器

public void closeWebApplicationContext(ServletContext servletContext) {

servletContext.log("Closing Spring root WebApplicationContext");

try {

if (this.context instanceof ConfigurableWebApplicationContext) {

((ConfigurableWebApplicationContext) this.context).close();

}

}

finally {

ClassLoader ccl = Thread.currentThread().getContextClassLoader();

if (ccl == ContextLoader.class.getClassLoader()) {

currentContext = null;

}

else if (ccl != null) {

currentContextPerThread.remove(ccl);

}

servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

if (this.parentContextRef != null) {

this.parentContextRef.release();

}

}

}

//在当前线程中获取WebApplicationContext

public static WebApplicationContext getCurrentWebApplicationContext() {

ClassLoader ccl = Thread.currentThread().getContextClassLoader();

if (ccl != null) {

WebApplicationContext ccpt = currentContextPerThread.get(ccl);

if (ccpt != null) {

return ccpt;

}

}

return currentContext;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息