Spring Container的擴充套件點
阿新 • • 發佈:2019-01-01
Spring在解析完配置檔案後,會呼叫一些callback方法,使用Spring的開發者可以通過提供這些callback方法達到對Spring Container的擴充套件.
1,通過實現BeanPostProcessor來完成對某些Bean的一些'定製',BeanPostProcessor定義了兩個方法,postProcessBeforeInitialization(Object bean, String beanName)和postProcessAfterInitialization(Object bean, String beanName).
postProcessBeforeInitialization會在Spring對bean初始化完成之後,依賴註冊(對property指定的成員變數完成了賦值)已經完成,但是Container還沒有呼叫申明的initialization方法(如afterPropertiesSet,init-method)之前被呼叫.postProcessAfterInitialization會在在Container呼叫申明的initialization方法(如afterPropertiesSet)之後被呼叫.如果需要有多個實現了BeanPostProcessor的類,可以通過讓其實現Ordered介面來控制這些類的callback被呼叫的順序.
這種bean的post-processor一般用來檢查bean是否實現了某個介面,或者把bean包裝成某個proxy,Spring的AOP某些框架類就是實現了BeanPostProcessor.
例:
如果在配置檔案裡就只有BeanPostProcessor本身,沒有其他的bean,如只有<beanclass="com.test.spring.extent.MyBeanPostProcessor"/>配置,postProcessBeforeInitialization和postProcessAfterInitialization這兩個方法是不會被呼叫的.BeanPostProcessor會在普通bean被初始化之前先被容器初始化.
呼叫程式碼:
Intf intfBean = (Intf) ctx.getBean("IntfBean");
System.out.println(intfBean.testFunc());
這樣,MyBeanPostProcessor會把實現Intf介面的bean包裝成一個proxy.
2,通過實現BeanFactoryPostProcessor介面來操作配置檔案,對配置的元資料進行'特製'.Spring IoC容器允許BeanFactoryPostProcessor在容器實際例項化任何其它的bean之前讀取配置元資料,並有可能修改它。Spring自身的PropertyPlaceholderConfigurer就實現了這個介面,通過對XML配置檔案中使用佔位符,PropertyPlaceholderConfigurer從別的property檔案中讀取值進行替換.
例子:從ext.properties讀取值替換${TEST.PROP1},${TEST.PROP2}.
通常比較有用的場景是資料庫的url,使用者名稱,密碼的配置,還可以用來動態指定某個bean的類名,把XMl檔案和property檔案分開維護更容易.
<bean id="xxxBean" class="${com.xxx.class}"/>
自定義的BeanFactoryPostProcessor,通過實現Ordered介面可以改變被callback的順序.callback方法中的ConfigurableListableBeanFactory beanFactory提供了配置檔案的元資料.(*碰到一個困惑的問題是: PropertyPlaceholderConfigurer的order是 Integer.MAX_VALUE,卻發現它比自己定義的order 為-1的 processor先執行.)
1,通過實現BeanPostProcessor來完成對某些Bean的一些'定製',BeanPostProcessor定義了兩個方法,postProcessBeforeInitialization(Object bean, String beanName)和postProcessAfterInitialization(Object bean, String beanName).
postProcessBeforeInitialization會在Spring對bean初始化完成之後,依賴註冊(對property指定的成員變數完成了賦值)已經完成,但是Container還沒有呼叫申明的initialization方法(如afterPropertiesSet,init-method)之前被呼叫.postProcessAfterInitialization會在在Container呼叫申明的initialization方法(如afterPropertiesSet)之後被呼叫.如果需要有多個實現了BeanPostProcessor的類,可以通過讓其實現Ordered介面來控制這些類的callback被呼叫的順序.
這種bean的post-processor一般用來檢查bean是否實現了某個介面,或者把bean包裝成某個proxy,Spring的AOP某些框架類就是實現了BeanPostProcessor.
例:
注意這裡實現BeanPostProcessor的類是針對Ioc容器裡其他的bean呼叫這兩個方法,不是針對BeanPostProcessor自身和其他BeanPostProcessor呼叫這兩個方法,public class MyBeanPostProcessor implements BeanPostProcessor, InvocationHandler { private Object proxyobj; public MyBeanPostProcessor() { } public MyBeanPostProcessor(Object obj) { proxyobj = obj; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization Bean '" + beanName + "' created : " + bean.toString()); if (bean instanceof Intf) { Class cls = bean.getClass(); return Proxy.newProxyInstance(cls.getClassLoader(), cls .getInterfaces(), new MyBeanPostProcessor(bean)); } else { return bean; } } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization Bean '" + beanName + "' created : " + bean.toString()); return bean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy is " + proxy.getClass().getName()); System.out.println("before calling " + method); if (args != null) { for (int i = 0; i < args.length; i++) { System.out.println(args[i] + ""); } } Object o = method.invoke(proxyobj, args); System.out.println("after calling " + method); return o; } } public interface Intf { public String testFunc(); } public class IntfBean implements Intf { private String strVal = "default value"; @Override public String testFunc() { // TODO Auto-generated method stub return strVal; } } <bean id="IntfBean" class="com.test.spring.extent.IntfBean" /> <bean class="com.test.spring.extent.MyBeanPostProcessor" />
如果在配置檔案裡就只有BeanPostProcessor本身,沒有其他的bean,如只有<beanclass="com.test.spring.extent.MyBeanPostProcessor"/>配置,postProcessBeforeInitialization和postProcessAfterInitialization這兩個方法是不會被呼叫的.BeanPostProcessor會在普通bean被初始化之前先被容器初始化.
呼叫程式碼:
Intf intfBean = (Intf) ctx.getBean("IntfBean");
System.out.println(intfBean.testFunc());
這樣,MyBeanPostProcessor會把實現Intf介面的bean包裝成一個proxy.
2,通過實現BeanFactoryPostProcessor介面來操作配置檔案,對配置的元資料進行'特製'.Spring IoC容器允許BeanFactoryPostProcessor在容器實際例項化任何其它的bean之前讀取配置元資料,並有可能修改它。Spring自身的PropertyPlaceholderConfigurer就實現了這個介面,通過對XML配置檔案中使用佔位符,PropertyPlaceholderConfigurer從別的property檔案中讀取值進行替換.
例子:從ext.properties讀取值替換${TEST.PROP1},${TEST.PROP2}.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:conf/ext.properties" /> <property name="properties"> <value>TEST.PROP3=inside property</value> </property> </bean> <bean id="PropReplaceBean" class="com.test.spring.extent.PropReplaceBean"> <property name="strVal" value="${TEST.PROP1}" /> <property name="intVal" value="${TEST.PROP2}" /> <property name="insideProp" value="${TEST.PROP3}" /> </bean>
通常比較有用的場景是資料庫的url,使用者名稱,密碼的配置,還可以用來動態指定某個bean的類名,把XMl檔案和property檔案分開維護更容易.
<bean id="xxxBean" class="${com.xxx.class}"/>
自定義的BeanFactoryPostProcessor,通過實現Ordered介面可以改變被callback的順序.callback方法中的ConfigurableListableBeanFactory beanFactory提供了配置檔案的元資料.(*碰到一個困惑的問題是: PropertyPlaceholderConfigurer的order是 Integer.MAX_VALUE,卻發現它比自己定義的order 為-1的 processor先執行.)
Spring檢測到BeanPostProcessor和BeanFactoryPostProcessor後會由容器自動呼叫它們的callback方法,不用在程式碼裡主動的去呼叫.<bean class="com.test.spring.extent.MyBeanFactoryPostProcessor" /> public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered { private int order = -1; public void setOrder(int order) { this.order = order; } public int getOrder() { return this.order; } @Override public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition beanDefinition = beanFactory .getBeanDefinition("PropReplaceBean"); MutablePropertyValues pvs = beanDefinition.getPropertyValues(); PropertyValue[] pvArray = pvs.getPropertyValues(); for (PropertyValue pv : pvArray) { System.out.println(pv.getName() + " " + pv.getValue().getClass()); } } }