1. 程式人生 > >Spring原始碼解析筆記8——容器的功能擴充套件ApplicationContext

Spring原始碼解析筆記8——容器的功能擴充套件ApplicationContext

ApplicationContext包含BeanFactory的所有功能,並且可以對BeanFactory現有的功能進行擴充套件。絕大多數典型的企業應用和系統,使用的都是ApplicationContext而不是直接使用BeanFactory。下面看一下ApplicationContext相對於BeanFactory擴充套件了哪些功能。

//BeanFactory的載入配置檔案的方式:
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

//ApplicationContext載入配置檔案的方式:
ApplicationContext bf = new ClassPathXmlApplicationContext("");

繼續根據ClassPathXmlApplicationContext進行跟蹤:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
}

//根據this繼續走:
public ClassPathXmlApplicationContext
(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); //將配置檔案的路徑以陣列的方式傳入。 this.setConfigLocations(configLocations); if(refresh) { //解析和功能實現。 this.refresh(); } }

繼續跟蹤setConfigLocations:

public void setConfigLocations(String... locations) {
        if(locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];

            for(int i = 0; i < locations.length; ++i) {

                //解析給定路徑,如果陣列總包含特殊符號,如${var},那麼resolvePath中會搜尋系統的變數被替換。
                this.configLocations[i] = this.resolvePath(locations[i]).trim();
            }
        } else {
            this.configLocations = null;
        }

    }

繼續跟蹤refresh函式:

 public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {

            //準備重新整理上下文環境,例如對系統屬性或者環境變數進行準備及驗證。
            this.prepareRefresh();

            //初始化BeanFactory,並進行XML檔案的讀取,這一步驟要服用BeanFactory的配置檔案讀取的以及其他功能。
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

            //對BeanFactory進行各種功能填充。@Qualifier和@Autowired是在此步驟增加的支援。
            this.prepareBeanFactory(beanFactory);

            try {

                //子類覆蓋方法做額外的處理。
                this.postProcessBeanFactory(beanFactory);

                //啟用各種BeanFactory處理器
                this.invokeBeanFactoryPostProcessors(beanFactory);

                //註冊攔截Bean建立的Bean處理器,這裡只是註冊,真正的呼叫是在getBean的時候
                this.registerBeanPostProcessors(beanFactory);

                //為上下文初始化Mesagge源,即不同語言的訊息體,國際化處理。
                this.initMessageSource();

                //初始化應用訊息廣播器,並放入applicationEventMulticaster中
                this.initApplicationEventMulticaster();

                //留給子類來初始化其他的bean
                this.onRefresh();

                //在所有註冊的bean中查詢Listener bean,註冊到訊息廣播器中。
                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();
            }

        }
    }

繼續跟蹤refresh方法中的this.prepareRefresh()方法:

protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);
        if(this.logger.isInfoEnabled()) {
            this.logger.info("Refreshing " + this);
        }

        //留給子類覆蓋。
        this.initPropertySources();

        //驗證需要的屬性檔案是否都已經放入環境中。
        this.getEnvironment().validateRequiredProperties();
        this.earlyApplicationEvents = new LinkedHashSet();
    }

上述留給子類覆蓋的方法initPropertySources使用舉例:

class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{

        public MyClassPathXmlApplicationContext(String ... configLocations) {
            super(configLocations);
        }

        @Override
        protected void initPropertySources() {
            getEnvironment().setRequiredProperties("VAR");
        }
    }

 //讀取配置檔案時這樣呼叫:
 ApplicationContext bf2 = new MyClassPathXmlApplicationContext("");

繼續跟蹤refresh方法中的this.obtainFreshBeanFactory()方法:

//通過了這個函式以後,ApplicationContext就已經擁有了BeanFactory的全部功能。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

        //初始化BeanFactory,並進行XML檔案讀取,並將得到的BeanFactory記錄在當前實體的屬性中,
        this.refreshBeanFactory();

        //返回當前實體的beanFactory
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
        }

        return beanFactory;
    }


//上面的this.refreshBeanFactory()方法原始碼:
protected final void refreshBeanFactory() throws BeansException {
        if(this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {

            //建立DefaultListableBeanFactory
            DefaultListableBeanFactory ex = this.createBeanFactory();

            //為了反序列化指定id,如果需要,讓這個BeanFactory從id反序列化到BeanFactory物件。
            ex.setSerializationId(this.getId());

            //定製beanFactory,設定相關屬性,包括是否允許覆蓋同名稱的不同定義的物件以及迴圈依賴
            //設定@Autowired和@Qualitier註冊解析器QualifierAnnotationAutowireCandidateResolver
            this.customizeBeanFactory(ex);

            //初始化DocumentReader,並進行XML檔案讀取及解析。
            this.loadBeanDefinitions(ex);
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = ex;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }


//跟蹤上面的this.customizeBeanFactory(ex)方法:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {

        //是否允許覆蓋同名稱的不同定義的物件。
        if(this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding.booleanValue());
        }

        //是否允許bean之間存在迴圈依賴。
        if(this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences.booleanValue());
        }

    }

