ApplicationContext(四)BeanFactory 功能擴展

ApplicationContext(四)BeanFactory 功能擴展

上節咱們提到容器刷新的第二步初始化 BeanFactory 工廠並解析配製文件,但此時 BeanFactory 的功能還很簡單,須要對其進行擴展。這就涉及到下面第三步:BeanFactory 功能擴展。java

那 Spring 究竟進行了那些功能擴展呢?git

源代碼【AbstractApplicationContext】github

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 1. 設置 beanFactory 的 classLoader 爲當前 context 的 classLoader
    beanFactory.setBeanClassLoader(getClassLoader());

    // 2. 設置 beanFactory 的表達式語言處理器,Spring3 增長了表達式語言的支持
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

    // 3. 爲 beanFactory 增長一個默認的 propertyEditor,這個主要是對 bean 的屬性等設置管理的一個工具
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 4. 添加 BeanPostProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    // 5. 設置了幾個忽略自動裝配的接口
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

    // 6. 設置了幾個自動裝配的特殊規則
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 7. 增長對 AspectJ 的支持
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 8. 添加默認的系統環境 bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

上面函數中主要進行了幾個方面的擴展。spring

(1) 增長對 SPEL 語言的支持app

(2) 增長對屬性編輯器的支持編輯器

(3) 增長對一些內置類,好比 Environmentaware、 Messagesourceaware 的信息注入。ide

(4) 設置了依賴功能可忽略的接口函數

(5) 註冊一些固定依的屬性工具

(6) 增長 Aspect的支持(會在第7章中進行詳細的講解)post

(7) 將相關環境變量及屬性註冊以單例模式註冊

可能讀者不是很理解每一個步驟的具體含義,接下來咱們會對各個步驟進行詳細地分析。

1、增長對 SPEL 語言的支持

SpEL使用 #{...} 做爲定界符,全部在大框號中的字符都將被認爲是 SpEL,使用格式以下:

@Value("#{19 - 1}")
private int age3;

在源碼中經過代碼 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())) 註冊語言解析器,就能夠對 SPEL 進行解析了,那麼在註冊解析器後 Spring 又是在何時調用這個解析器進行解析呢?詳見 《SPEL語言執行過程》

2、增長對屬性編輯器的支持

2.1 屬性編輯器的基本用法

在 Spring DI 注入的時候能夠把普通屬性注入進來,可是像 Date 類型就沒法被識別。例如:

public class UserManager {
    private Date data;
    // 省略get/set
}

上面代碼中,須要對日期型屬性進行注入:

<bean id="userManager" class="UserManager">
    <property name="data" value="2018-08-01 00:00:00"/>
</bean>

若是直接這樣使用,程序則會報異常,類型轉換不成功。由於在 Usermanager 中的 data Value 屬性是 Date 類型型的,而在 XML中配置的倒是 String 類型的,因此固然會報異常。

Spring 針對此問題提供了兩種解決辦法。

1. 使用用自定義屬性編輯器

使用自定義屬性編輯器,經過繼承 PropertyEditorSupport,重寫 setastext 方法,具體步驟以下。

(1) 編寫自定義的屬性編輯器。

public class DatePropertyEdit extends PropertyEditorSupport implements PropertyEditor {
    private String format = "yyyy-MM-dd HH:mm:ss";
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            Date date = sdf.parse(text);
            this.setValue(date);
        } catch (ParseException e) {
        }
    }
}

(2) 將自定義屬性編輯器註冊到 Spring 中。

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="java.util.Date"
                   value="com.github.binarylei.spring01.day0728.propertyedit.DataPropertyEdit">
            </entry>
        </map>
    </property>
</bean>

在配置文件中引入類型爲 CustomEditorConfigurer 的 bean,並在屬性 customeditors 中加入自定義的屬性編輯器,其中 key 爲屬性編輯器所對應的類型。經過這樣的配置,當 Spring 在注入 bean 的屬性時一旦遇到了 java.uti.Date 類型的屬性會自動調用自定義的 DatepropertyEditor 解解析器進行解析,並用解析結果代替配置屬性進行注入。

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

