初探spring applicationContext在web容器中加载过程

初探spring applicationContext在web容器中加载过程

转载自 www.javaworld.com.tw 袁杰 原文

首先从WEB.XML入手

==>web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<context-param>
            <param-name>webAppRootKey</param-name>
            <param-value>task.root</param-value>
            </context-param>
            <!-- 定义SPRING配置文件 -->
            
            <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/taskContext*.xml</param-value>
            </context-param>
            
            <context-param>
            <param-name>log4jConfigLocation</param-name>
            <param-value>/WEB-INF/log4j.properties</param-value>
            </context-param>
            <!-- 定义LOG4J监听器 -->
            <listener>
            <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
            </listener>
             
            <!-- 定义SPRING监听器 -->
            
            <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
            
            

进入contextLoaderListener看看到底加载时做了甚么
==>org.springframework.web.context.ContextLoaderListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ContextLoaderListener implements ServletContextListener {
             
            private ContextLoader contextLoader;
             
            /**
            * Initialize the root web application context.
            */
            //当WEB上下文初始化时,系统会调用此方法
            public void contextInitialized(ServletContextEvent event) {
            this.contextLoader = createContextLoader();
             
            
            //监听到WEB上下文初始化的时候执行SPRING上下文contextLoader的初始化工作
            this.contextLoader.initWebApplicationContext(event.getServletContext());
            
            }
             
            /**
            * Create the ContextLoader to use. Can be overridden in subclasses.
            * @return the new ContextLoader
            */
            protected ContextLoader createContextLoader() {
            return new ContextLoader();
            }
             
            /**
            * Return the ContextLoader used by this listener.
            */
            public ContextLoader getContextLoader() {
            return contextLoader;
            }
             
            /**
            * Close the root web application context.
            */
            public void contextDestroyed(ServletContextEvent event) {
            if (this.contextLoader != null) {
            this.contextLoader.closeWebApplicationContext(event.getServletContext());
            }
            }
             
            }
            

看一下是怎么来初始化webapplicationContext的
==>contextLoader.initWebApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
            throws IllegalStateException, BeansException {
             
            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!");
            }
             
            long startTime = System.currentTimeMillis();
            if (logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
            }
            servletContext.log("Loading Spring root WebApplicationContext");
             
            try {
            // Determine parent for root web application context, if any.
            ApplicationContext parent = loadParentContext(servletContext);
             
            // Store context in local instance variable, to guarantee that
            // it is available on ServletContext shutdown.
             
            
            //创建web上下文
            this.context = createWebApplicationContext(servletContext, parent);
            servletContext.setAttribute(
            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
            
             
            if (logger.isInfoEnabled()) {
            logger.info("Using context class [" + this.context.getClass().getName() +
            "] for root WebApplicationContext");
            }
            if (logger.isDebugEnabled()) {
            logger.debug("Published root WebApplicationContext [" + this.context +
            "] 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");
            }
             
            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;
            }
            }
            

==>contextLoader.createWebApplicationContext(servletContext, parent);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 protected WebApplicationContext createWebApplicationContext(
            ServletContext servletContext, ApplicationContext parent) throws BeansException {
            
            //根据servletContext来决定要实例化的WebApplicationContext
            Class contextClass = determineContextClass(servletContext);
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
            "] is not of type ConfigurableWebApplicationContext");
            }
            ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
            wac.setParent(parent);
            wac.setServletContext(servletContext);
             
            //得到WEB.XML中设置的SPRING配置文件位置
            String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
            if (configLocation != null) {
            //把配置文件分段后设置到WebApplicationContext的ConfigLocations中
            wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
            ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
            }
            //刷新WebApplicationContext
            wac.refresh();
            
            return wac;
            }
            

