spring源码(一)--ContextLoaderListener

在spring框架存在的web项目中,需要在web.xml中配置

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

这个监听器,作用是初始化spring容器。

public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }

调用了ContextLoader的initWebApplicationContext()方法,这个方法关键有2步;第一步createWebApplicationContext,
第二步,this.configureAndRefreshWebApplicationContext(cwac, servletContext);

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        
                this.context = this.createWebApplicationContext(servletContext);
                //xmlWebAplicationContext

                if(this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    if(!cwac.isActive()) {
                        if(cwac.getParent() == null) {//第一次启动parent肯定是null的
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);//如果web.xml中没有配置locatorFactorySelector、parentContextKey,则parent还是null
                        }
//修改cwac的id org.springframework.web.context.support.XmlWebApplicationContext@5f98e77d为org.springframework.web.context.WebApplicationContext:
//设置ConfigLocation参数,在web.xml中有配置
//把servletcontext和ServletConfig放到environment中
//定制cwac
                        this.configureAndRefreshWebApplicationContext(cwac, 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);
                }         
                return this.context;
            } catch (RuntimeException var8) {
       
            }
        }
    }

创建webContext,这一块比较简单,使用tomcat 的类加载器加载context的class,然后通过反射创建对象实例:

contextClass会使用默认的org.springframework.web.context.support.XmlWebApplicationContext,
也可以在web.xml中配置初始化参数contextClass来自定义。

 protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        Class<?> contextClass = this.determineContextClass(sc);
        if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {//判断contextClass是不是ConfigurableWebApplicationContext.class的子类,必须是,不然抛异常
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);//实例化类,调用无参构造函数。
        }
    }
/**
*获取实际实例化的class 
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter("contextClass");
        if(contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException var4) {
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
            }
        } else {//没有配置contextClass参数的话,取默认的值,在ContextLoader.properties中取,默认值是org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException var5) {
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
            }
        }
    }

第二步,配置并刷新web容器

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        String configLocationParam;
        if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
        //wac的id在初始化的时候被设置了,也是使用的objectUtils的算法,所以这里是相等的
        //走这一步是替换初识的id
            configLocationParam = sc.getInitParameter("contextId");
            if(configLocationParam != null) {
                wac.setId(configLocationParam);//有自定义,则替换成自定义的
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);//持有servletContext
        configLocationParam = sc.getInitParameter("contextConfigLocation");
        if(configLocationParam != null) {
        //这里获取的是spring的配置文件地址
            wac.setConfigLocation(configLocationParam);
        }

        ConfigurableEnvironment env = wac.getEnvironment();
        //env是StandardServletEnvironment的实例
        if(env instanceof ConfigurableWebEnvironment) {
        //StandardServletEnvironment是ConfigurableWebEnvironment的子类
            ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);//这一步没做什么很重要的事情
        }

        this.customizeContext(sc, wac);//这一步也没什么
        wac.refresh();//重点
    }

关于定制cwac:
没有特殊配置的话,这个方法什么事情都不会干

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
        List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = this.determineContextInitializerClasses(sc);//没有特殊配置,一般是空的
        Iterator var4 = initializerClasses.iterator();

        while(var4.hasNext()) {
            Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass = (Class)var4.next();
            Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
            if(initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
                throw new ApplicationContextException(String.format("Could not apply context initializer [%s] since its generic parameter [%s] is not assignable from the type of application context used by this context loader: [%s]", new Object[]{initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName()}));
            }

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

        AnnotationAwareOrderComparator.sort(this.contextInitializers);
        var4 = this.contextInitializers.iterator();

        while(var4.hasNext()) {
            ApplicationContextInitializer<ConfigurableApplicationContext> initializer = (ApplicationContextInitializer)var4.next();
            initializer.initialize(wac);
        }

    }

所以在web应用中,spring项目的启动核心就是refresh方法:

public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();//基本设置,可以忽略
            
            //创建DefaultListableBeanFactory,设置ID为applicationContext的id
            //loadBeanDefinitions,加载bean的定义,
            //把所有的bean定义加载到Map<String, BeanDefinition> beanDefinitionMap
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            
            //注册一些依赖的bean
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if(this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

其中obtainFreshBeanFactory();调用XmlWebApplicationContext的loadBeanDefinitions,一路下去,直到XmlBeanDefinitionReader的loadBeanDefinitions,也就是第三篇的核心内容。这条线,作用就是解析配置文件,把符合条件的beanDefinition注册到factory中。

 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        this.refreshBeanFactory();
        return this.getBeanFactory();
    }

在refreshBeanFactory();中,先判断有没有beanfactory,有的话,先去除引用。

if(this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

创建一个DefaultListableBeanFactory实例,如果有parent,则把parent的bf也传递过来。

  DefaultListableBeanFactory beanFactory = this.createBeanFactory();

给bf一个id,采用的系统默认hashcode实现,所以对于这个对象基本上不会重复。然后会把bf放到一个static的map中,使用弱引用包裹。

beanFactory.setSerializationId(this.getId());
loadBeanDefinitions,这是关键方法,在其他篇中详解

至此,refreshBeanFactory解析完毕,getBefactory获得的也是这个factory。


猜你喜欢

转载自blog.csdn.net/ljz2016/article/details/82587254