1. 程式人生 > >ApplicationContext(四)BeanFactory 功能擴展

ApplicationContext(四)BeanFactory 功能擴展

ice onf ann factor extends embedded mbo 需要 ant

ApplicationContext(四)BeanFactory 功能擴展

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

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

源代碼【AbstractApplicationContext】

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());
    }
}

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

(1) 增加對 SPEL 語言的支持

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

(3) 增加對一些內置類,比如 Environmentaware、 Messagesourceaware 的信息註入。

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

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

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

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

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

一、增加對 SPEL 語言的支持

SpEL使用 #{...} 作為定界符,所有在大框號中的字符都將被認為是 SpEL,使用格式如下:

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

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

二、增加對屬性編輯器的支持

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;
}

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

ApplicationContext(四)BeanFactory 功能擴展