spring源码系列-1

来源:互联网 发布:软件的摊销年限 编辑:程序博客网 时间:2024/06/12 09:23

1. 入口web.xml

配置spring监听器

<listener>  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {   public ContextLoaderListener() {}   //带参构造   public ContextLoaderListener(WebApplicationContext context) {      super(context);   }  //会先加载和实例化父类,所以父类中的静态代码块先执行,然后再执行init,其实还是调用父类的initWebApplicationContext方法。  @Override  public void contextInitialized(ServletContextEvent event) {      initWebApplicationContext(event.getServletContext());   }  //desdroy  @Override  public void contextDestroyed(ServletContextEvent event) {      closeWebApplicationContext(event.getServletContext());      ContextCleanupListener.cleanupAttributes(event.getServletContext());   }}

该监听器继承了ContextLoader类如下:

public class ContextLoader {    public static final String CONTEXT_ID_PARAM = "contextId";    public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";    //用于指定webApplicationContext的默认类    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";    //静态代码块将spring-web jar包中的ContextLoader.properties配置文件中的org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext加载到Properties作为默认策略,赋给变量defaultStrategies    private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";    private static final Properties defaultStrategies;    static {        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());        }    }    //Map from (thread context) ClassLoader to corresponding 'current' WebApplicationContext.    private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread =            new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);    // The 'current' WebApplicationContext, if the ContextLoader class is deployed in the web app ClassLoader itself.    private static volatile WebApplicationContext currentContext;     // The root WebApplicationContext instance that this loader manages.    private WebApplicationContext context;    //Holds BeanFactoryReference when loading parent factory via ContextSingletonBeanFactoryLocator.    private BeanFactoryReference parentContextRef;    public ContextLoader() {    }    public ContextLoader(WebApplicationContext context) {        this.context = context;    }    //执行完静态代码块就执行这个方法,初始化web项目的上下文对象    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {    //如果从ServletContext里获取到org.springframework.web.context.WebApplicationContext.ROOT的话抛出异常(无法初始化上下文对象,请检查web.xml里是否配置了多余的ContextLoader*)        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 {            //在本地变量(context)保存上下文,确保上下文在ServletContext关闭情况下可用            //原文: Store context in local instance variable, to guarantee that it is available on ServletContext shutdown.            if (this.context == null) {                //创建web项目上下文对象                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);                    }                    //初始化spring上下文信息(重要)                    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);            }            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");            }            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;        }    }    //要实例化的上下文对象类必须是ConfigurableWebApplicationContext接口的实现类,按照默认构造实例化该对象,默认类XmlWebApplicationContext也是该接口的实现类,可自行查看该接口的其它实现类。如果自定义的类不是该接口的实现类则会抛出异常:Custom context class [自定义class] is not of type [org.springframework.web.context.ConfigurableWebApplicationContext]    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() + "]");        }        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);    }    //返回WebApplicationContext的实现类Class对象, 默认XmlWebApplicationContext或者用户自定义的类。web.xml中配置contextClass用于指定该类,如果没有配置则使用默认的类    protected Class<?> determineContextClass(ServletContext servletContext) {        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 {            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);            }        }    }    @Deprecated    protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {        return createWebApplicationContext(sc);    }    //初始化spring上下文信息    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {            // 设置spring上下文对象的id,web.xml配置了则使用配置的id名,没有配置则使用org.springframework.web.context.WebApplicationContext:            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);            if (idParam != null) {                wac.setId(idParam);            }            else {                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +                        ObjectUtils.getDisplayString(sc.getContextPath()));            }        }        //将ServletContext和contextConfigLocation初始化到spring上下文对象        wac.setServletContext(sc);        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);        wac.refresh();    }    protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {        List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =                determineContextInitializerClasses(sc);        if (initializerClasses.isEmpty()) {            // no ApplicationContextInitializers have been declared -> nothing to do            return;        }        ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =                new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();        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()));            }            initializerInstances.add(BeanUtils.instantiateClass(initializerClass));        }        AnnotationAwareOrderComparator.sort(initializerInstances);        for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {            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);        }    }    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;    }    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();            }        }    }    public static WebApplicationContext getCurrentWebApplicationContext() {        ClassLoader ccl = Thread.currentThread().getContextClassLoader();        if (ccl != null) {            WebApplicationContext ccpt = currentContextPerThread.get(ccl);            if (ccpt != null) {                return ccpt;            }        }        return currentContext;    }}
1 0