1. 程式人生 > >架構師必須掌握的知識——spring容器擴充套件點

架構師必須掌握的知識——spring容器擴充套件點

架構師必須掌握的知識——spring容器擴充套件點

寫作意圖

spring作為目前最為主流的框架,能掌握它的各個知識點是必不可少的技能,有些知識在業務程式碼中不經常使用,但在框架開發時會經常用到。這篇文章的知識就是如此,希望這篇文章能起到夯實基礎的作用。本人能力有限,理解不當的地方在所難免,希望各位看官指正。

文章閱讀建議

文章遵循帶著問題閱讀的方式,這樣能深刻掌握知識。文章會先丟擲要解決的問題,引導思考,一步步闡明如何解決問題。
【同時也更新到了頭條上https://www.toutiao.com/i6641387847611859464/

本次主題

本次總結的主題是spring core部分的1.8節內容——容器擴充套件點

本節要解決的問題_什麼時候需要用到容器擴充套件點

  1. 先想一想容器解決什麼問題?
    答:管理beans,例項化beans,獲取beans,依據是beans的定義(比如xml,@Bean會解析成Bean的定義物件)
    【注意這裡我用的是複數beans,因為對於單個bean有生命週期的擴充套件點,這會在以後文章中介紹。不要搞混了】

知道了容器能做什麼,下面我們就能回答以下問題了

  1. 擴充套件容器能擴充套件什麼?
    答:自定義bean、自定義bean的定義、自定義bean的例項化邏輯。特別是需要在執行時才能確定屬性的賦值,需要哪些依賴的問題。比如通過xml可以配置依賴,但是如果我的依賴需要根據引用的jar包不同,依賴不同怎麼辦。下面是一些其他的例子
    【舉個例子】:
    a. 自定義bean:我希望bean在初始化之前根據一些條件改變bean的屬性值,比如有的屬性值在某些條件下從環境變數中取值。
    b.自定義bean的定義:比如我希望根據一些條件給bean新增一些屬性,修改依賴的bean,這些條件在執行時才能判斷,比如引入的jar包。
    c.自定義bean的例項化邏輯:我希望根據一定條件判斷例項化的bean型別

如何擴充套件

估計你首先會想到繼承容器物件,這是很自然的想法,但是遺憾的是官方並不推薦
能想到的思路
通過可插拔的介面可以更優雅的實現擴充套件,我們看看有哪些介面吧
在這裡插入圖片描述
我們再進一步看看他們提供了什麼方法
在這裡插入圖片描述

1. BeanPostProcessor
  1. 為什麼叫PostProcessor?
    答:bean已經完成了屬性賦值的後處理
  2. postProcessBefore/AfterInitializing方法名透露了什麼資訊?
    答:在bean屬性賦值後,初始化之前/後呼叫(比如init-method,這屬於bean的生命週期擴充套件點,我們會在下一篇文章介紹)
    在這裡插入圖片描述

特別注意,由於是容器的擴充套件點,方法會對所有的bean進行回撥,我們需要判斷是否是我們需要處理的bean物件,比如:
if (bean instanceof MybatisProperties) { }

【舉個例子】

@Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MybatisProperties) {
            MybatisProperties mybatisProperties = (MybatisProperties) bean;
            // 根據條件修改mybatisProperties的屬性值
            if(xxx){
            	mybatisPropertiess.setXXX(xxx);
            }
        }
        return bean;
    }
@Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MybatisProperties) {
           MybatisProperties mybatisProperties = (MybatisProperties) bean;
            // 包裝,或者代理
           MybatisProperties mybatisPropertiesProxy = Wrap(mybatisProperties);
           return mybatisPropertiesProxy;
        }
        return bean;
    }
2. BeanFactoryPostProcessor
  1. 也叫做PostProcessor,它是什麼時候的後處理?
    答:容器被初始化以後,所有的bean的定義被載入到容器後,所有的bean沒有被初始化

    【舉個例子】
    transactionManager根據不同的資料來源型別,動態定義依賴。這個時候你無法用xml或者@Bean去定義依賴,因為只有執行環境你才能知道引入了哪些資料來源
@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
			throws BeansException {
		String[] transactionManagers = beanFactory
				.getBeanNamesForType(TransactionManager.class, true, false);
		for (String transactionManager : transactionManagers) {
			addTransactionManagerDependencies(beanFactory, transactionManager);
		}
	}

	private void addTransactionManagerDependencies(
			ConfigurableListableBeanFactory beanFactory, String transactionManager) {
		for (String dependentBeanName : getBeanNamesForType(beanFactory,
				"javax.jms.ConnectionFactory")) {
			beanFactory.registerDependentBean(transactionManager, dependentBeanName);
		}
		for (String dependentBeanName : getBeanNamesForType(beanFactory,
				"javax.sql.DataSource")) {
			beanFactory.registerDependentBean(transactionManager, dependentBeanName);
		}
	}
3. FactoryBean
  1. 從名字中能看出什麼含義?
    答:從factory能看出是負責物件建立的,bean說明它是一個bean(注意不要和BeanFactory弄混了)

  2. 建立物件的時機是什麼?
    答:既然是spring,時機是獲取相應bean的時候
    在這裡插入圖片描述
    【舉個例子】
    通過FactoryBean,可以通過ProxyFactory(後面的文章會介紹到)返回一個代理物件,這個物件會產生一個實現了介面的空實現物件,介面可以注入,比如@Autowired。具體的業務邏輯通過MethodInterceptor介面攔截實現

@Override
    public synchronized Object getObject() throws Exception {
        if (this.proxy == null) {
            ProxyFactory factory = new ProxyFactory(MyInterceptor.class, myMethodInterceptor);
            this.proxy = factory.getProxy();
        }
        return this.proxy;
    }
@Autowired
private MyInterceptor myInterceptor;

這個時候呼叫myInterceptor的方法,會被myMethodInterceptor進行攔截,在其中實現業務邏輯

4. 其他特性

本主題的問題已經解決,我們還需要關注這些擴充套件點的其他一些特性,比如BeanPostProcessor和BeanFactoryPostProcessor他們是由applicationContext自動檢測生效的,他們的有效範圍只針對某個容器,他們可以用Ordered介面設定執行順序,延遲初始化設定對其無效等。這些特性總結如下:
在這裡插入圖片描述