//關於上面方法中allowBeanDefinitionOverriding和allowCircularReferences的變數設定舉例:

class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{

        @Override
        protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
            super.setAllowBeanDefinitionOverriding(false);
            super.setAllowCircularReferences(false);
            super.customizeBeanFactory(beanFactory);
        }
    }

//繼續走this.loadBeanDefinitions(ex),此方法是用來載入BeanDefinition
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        //對上一步建立的beanDefinitionReader進行環境變數的設定。
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        //對beanDefinitionReader進行設定,可以覆蓋。
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

//繼續檢視this.loadBeanDefinitions(beanDefinitionReader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if(configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if(configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

    }

繼續跟蹤refresh方法中的this.prepareBeanFactory(beanFactory)

//原始碼如下:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

        //設定beanFactory的classLoader為當前的classLoader。
        beanFactory.setBeanClassLoader(this.getClassLoader());

        //預設可以使用EL表示式
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

        //為beanFactory增加了一個預設propertyEditor,主要是對bean的屬性等設定管理的一個工具。
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));

        //新增PostProcessor
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);

        //設定幾個自動裝配的特殊規則
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        //增加對ApectJ的支援
        if(beanFactory.containsBean("loadTimeWeaver")) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        if(!beanFactory.containsLocalBean("environment")) {
            beanFactory.registerSingleton("environment", this.getEnvironment());
        }

        if(!beanFactory.containsLocalBean("systemProperties")) {
            beanFactory.registerSingleton("systemProperties", this.getEnvironment().getSystemProperties());
        }

        //增加預設環境bean.
        if(!beanFactory.containsLocalBean("systemEnvironment")) {
            beanFactory.registerSingleton("systemEnvironment", this.getEnvironment().getSystemEnvironment());
        }

    }

/**
    setBeanExpressionResolver註冊語言解析器,就可以對SPEL進行解析了,
    真正呼叫這個解析器是在Spring對bean進行初始化時的屬性填充這步,
    在這一步中Spring會呼叫AbstractAutowireCapableBeanFactory類中的applyPropertyValues函式來完成。
**/

上述方法中對bean的屬性等設定管理說明:在SpringDI注入的時候可以把普通屬性注入進來,但是像Date這種型別就無法被識別。例如:

class UserManager{
        private Date dataValue;

        public Date getDataValue() {
            return dataValue;
        }

        public void setDataValue(Date dataValue) {
            this.dataValue = dataValue;
        }
 }

//上述程式碼中需要對日期型別的屬性進行注入:
<bean id="userManager" class com.test.UserManager>
    <property name="dataValue">
        <value>2013-03-15</value>
    </property>
</bean>

//測試
ApplicationContext bf = new ClassPathXmlApplicationContext("");
UserManager userManager = (UserManager)bf.getBean("userManager");
System.out.println(userManager);

如果直接這樣使用,程式會報錯,型別轉換不成功。String無法轉換成Date型別。spring針對這類問題提供了兩種解決辦法:
1.自定義屬性編輯器,通過繼承PropertyEditorSupport.

//編寫自定義的屬性編輯器
class DatePropertyEditor extends PropertyEditorSupport{
        private String format = "yyyy-MM-dd";

        public void setFormat(String format) {
            this.format = format;
        }

