Spring核心——IOC功能擴展點(二)

全文共 1685 個字git

上一篇文章介紹了非侵入式的框架的概念以及IOC的功能擴展點之一——BeanPostProcessor,咱們接下來的內容繼續說明IoC更多的擴展方法。spring

BeanFactoryPostProcessor

BeanFactoryPostProcessor是針對整個容器的後置處理器。他的使用也很是簡單,只要向容器中添加一個繼承BeanFactoryPostProcessor的Bean便可。框架

如何使用

繼承了BeanFactoryPostProcessor接口的類PostProcessors:ide

package chkui.springcore.example.xml.beanfactorypostprocessor;

public class PostProcessors implements BeanFactoryPostProcessor{
@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
         //DO
    }
}

而後再向容器中添加這個Bean就增長了一個BeanFactoryPostProcessor。post

<?xml version="1.0" encoding="UTF-8"?>
<!-- xml.beanfactorypostprocessor -->
<beans>
    <bean class="chkui.springcore.example.xml.beanfactorypostprocessor.PostProcessors" />
</beans>

BeanFactoryPostProcessor主要用於處理容器相關的內容,他被觸發時機是在IoC容器加載完各類配置後,還沒執行Bean的初始化以前。這個時候除了PostProcessors這個Bean,其餘任何Bean都沒有被建立。 因此在BeanFactoryPostProcessor處理Bean是不合適的,Bean應該要到BeanPostProcessor中去處理,2者的區別就是前者面向容器,後者面向Bean。接下來將經過一個詳細例子來講明BeanFactoryPostProcessor和BeanPostProcessor的區別以及使用方式。期間還會介紹BeanDefinitio相關的內容。ui

BeanFactoryPostProcessor與BeanPostProcessor使用

(文中僅僅是示例代碼,沒法運行,源碼在https://gitee.com/chkui-com/spring-core-sample,如需下載請自行clone)this

建造者模式

下面將會經過一個例子介紹2者的使用方式和使用場景。例子使用建造者模式模擬組裝一臺我的電腦,分爲一下3步:spa

  1.  容器啓動以後,會將電腦的全部「配件」(Cpu、Graphics、Ram)都添加到容器中。
  2.  在PostProcessorS實現BeanFactoryPostProcessor接口,它的功能是向容器添加一個Pc對象。
  3.  在PostProcessor實現BeanPostProcessor接口。他的工做是組裝電腦——每個Bean都會檢查域上的@Autowired註解,並注入對應的部件,部件也會標記本身所屬的電腦。

下面是XML配置文件,它負責將Cpu、顯卡、內存等電腦經常使用品牌的部件放置到容器中等待組裝。此外它還添加了PostProcessorS和PostProcessor兩個後置處理器用於裝載電腦。.net

<beans>
    <bean class="chkui.springcore.example.xml.beanfactorypostprocessor.bean.Cpu">
     	<property name="brand" value="Amd"/>
    </bean>
    
    <bean class="chkui.springcore.example.xml.beanfactorypostprocessor.bean.Graphics">
     	<property name="brand" value="Nvdia"/>
    </bean>
    
    <bean class="chkui.springcore.example.xml.beanfactorypostprocessor.bean.Ram">
     	<property name="brand" value="Kingston"/>
    </bean>
    
    <bean class="chkui.springcore.example.xml.beanfactorypostprocessor.PostProcessor" />
    
    <bean class="chkui.springcore.example.xml.beanfactorypostprocessor.PostProcessors" />
</beans>

下面是一個Cpu對象的結構,他標記了品牌和所屬電腦。Graphics和Ram的結構和它如出一轍。設計

package chkui.springcore.example.xml.beanfactorypostprocessor.bean;

public class Cpu {
	private String brand;
	
	@Autowired
	private Pc belong;
}

注意這裏的@Autowired註解,咱們的配置文件並無開啓Spring的自動裝配功能,咱們將在PostProcessor實現自動裝配。

PostProcessorS的做用是向容器動態添加一個以前未定義的Bean——Pc。

package chkui.springcore.example.xml.beanfactorypostprocessor;

public class PostProcessors implements BeanFactoryPostProcessor{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		//獲取容器的註冊接口
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;
		//新建一個BeanDefinition用於動態裝配Bean
		GenericBeanDefinition defininition = new GenericBeanDefinition();
		//設置要添加的類
		defininition.setBeanClass(Pc.class);
		//註冊BeanDefinition
		registry.registerBeanDefinition("postBean", defininition);
	}
}