==>contextLoader.determineContextClass(servletContext);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
            
            //获得需要实例化的CONTEXT类名,在web.xml中有设置,如果没有设置,那么为空
            String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
            if (contextClassName != null) {
            try {
            return ClassUtils.forName(contextClassName);
            }
            catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
            "Failed to load custom context class [" + contextClassName + "]", ex);
            }
            }
            //如果在spring web.xml中没有设置context类位置,那么取得默认context
            else {
            //取得defaultStrategies配置文件中的WebApplicationContext属性
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
            return ClassUtils.forName(contextClassName);
            }
            catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
            "Failed to load default context class [" + contextClassName + "]", ex);
            }
            }
            
            }
            

SPRING上下文默认的策略是甚么呢?
==>contextLoader.defaultStrategies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  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 {
            
            //设置classpath为contextLoader同级目录
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
            //加载该目录下的所有properties文件
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
            
            }
            catch (IOException ex) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
            }
            }
            

找到同级目录下的配置文件
==>ContextLoader.properties

1
2
3
4
5
6
7
8
# Default WebApplicationContext implementation class for ContextLoader.
            # Used as fallback when no explicit context implementation has been specified as context-param.
            # Not meant to be customized by application developers.
             
            
            #默认的WebApplicationContext为org.springframework.web.context.support.XmlWebApplicationContext
            org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
            
            

==>org.springframework.web.context.support.XmlWebApplicationContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
             
            /** Default config location for the root context */
             
            
            //配置了默认的spring配置文件
            public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
            
             
            //配置文件默认BUILD路径
            public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
             
            //配置文件默认后缀名
            public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
             
            /**
            * Loads the bean definitions via an XmlBeanDefinitionReader.
            * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
            * @see #initBeanDefinitionReader
            * @see #loadBeanDefinitions
            */
            //获得bean配置
            protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
            //从BEAN工厂获得一个XmlBeanDefinitionReader 来读取SPRING配置文件
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
             
            //设置beanDefinitionReader服务于当前CONTEXT
            // resource loading environment.
            beanDefinitionReader.setResourceLoader(this);
            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
             
            // Allow a subclass to provide custom initialization of the reader,
            // then proceed with actually loading the bean definitions.
            initBeanDefinitionReader(beanDefinitionReader);
            
            //读取配置文件
            loadBeanDefinitions(beanDefinitionReader);
            
            }
             
            /**
            * Initialize the bean definition reader used for loading the bean
            * definitions of this context. Default implementation is empty.
            * <p>Can be overridden in subclasses, e.g. for turning off XML validation
            * or using a different XmlBeanDefinitionParser implementation.
            * @param beanDefinitionReader the bean definition reader used by this context
            * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
            * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
            */
            protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
            }
             
            /**
            * Load the bean definitions with the given XmlBeanDefinitionReader.
            * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
            * therefore this method is just supposed to load and/or register bean definitions.
            * <p>Delegates to a ResourcePatternResolver for resolving location patterns
            * into Resource instances.
            * @throws org.springframework.beans.BeansException in case of bean registration errors
            * @throws java.io.IOException if the required XML document isn't found
            * @see #refreshBeanFactory
            * @see #getConfigLocations
            * @see #getResources
            * @see #getResourcePatternResolver
            */
            
            //读取配置文件
            protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
            String[] configLocations = getConfigLocations();
            if (configLocations != null) {
            for (int i = 0; i < configLocations.length; i++) {
            reader.loadBeanDefinitions(configLocations[i]);
            }
            }
            }
            
             
            /**
            * The default location for the root context is "/WEB-INF/applicationContext.xml",
            * and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
            * (like for a DispatcherServlet instance with the servlet-name "test").
            */
            //获得默认的ConfigLocations
            protected String[] getDefaultConfigLocations() {
            if (getNamespace() != null) {
            return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() +
             
            DEFAULT_CONFIG_LOCATION_SUFFIX};
            }
            else {
            return new String[] {DEFAULT_CONFIG_LOCATION};
            }
            }
            

==>AbstractBeanDefinitionReader.loadBeanDefinitions()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
            }
             
            if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
            
            //根据配置文件读取相应配置
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            
            int loadCount = loadBeanDefinitions(resources);
            if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
            }
            return loadCount;
            }
            catch (IOException ex) {
            throw new BeanDefinitionStoreException(
            "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
            }
            else {
            // Can only load single resources by absolute URL.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
            }
            }
            