經過註冊 Spring 自帶的屬性編輯器 CustomDateEditor,具體步驟以下。

(1) 定義屬性編輯器

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

(2) 註冊到 Spring

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <bean class="spring.context.spring_di.DatePropertyEditorRegistar"/>
        </list>
    </property>
</bean>

經過在配置文件中將自定義的 DatePropertyEditorRegistar 註冊進人 org.springframework.beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 屬性中,能夠具備與方法 1 一樣的效果咱們瞭解了自定義屬性編輯器的使用。

可是,彷佛這與本節中圍繞的核心代碼 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 並沒有聯繫,由於在註冊自定義屬性編輯器的時候使用的是 PropertyEditorRegistrar 的 registerCustomEditors 方法,而這裏使用的是 ConfigurableListableBeanFactory 的 addPropertyEditorRegistrar 方法。咱們不妨深刻探索下 ResourceEditorRegistrar 的內部實現,在 ResourceEditorRegistrar 中,咱們最關心的方法是 registerCustomEditor 方法。

2.2 屬性編輯器的內部原理

beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))

源代碼【AbstractBeanFactory】

public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
    Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
    this.propertyEditorRegistrars.add(registrar);
}

源代碼【ResourceEditorRegistrar】

// 1. PropertyEditorRegistrar 將經常使用的資源編輯器註冊到 PropertyEditorRegistry 上,至關於一個工具類
// 2. PropertyEditorRegistry 實際持有編輯器
public void registerCustomEditors(PropertyEditorRegistry registry) {
    ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
    doRegisterEditor(registry, Resource.class, baseEditor);
    doRegisterEditor(registry, ContextResource.class, baseEditor);
    doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
    doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
    doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
    doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
    doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

    ClassLoader classLoader = this.resourceLoader.getClassLoader();
    doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
    doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
    doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

    if (this.resourceLoader instanceof ResourcePatternResolver) {
        doRegisterEditor(registry, Resource[].class,
                new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
    }
}

/**
 * 註冊對應的屬性編輯器
 */
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
    if (registry instanceof PropertyEditorRegistrySupport) {
        ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
    }
    else {
        registry.registerCustomEditor(requiredType, editor);
    }
}

在 doRegisterEditor 函數中,能夠看到在以前提到的自定義屬性中使用的關鍵代碼 registry.registerCustomEditor(requiredType, editor),回過頭來看 ResourceEditorRegistrar 類的 registerCustomEditors 方法的核心功能,其實無非是註冊了一系列的經常使用類型的屬性編輯器,例如,代碼 doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)) 實現的功能就是註冊 Class 類對應的屬性編輯器。那麼,註冊後,一且某個實體 bean 中存在一些 Class 類型的屬性,那麼 Spring 會調用 Classeditor 將配置中定義的 String 類型轉換爲 Class 類型並進行賦值。

分析到這裏,咱們不由有個疑問,雖然說 ResourceEditorRegistrar 類的 registerCustomEditors 方法實現了批量註冊的功能,可是 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 僅僅是註冊了 ResourceEditorRegistrar 實例,卻並無調用 ResourceEditorRegistrar 的 registerCustomEditors 方法進行註冊,那麼究竟是在何時進行註冊的呢?

源代碼【Abstractbeanfactory】

// 如此,BeanWrapper 就註冊了各類屬性編輯器,能夠在 populate() 時解析各類 String 類型的字段進行注入
protected void initBeanWrapper(BeanWrapper bw) {
    bw.setConversionService(getConversionService());
    registerCustomEditors(bw);
}