若是看過 Ioc結構介紹的這篇文章,你就會知道BeanFactory通過層層派生,實際上大部分接口都在一個類實現——DefaultListableBeanFactory,它除了實現ConfigurableListableBeanFactory接口,還實現了BeanDefinitionRegistry接口。BeanDefinitionRegistry提供了BeanDefinition的管理功能。

BeanDefinition與適配器模式

在上面的代碼中出現了BeanDefinition接口,這裏就順道說一說這個有趣的小玩意。關於他如何使用Spring的官網並無太詳細的介紹(至少我沒找到),網上卻是有各路大神的博客在解讀他的源碼,不過代碼只是表象,要理解他的整套設計思路才能提高。

關於BeanDefinition的使用模式,官網將其稱呼爲configuration metadata,直譯過來叫「配置元數據」。 他的做用有點相似於Context分層應用的效果(見Spring核心——上下文與IoC 關於 ApplicationContext的說明),目的就是將Bean的配置和初始化工做分紅2個互不干擾的部分。

咱們知道 Spring如今支持各類各樣的方式來添加Bean,好比在XML配置文件中使用<bean>標籤、使用@Component以及他的派生類註解、能夠在@Configuration類中生成、甚至還能夠經過RMI實現遠程配置等等。若是全部的這些配置來源直接和IoC容器產生關係生成Bean,那麼耦合度、代碼複雜度會愈來愈高,並且之後指不定何時又會加入什麼新的配置方式。

爲了解決這個問題Spring的大神們引入了適配器模式——IoC容器只接受BeanDefinition接口,IoC如何初始化一個Bean是僅僅是看BeanDefinition裏的信息。而各類配置方式都有本身的適配器,全部的適配器都會根據他所須要處理的內容來生成一個BeanDefinition的實現類。這樣,若是新增一個新的配置方式,增長一個適配器就能夠搞定。

Spring核心——IOC功能擴展點

因此,咱們也能夠利用BeanDefinitionRegistry接口向容器添加一個BeanDefinition,進而在隨後的執行過程當中IoC容器會根據 這個BeanDefinition建立一個對應的Bean。

BeanPostProcessor

前面已經提到,BeanFactoryPostProcessor用於處理容器級別的問題,而BeanPostProcessor用來處理每個Bean。咱們前面已經用BeanFactoryPostProcessor向容器添加了一個Pc對象的Bean,接下來咱們在BeanPostProcessor中處理每個Bean的自動注入註解。

package chkui.springcore.example.xml.beanfactorypostprocessor;

public class PostProcessor implements BeanPostProcessor, BeanFactoryAware {
	private ConfigurableListableBeanFactory beanFactory;
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return autowiredImplement(bean);
    }
	public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
	
	//自定義實現autowired功能
	private Object autowiredImplement(final Object bean) {
		for(Field field : bean.getClass().getDeclaredFields()) {
			Autowired value = field.getAnnotation(Autowired.class);
			if(null != value) {
				Object obj = beanFactory.getBean(field.getType());
				field.setAccessible(true);
				field.set(bean, obj);
			}
		}
		return bean;
	}
	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
	}
}

這裏的PostProcessor實現BeanFactoryAware接口來獲取 BeanFactory。自動注入的處理邏輯都在autowiredImplement方法中,它會掃描Bean的每個域檢查是否有@Autowired註解,若是有則根據這個域的Class類型到BeanFactory去獲取對應的Bean,而後反射注入。

最後咱們建立一個ApplicationContext來運行他們:

public class SampleApp {
    public static void main(String[] args) {
    	ApplicationContext context = new ClassPathXmlApplicationContext("xml/beanfactorypostprocessor/config.xml");
    	Pc pc = context.getBean(Pc.class);
        /**
        Pc Info: Graphics=Nvdia, Cpu=Amd, Ram=Kingston]
        */
        System.out.println(pc);
    }
}

本文介紹了BeanFactoryPostProcessor和BeanPostProcessor的使用方式,以及IoC容易是如何經過BeanDefinition裝載各類配置的。後續還會持續介紹Spring IoC容器的各類功能擴展點。

相關文章
相關標籤/搜索