        public void setAsText(String arg0) throws IllegalArgumentException{
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            try {
                Date d = sdf.parse(arg0);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    //將自定義屬性編輯器註冊到Spring中。
    <bean class="org.Springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="java.util.Date">
                <bean class="com.test.DatePropertyEditor">
                    <property name="format" value="yyyy-MM-dd"/>
                </bean>
            </map>
        </property>
    </bean>   

2.註冊Spring自帶的屬性編輯器CustomDateEditor


//定義屬性編輯器
class DatePropertyEditorRegostrar implements PropertyEditorRegistrar{

        @Override
        public void registerCustomEditors(PropertyEditorRegistry propertyEditorRegistry) {
            propertyEditorRegistry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
        }
    }

//註冊到spring
<bean class="org.Springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <bean class="com.test.DatePropertyEditorRegistrar"></bean>
        </list>
    </property>
</bean>

ResourceEditorRegistrar中也實現了registerCustomEditors方法:

//原始碼如下:
public void registerCustomEditors(PropertyEditorRegistry registry) {
        ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
        this.doRegisterEditor(registry, Resource.class, baseEditor);
        this.doRegisterEditor(registry, ContextResource.class, baseEditor);
        this.doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
        this.doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
        this.doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
        if(pathClass != null) {
            this.doRegisterEditor(registry, pathClass, new PathEditor(baseEditor));
        }

        this.doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
        this.doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
        ClassLoader classLoader = this.resourceLoader.getClassLoader();
        this.doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
        this.doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
        this.doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
        if(this.resourceLoader instanceof ResourcePatternResolver) {
            this.doRegisterEditor(registry, Resource[].class, new ResourceArrayPropertyEditor((ResourcePatternResolver)this.resourceLoader, this.propertyResolver));
        }
}

//繼續追蹤doRegisterEditor:
 private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
        if(registry instanceof PropertyEditorRegistrySupport) {
            ((PropertyEditorRegistrySupport)registry).overrideDefaultEditor(requiredType, editor);
        } else {

            //前面提到的自定義屬性的關鍵程式碼在此被呼叫
            registry.registerCustomEditor(requiredType, editor);
        }

    }

查找了registerCustomEditors方法被呼叫的位置,發現AbstractBeanFactory類中的initBeanWrapper方法呼叫了此方法,這個方法是在bean初始化時使用的一個方法,主要作用就是將BeanDefinition轉換為BeanWrapper後用於屬性的填充。在bean初始化以後會呼叫ResourceEditorRegistrar的registerCustomEditors方法進行批量的通用屬性編輯器註冊。註冊後,在屬性填充的環節便可以直接讓Spring使用這些編輯器進行屬性的解析了。


BeanWrapper除了實現了BeanWrapperImpl還繼承了PropertyEditorRegistrySupport,在該類中有這樣一個方法:
/**
    通過這個方法可以知道在spring中定義了上面一系列常用的屬性編輯器,
    如果我們定義的bean中的某個屬性的型別不在上面的常用配置中,才需要我們進行個性化屬性編輯器的註冊。
**/
private void createDefaultEditors() {
        this.defaultEditors = new HashMap(64);
        this.defaultEditors.put(Charset.class, new CharsetEditor());
        this.defaultEditors.put(Class.class, new ClassEditor());
        this.defaultEditors.put(Class[].class, new ClassArrayEditor());
        this.defaultEditors.put(Currency.class, new CurrencyEditor());
        this.defaultEditors.put(File.class, new FileEditor());
        this.defaultEditors.put(InputStream.class, new InputStreamEditor());
        this.defaultEditors.put(InputSource.class, new InputSourceEditor());
        this.defaultEditors.put(Locale.class, new LocaleEditor());
        if(pathClass != null) {
            this.defaultEditors.put(pathClass, new PathEditor());
        }

        this.defaultEditors.put(Pattern.class, new PatternEditor());
        this.defaultEditors.put(Properties.class, new PropertiesEditor());
        this.defaultEditors.put(Reader.class, new ReaderEditor());
        this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
        this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
        this.defaultEditors.put(URI.class, new URIEditor());
        this.defaultEditors.put(URL.class, new URLEditor());
        this.defaultEditors.put(UUID.class, new UUIDEditor());
        if(zoneIdClass != null) {
            this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
        }

        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
        this.defaultEditors.put(Character.TYPE, new CharacterEditor(false));
        this.defaultEditors.put(Character.class, new CharacterEditor(true));
        this.defaultEditors.put(Boolean.TYPE, new CustomBooleanEditor(false));
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
        this.defaultEditors.put(Byte.TYPE, new CustomNumberEditor(Byte.class, false));
        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
        this.defaultEditors.put(Short.TYPE, new CustomNumberEditor(Short.class, false));
        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
        this.defaultEditors.put(Integer.TYPE, new CustomNumberEditor(Integer.class, false));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
        this.defaultEditors.put(Long.TYPE, new CustomNumberEditor(Long.class, false));
        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
        this.defaultEditors.put(Float.TYPE, new CustomNumberEditor(Float.class, false));
        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
        this.defaultEditors.put(Double.TYPE, new CustomNumberEditor(Double.class, false));
        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
        this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
        this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
        if(this.configValueEditorsActive) {
            StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
            this.defaultEditors.put(String[].class, sae);
            this.defaultEditors.put(short[].class, sae);
            this.defaultEditors.put(int[].class, sae);
            this.defaultEditors.put(long[].class, sae);
        }

    }

繼續跟蹤this.prepareBeanFactory(beanFactory)方法中的beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));,該方法主要還是用來註冊BeanPostProcessor.

/**
    該方法的重要邏輯都在ApplicationContextAwareProcessor中。
    前面幾章提到過Spring啟用bean的init-method的前後,會呼叫BeanPostProcessor中的
    postProcessBeforeInitialization和postProcessAfterInitialization方法。
    ApplicationContextAwareProcessor類的原始碼實現:
**/
class ApplicationContextAwareProcessor implements BeanPostProcessor {
    private final ConfigurableApplicationContext applicationContext;
    private final StringValueResolver embeddedValueResolver;

    public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
    }

    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;
        if(System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if(acc != null) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    ApplicationContextAwareProcessor.this.invokeAwareInterfaces(bean);
                    return null;
                }
            }, acc);
        } else {
            this.invokeAwareInterfaces(bean);
        }

        return bean;
    }

    //通過此方法可以看出實現了Aware介面的bean在被初始化之後,可以獲得一些對應的資源。之前設定的忽略自動裝配的介面就是在此處生效的。
    private void invokeAwareInterfaces(Object bean) {
        if(bean instanceof Aware) {
            if(bean instanceof EnvironmentAware) {
                ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
            }

            if(bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }

            if(bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
            }

            if(bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
            }

            if(bean instanceof MessageSourceAware) {
                ((MessageSourceAware)bean).setMessageSource(this.applicationContext);
            }

            if(bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
            }
        }

    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}