// 調用 propertyEditorRegistrars 爲 registry 注入屬性編輯器
protected void registerCustomEditors(PropertyEditorRegistry registry) {
    PropertyEditorRegistrySupport registrySupport =
            (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
    if (registrySupport != null) {
        registrySupport.useConfigValueEditors();
    }
    if (!this.propertyEditorRegistrars.isEmpty()) {
        for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
            registrar.registerCustomEditors(registry);
        }
    }
    if (!this.customEditors.isEmpty()) {
        for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
            Class<?> requiredType = entry.getKey();
            Class<? extends PropertyEditor> editorClass = entry.getValue();
            registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass));
        }
    }
}

既然提到了 BeanWrapper,這裏也有必要強調下, Spring 中用於封裝 bean 的是 BeanWrapper 類型,而它又間接繼承了 PropertyEditorRegistry 類型,也就是咱們以前反覆看到的方法參數 PropertyEditorRegistry registry,其實大部分狀況下都是 BeanWrapper,對於 BeanWrapper 在 Spring 中的默認實現是 BeanWrapperlmpl,而 BeanWrapperlmpl 除了實現 BeanWrapper 接口外還繼承了 PropertyEditorRegistrySupport,在 PropertyEditorRegistrySupport 中有這樣一個方法

源代碼【PropertyEditorRegistrySupport】

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
    // 在 PropertyEditorRegistrySupport 中註冊了一系列默認的屬性編輯器
    private void createDefaultEditors() {
        this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);

        // Simple editors, without parameterization capabilities.
        // The JDK does not contain a default editor for any of these target types.
        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());
        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());
        }

        // Default instances of collection editors.
        // Can be overridden by registering custom instances of those as custom editors.
        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));

        // Default editors for primitive arrays.
        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

        // The JDK does not contain a default editor for char!
        this.defaultEditors.put(char.class, new CharacterEditor(false));
        this.defaultEditors.put(Character.class, new CharacterEditor(true));

        // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
        this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

        // The JDK does not contain default editors for number wrapper types!
        // Override JDK primitive number editors with our own CustomNumberEditor.
        this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
        this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
        this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
        this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
        this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
        this.defaultEditors.put(double.class, 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));

        // Only register config value editors if explicitly requested.
        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);
        }
    }
}

2.3 添加 ApplicationContextAwareProcessor 處理器

在 Spring 中能夠注入一些底層的組件,怎麼實現的呢?這就是 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)) 的做用了。首先回顧一下 ApplicationContextAware 的使用方法:

// 可使用 ApplicationContext 組件
@Service
public class Bean implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}

很顯然 context 是在 bean 實例化完成,屬性注入後準備進行 init-method 方法前完成的。咱們看一下代碼:

源代碼【PropertyEditorRegistrySupport】

// 拋開各類異常處理 Spring 做了以下的處理,這裏咱們重點關注 applyBeanPostProcessorsBeforeInitialization
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    
    // 1. 注入 Spring 組件
    invokeAwareMethods(beanName, bean);

    // 2. 執行 init-method 前的回調,ApplicationContextAware 就是在這一步注入屬性
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

    // 3. 執行 init-method 方法
    invokeInitMethods(beanName, wrappedBean, mbd);
    
    // 4. 執行 init-method 後的回調
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    return wrappedBean;
}

// 注入 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

// 執行後置處理器,ApplicationContextAware 在這裏注入了部分其它的 Aware
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}

下面咱們看一下 ApplicationContextAwareProcessor 都作了那些事情,postProcessBeforeInitialization 實際上事情委託給了 invokeAwareInterfaces()

class ApplicationContextAwareProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
           
        invokeAwareInterfaces(bean);

        return 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(
                        new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
            }
            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);
            }
        }
    }
}

2.4 設置忽略依賴

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

很明顯 ApplicationContextAware 不能再注入到 bean 中:

public class Bean {
    @Autowired
    private ApplicationContextAware applicationContextAware;
}

2.5 註冊依賴

beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

Spring 還簡化了底層組件的注入問題:

public class Bean {
    @Autowired
    private ApplicationContext context;
}

天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索