这个是其中一个ResourceLoader的实现
==>PathMatchingResourcePatternResolver.getResources(String locationPattern);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 public Resource[] getResources(String locationPattern) throws IOException {
            Assert.notNull(locationPattern, "Location pattern must not be null");
            if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            // a class path resource (multiple resources for same name possible)
            
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // a class path resource pattern
            return findPathMatchingResources(locationPattern);
            }
            else {
            // all class path resources with the given name
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
            
            }
            else {
            // Only look for a pattern after a prefix here
            // (to not get fooled by a pattern symbol in a strange prefix).
            int prefixEnd = locationPattern.indexOf(":") + 1;
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            // a file pattern
            return findPathMatchingResources(locationPattern);
            }
            else {
            // a single resource with the given name
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
            }
            }
            


==>PathMatchingResourcePatternResolver.findPathMatchingResources(String locationPattern);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
            String rootDirPath = determineRootDir(locationPattern);
            String subPattern = locationPattern.substring(rootDirPath.length());
            Resource[] rootDirResources = getResources(rootDirPath);
            
            //collectionFactory初始化一个set容量为16
            Set result = CollectionFactory.createLinkedSetIfPossible(16);
            for (int i = 0; i < rootDirResources.length; i++) {
            Resource rootDirResource = rootDirResources[i];
            if (isJarResource(rootDirResource)) {
            result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
            }
            else {
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
            }
            }
            
            if (logger.isDebugEnabled()) {
            logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
            }
            return (Resource[]) result.toArray(new Resource[result.size()]);
            }
            


前面说到有一个刷新WebApplicationContext的操作,但是XmlWebApplicationContext 并没有实现refresh方法,而方法的实现写在

AbstractRefreshableWebApplicationContext 中

==>AbstractRefreshableWebApplicationContext.refresh();
1
2
3
4
5
6
7
public void refresh() throws BeansException {
            if (ObjectUtils.isEmpty(getConfigLocations())) {
            //设置configLocations为默认的getDefaultConfigLocations()
            setConfigLocations(getDefaultConfigLocations());
            }
            super.refresh();
            }
            


==>AbstractApplicationContext.refresh();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
            this.startupTime = System.currentTimeMillis();
             
            synchronized (this.activeMonitor) {
            this.active = true;
            }
             
            // Tell subclass to refresh the internal bean factory.
            refreshBeanFactory();
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
             
            // Tell the internal bean factory to use the context's class loader.
            beanFactory.setBeanClassLoader(getClassLoader());
             
            // Populate the bean factory with context-specific resource editors.
            beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));
             
            // Configure the bean factory with context semantics.
            beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
            beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
            beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
             
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
             
            // Invoke factory processors registered with the context instance.
            for (Iterator it = getBeanFactoryPostProcessors().iterator(); it.hasNext();) {
            BeanFactoryPostProcessor factoryProcessor = (BeanFactoryPostProcessor) it.next();
            factoryProcessor.postProcessBeanFactory(beanFactory);
            }
             
            if (logger.isInfoEnabled()) {
            if (getBeanDefinitionCount() == 0) {
            logger.info("No beans defined in application context [" + getDisplayName() + "]");
            }
            else {
            logger.info(getBeanDefinitionCount() + " beans defined in application context [" +
             
            getDisplayName() + "]");
            }
            }
             
            try {
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors();
             
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors();
             
            // 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 singletons this late to allow them to access the message source.
            beanFactory.preInstantiateSingletons();
             
            // Last step: publish corresponding event.
            publishEvent(new ContextRefreshedEvent(this));
            }
             
            catch (BeansException ex) {
            // Destroy already created singletons to avoid dangling resources.
            beanFactory.destroySingletons();
            throw ex;
            }
            }
            }
            


你可能感兴趣的:(初探spring applicationContext在web容器中加载过程)