架構師必須掌握的知識——spring容器擴充套件點
架構師必須掌握的知識——spring容器擴充套件點
寫作意圖
spring作為目前最為主流的框架,能掌握它的各個知識點是必不可少的技能,有些知識在業務程式碼中不經常使用,但在框架開發時會經常用到。這篇文章的知識就是如此,希望這篇文章能起到夯實基礎的作用。本人能力有限,理解不當的地方在所難免,希望各位看官指正。
文章閱讀建議
文章遵循帶著問題閱讀的方式,這樣能深刻掌握知識。文章會先丟擲要解決的問題,引導思考,一步步闡明如何解決問題。
【同時也更新到了頭條上https://www.toutiao.com/i6641387847611859464/】
本次主題
本次總結的主題是spring core部分的1.8節內容——容器擴充套件點
本節要解決的問題_什麼時候需要用到容器擴充套件點
- 先想一想容器解決什麼問題?
答:管理beans,例項化beans,獲取beans,依據是beans的定義(比如xml,@Bean會解析成Bean的定義物件)
【注意這裡我用的是複數beans,因為對於單個bean有生命週期的擴充套件點,這會在以後文章中介紹。不要搞混了】
知道了容器能做什麼,下面我們就能回答以下問題了
- 擴充套件容器能擴充套件什麼?
答:自定義bean、自定義bean的定義、自定義bean的例項化邏輯。特別是需要在執行時才能確定屬性的賦值,需要哪些依賴的問題。比如通過xml可以配置依賴,但是如果我的依賴需要根據引用的jar包不同,依賴不同怎麼辦。下面是一些其他的例子
【舉個例子】:
a. 自定義bean:我希望bean在初始化之前根據一些條件改變bean的屬性值,比如有的屬性值在某些條件下從環境變數中取值。
b.自定義bean的定義:比如我希望根據一些條件給bean新增一些屬性,修改依賴的bean,這些條件在執行時才能判斷,比如引入的jar包。
c.自定義bean的例項化邏輯:我希望根據一定條件判斷例項化的bean型別
如何擴充套件
估計你首先會想到繼承容器物件,這是很自然的想法,但是遺憾的是官方並不推薦
通過可插拔的介面可以更優雅的實現擴充套件,我們看看有哪些介面吧
我們再進一步看看他們提供了什麼方法
1. BeanPostProcessor
- 為什麼叫PostProcessor?
答:bean已經完成了屬性賦值的後處理 - 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
- 也叫做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
-
從名字中能看出什麼含義?
答:從factory能看出是負責物件建立的,bean說明它是一個bean(注意不要和BeanFactory弄混了) -
建立物件的時機是什麼?
答:既然是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介面設定執行順序,延遲初始化設定對其無效等。這些特性總結如下: