這些擴展其實是在初始過程中植入的鉤子或回調(diào)method源武,主要看refresh方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
...
}
}
}
ApplicationListener
ApplicationListener 其實是 spring 事件通知機制中核心概念博助。
在java的事件機制中盾致,一般會有三個概念:
- event object : 事件對象,描述事件本身一些屬性
- event source :事件源铺厨,產(chǎn)生事件的地方
- event listener :監(jiān)聽具體的事件并作出響應(yīng)
對應(yīng)的在Spring事件傳播機制中:
- 事件類:
ApplicationEvent
- 監(jiān)聽類:實現(xiàn)
ApplicationListener
接口 - 事件發(fā)布類偏竟,:實現(xiàn)
ApplicationEventPublisherAware
接口 - 將事件類和監(jiān)聽類交給Spring容器
例子
public class UserRegisterEvent extends ApplicationEvent {
public String name;
public UserRegisterEvent(Object o) {
super(o);
}
public UserRegisterEvent(Object o, String name) {
super(o);
this.name=name;
}
}
public class UserService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void setApplicationEventPublisher(ApplicationEventPublisher
applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void register(String name) {
System.out.println("用戶:" + name + " 已注冊!");
applicationEventPublisher.publishEvent(new UserRegisterEvent(name));
}
}
public class BonusServerListener implements
ApplicationListener<UserRegisterEvent> {
public void onApplicationEvent(UserRegisterEvent event) {
System.out.println("積分服務(wù)接到通知民轴,給 " + event.getSource() +
" 增加積分...");
}
}
// 注冊到容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userService" class="com.glmapper.extention.UserService"/>
<bean id="bonusServerListener"
class="com.glmapper.extention.BonusServerListener"/>
</beans>
public class MainTest {
public static void main(String[] args) {
ApplicationContext context =new
ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService)
context.getBean("userService");
//注冊事件觸發(fā)
userService.register("glmapper");
}
}
// 輸出
用戶:glmapper 已注冊攻柠!
積分服務(wù)接到通知球订,給 glmapper 增加積分...
FactroyBean
XML 方式的 AOP 就是通過該接口實現(xiàn)的
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
Spring 在 IOC 初始化的時候,一般的Bean都是直接調(diào)用構(gòu)造方法瑰钮,而如果該Bean實現(xiàn)了FactoryBean 接口冒滩,則會調(diào)用該Bean的 getObject 方法獲取bean,這也是Spring 使用此接口構(gòu)造AOP的原因浪谴。在 IOC 調(diào)用此方法的時候开睡,返回一個代理,完成AOP代理的創(chuàng)建较店。
BeanNameAware士八、ApplicationContextAware 和 BeanFactoryAware 針對bean工廠,可以獲取上下文梁呈,可以獲取當(dāng)前bena的id
ApplicationContextAware
這個比較常用
ApplicationContextAware中只有一個setApplicationContext方法婚度。實現(xiàn)了ApplicationContextAware接口的類,可以在該Bean被加載的過程中獲取Spring的應(yīng)用上下文ApplicationContext官卡,通過ApplicationContext可以獲取
Spring容器內(nèi)的很多信息蝗茁。
public class GlmapperApplicationContext implements
ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext
applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public ApplicationContext getApplicationContext(){
return applicationContext;
}
}
BeanFactoryAware
我們知道BeanFactory是整個Ioc容器最頂層的接口,它規(guī)定了容器的基本行為寻咒。實現(xiàn)BeanFactoryAware接口就表明當(dāng)前類具體BeanFactory的能力哮翘。
BeanFactoryAware接口中只有一個setBeanFactory方法。實現(xiàn)了BeanFactoryAware接口的類毛秘,可以在該Bean被加載的過程中獲取加載該Bean的BeanFactory饭寺,同時也可以獲取這個BeanFactory中加載的其它Bean。
有一個問題叫挟,我們?yōu)槭裁葱枰ㄟ^BeanFactory的getBean來獲取Bean呢艰匙?Spring已經(jīng)提供了很多便捷的注入方式,那么通過BeanFactory的getBean來獲取Bean有什么好處呢抹恳?來看一個場景:寫死的邏輯改為由動態(tài)配置決定
public interface HelloService {
void sayHello();
}
//英文打招呼實現(xiàn)
public class GlmapperHelloServiceImpl implements HelloService {
public void sayHello() {
System.out.println("Hello Glmapper");
}
}
//中文打招呼實現(xiàn)
public class LeishuHelloServiceImpl implements HelloService {
public void sayHello() {
System.out.println("你好员凝,磊叔");
}
}
// 之前做法
if (condition=="英文"){
glmapperHelloService.sayHello();
}
if (condition=="中文"){
leishuHelloService.sayHello();
}
// 有沒有什么方式可以動態(tài)的去決定客戶端類到底去調(diào)用哪一種語言實現(xiàn),而不是用過if-else方式來羅列呢奋献?
// 是的健霹,對于這些需要動態(tài)的去獲取對象的場景,BeanFactoryAware就可以很好的搞定瓶蚂。
public class GlmapperBeanFactory implements BeanFactoryAware {
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) throws
BeansException {
this.beanFactory=beanFactory;
}
/**
* 提供一個execute 方法來實現(xiàn)不同業(yè)務(wù)實現(xiàn)類的調(diào)度器方案糖埋。
* @param beanName
*/
public void execute(String beanName){
HelloService helloService=(HelloService)
beanFactory.getBean(beanName);
helloService.sayHello();
}
}
//
public class HelloFacade {
private GlmapperBeanFactory glmapperBeanFactory;
//調(diào)用glmapperBeanFactory的execute方法
public void sayHello(String beanName){
glmapperBeanFactory.execute(beanName);
}
public void setGlmapperBeanFactory(GlmapperBeanFactory beanFactory){
this.glmapperBeanFactory = beanFactory;
}
}
public class MainTest {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("beans.xml");
HelloFacade helloFacade = (HelloFacade)
context.getBean("helloFacade");
GlmapperBeanFactory glmapperBeanFactory = (GlmapperBeanFactory)
context.getBean("glmapperBeanFactory");
//這里其實可以不通過set方法注入到helloFacade中,
//可以在helloFacade中通過autowired
//注入扬跋;這里在使用main方法來執(zhí)行驗證阶捆,所以就手動set進入了
helloFacade.setGlmapperBeanFactory(glmapperBeanFactory);
//這個只需要傳入不同HelloService的實現(xiàn)類的beanName,
//就可以執(zhí)行不同的業(yè)務(wù)邏輯
helloFacade.sayHello("glmapperHelloService");
helloFacade.sayHello("leishuHelloService");
}
}
優(yōu)化后只需要傳入不同HelloService的實現(xiàn)類的beanName,就可以執(zhí)行不同的業(yè)務(wù)邏輯
Bean 初始化順序相關(guān)接口
驗證Spring-Bean初始化順序洒试,先看幾個關(guān)鍵接口
InitializingBean
Spirng的InitializingBean為bean提供了定義初始化方法的方式倍奢。InitializingBean是一個接口,它僅僅包含一個方法:afterPropertiesSet()垒棋。
在spring 初始化后卒煞,執(zhí)行完所有屬性設(shè)置方法(即setXxx)將自動調(diào)用 afterPropertiesSet(), 在配置文件中無須特別的配置, 但此方式增加了bean對spring 的依賴叼架,應(yīng)該盡量避免使用init-method(非接口)
Spring雖然可以通過InitializingBean完成一個bean初始化后對這個bean的回調(diào)畔裕,但是這種方式要求bean實現(xiàn) InitializingBean接口。一但bean實現(xiàn)了InitializingBean接口乖订,那么這個bean的代碼就和Spring耦合到一起了扮饶。通常情況下我不鼓勵bean直接實現(xiàn)InitializingBean,可以使用Spring提供的init-method的功能來執(zhí)行一個bean 子定義的初始化方法乍构。BeanFactoryPostProcessor接口
可以在spring的bean創(chuàng)建之前甜无,修改bean的定義屬性。也就是說哥遮,Spring允許BeanFactoryPostProcessor在容器實例化任何其它bean之前讀取配置元數(shù)據(jù)岂丘,并可以根據(jù)需要進行修改,例如可以把bean的scope從singleton改為prototype眠饮,也可以把property的值給修改掉奥帘。可以同時配置多個BeanFactoryPostProcessor仪召,并通過設(shè)置'order'屬性來控制各個BeanFactoryPostProcessor的執(zhí)行次序寨蹋。
注意:BeanFactoryPostProcessor是在spring容器加載了bean的定義文件之后,在bean實例化之前執(zhí)行的扔茅。接口方法的入?yún)⑹荂onfigurrableListableBeanFactory钥庇,使用該參數(shù),可以獲取到相關(guān)bean的定義信息BeanPostProcessor接口
BeanPostProcessor咖摹,可以在spring容器實例化bean之后,在執(zhí)行bean的初始化方法前后难述,添加一些自己的處理邏輯萤晴。這里說的初始化方法,指的是下面兩種:
1)bean實現(xiàn)了InitializingBean接口胁后,對應(yīng)的方法為afterPropertiesSet
2)在bean定義的時候店读,通過init-method設(shè)置的方法-
InstantiationAwareBeanPostProcessor
個接口實際上我們也是非常的熟悉,該接口在我們剖析注解配置AOP的時候是我們的老朋友攀芯,實際上屯断,注解配置的AOP是間接實現(xiàn) BeanPostProcess 接口的,而 InstantiationAwareBeanPostProcessor 就是繼承該接口的。
例子
public class SpringIoCBean implements InitializingBean, BeanPostProcessor, BeanFactoryPostProcessor, BeanFactoryAware,
BeanNameAware, DisposableBean {
static {
System.out.println(" static block ");
}
private String hello;
{
System.out.println("{} block hello = " + hello);
}
public SpringIoCBean(){
System.out.println("調(diào)用HelloWorld構(gòu)造器...");
}
public String getHello() {
return hello;
}
public void setHello(String hello) {
this.hello = hello;
System.out.println("調(diào)用setHello()...");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("調(diào)用BeanPostProcessor的postProcessBeforeInitialization() -- " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("調(diào)用BeanPostProcessor的postProcessAfterInitialization() -- "+ beanName);
return bean;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("調(diào)用InitializingBean的afterPropertiesSet()...");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory paramConfigurableListableBeanFactory)
throws BeansException {
System.out.println("調(diào)用BeanFactoryPostProcessor的postProcessBeanFactory()...");
}
@Override
public String toString() {
return "HelloWorld [hello=" + hello + "]";
}
@Override
public void setBeanName(String paramString) {
System.out.println("調(diào)用BeanNameAware.setBeanName paramString:"+paramString);
}
@Override
public void setBeanFactory(BeanFactory paramBeanFactory) throws BeansException {
System.out.println("調(diào)用BeanFactoryAware.setBeanFactory");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean 接口 destroy方法");
}
public void init() throws Exception {
System.out.println("HelloWorld類 init-method 方法");
}
}
// 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="springIoCBean" class="com.alicp.jetcache.test.spring.SpringIoCBean" init-method="init" >
<property name="hello" value="bigtest" />
</bean>
</beans>
// 測試類
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4進行測試
@ContextConfiguration({"classpath:spring-bean.xml"}) //加載配置文件
public class SpringBeanIoCTest {
@Autowired
private SpringIoCBean bean;
@Test
public void testMain(){
System.out.println("testMain");
}
}
// ======輸出
static block
{} block hello = null
調(diào)用HelloWorld構(gòu)造器...
調(diào)用setHello()...
調(diào)用BeanNameAware.setBeanName paramString:springIoCBean
調(diào)用BeanFactoryAware.setBeanFactory
調(diào)用InitializingBean的afterPropertiesSet()...
HelloWorld類 init-method 方法
調(diào)用BeanFactoryPostProcessor的postProcessBeanFactory()...
調(diào)用BeanPostProcessor的postProcessBeforeInitialization() -- org.springframework.context.event.internalEventListenerProcessor
調(diào)用BeanPostProcessor的postProcessAfterInitialization() -- org.springframework.context.event.internalEventListenerProcessor
調(diào)用BeanPostProcessor的postProcessBeforeInitialization() -- org.springframework.context.event.internalEventListenerFactory
調(diào)用BeanPostProcessor的postProcessAfterInitialization() -- org.springframework.context.event.internalEventListenerFactory
調(diào)用BeanPostProcessor的postProcessBeforeInitialization() -- com.alicp.jetcache.spring.SpringBeanIoCTest
調(diào)用BeanPostProcessor的postProcessAfterInitialization() -- com.alicp.jetcache.spring.SpringBeanIoCTest
testMain
DisposableBean 接口 destroy方法
由結(jié)果得出初始化bean順序
構(gòu)造函數(shù) construct
初始化屬性 property
BeanNameAware : setBeanName
BeanFactoryAware : 接口執(zhí)行setBeanFactory方法
InitializingBean 接口執(zhí)行afterPropertiesSet方法
如果在配置文件中指定了init-method殖演,那么執(zhí)行該方法
如果實現(xiàn)了BeanFactoryPostProcessor 接口在 “new”其他類之前執(zhí)行 postProcessBeanFactory 方法(通過這個方法可以改變配置文件里面的屬性值的配置)// 該bean只會調(diào)用一次
如果實現(xiàn)了BeanPostProcessor 接口氧秘,那么會在其他bean初始化方法之前執(zhí)行postProcessBeforeInitialization 方法,之后執(zhí)行postProcessAfterInitialization方法 // 該bean會在每個其他bean初始化前后得到調(diào)用趴久,所以可以用來過濾有關(guān)聯(lián)的bean相應(yīng)的處理