? ? ? ?Spring啟動(dòng)過(guò)程中會(huì)找出IOC容器里面特定類型的Bean,之后自動(dòng)調(diào)用這些類型(一般是接口類)里面的方法。這種特性對(duì)我們非常有用,我們只需要實(shí)現(xiàn)這些特定類型的Bean并覆蓋其方法,在方法里面加入我們自定義的一些邏輯。Spring就會(huì)在恰當(dāng)?shù)臅r(shí)機(jī)調(diào)用我們定義的這些類里面的方法胧辽。讓我們可以做一些特別的邏輯。接下來(lái)我們就對(duì)這些特定類型的類做一個(gè)簡(jiǎn)單的收集公黑。
一 實(shí)現(xiàn)Aware接口的類
? ? ? ?Spring中有很多繼承Aware類的接口邑商。比如BeanFactoryAware、ApplicationContextAware凡蚜、ResourceLoaderAware人断、ServletContextAware等等.這些接口一般都有一個(gè)共同的回調(diào)方法setXXX()方法來(lái)設(shè)置對(duì)應(yīng)的Bean,讓我們實(shí)現(xiàn)Aware接口的Bean中獲取到每種Aware接口對(duì)應(yīng)的Bean朝蜘。
Aware:翻譯過(guò)來(lái)是知道的恶迈,已感知的,意識(shí)到的谱醇,所以這些接口從字面意思應(yīng)該是能感知到Aware對(duì)應(yīng)的Bean暇仲。
Aware接口 | Aware對(duì)應(yīng)的Beran | 備注 |
---|---|---|
ApplicationContextAware | ApplicationContext | Spring上下文 |
ApplicationEventPublisherAware | ApplicationEventPublisher | 用于發(fā)布事件 |
BeanNameAware | 組件名字 | 組件在IOC容器里面的名字 |
BeanFactoryAware | BeanFactory | 負(fù)責(zé)生產(chǎn)和管理Bean的工廠 |
BeanClassLoaderAware | BeanClassLoader | 讓Bean知道它是由哪一類裝載器負(fù)責(zé)裝載的 |
ImportAware | 用來(lái)處理自定義注解的,比如將注解里面的某些屬性值賦值給其他Bean | |
EnvironmentAware | Environment | 可以獲取到系統(tǒng)的環(huán)境變量信息 |
EmbeddedValueResolverAware | StringValueResolver | 可以獲取Spring加載properties文件的屬性值 |
LoadTimeWeaverAware | LoadTimeWeaver | 可獲取LoadTimeWeaver實(shí)例副渴,用于在加載時(shí)處理類定義 |
MessageSourceAware | MessageSource | 國(guó)際化處理相關(guān) |
NotificationPublisherAware | NotificationPublisher | JMX通知 |
ResourceLoaderAware | ResourceLoader | 可獲取Spring中配置的加載程序(ResourceLoader)奈附,用于對(duì)資源進(jìn)行訪問(wèn);可用于訪問(wèn)類l類路徑或文件資源 |
ServletConfigAware | ServletConfig | web開(kāi)發(fā)過(guò)程中獲取ServletConfig和ServletContext信息 |
ServletContextAware | ServletContext | web開(kāi)發(fā)過(guò)程中獲取ServletConfig和ServletContext信息 |
1.1 ApplicationContextAware
? ? ? ?ApplicationContextAware用于獲取ApplicationContext上下文的情況(僅僅適用于當(dāng)前運(yùn)行的代碼和已啟動(dòng)的Spring代碼處于同一個(gè)Spring上下文煮剧,否則獲取到的ApplicationContext是空的)斥滤。
? ? ? ?關(guān)于ApplicationContextAware的使用我們見(jiàn)到最多的一個(gè)應(yīng)用場(chǎng)景是。想在一個(gè)普通的類里面(非Bean)里面獲取到IOC容器里面的某個(gè)組件勉盅。這個(gè)時(shí)候我們一般會(huì)自定義一個(gè)類實(shí)現(xiàn)ApplicationContextAware接口(Spring啟動(dòng)過(guò)程中會(huì)找到這個(gè)類佑颇,然后會(huì)自動(dòng)執(zhí)行setApplicationContext()方法)得到ApplicationContext對(duì)象。當(dāng)然了在這個(gè)自定義的類里面我們會(huì)把ApplicationContext對(duì)象設(shè)置成static的草娜。之后在普通的類里面通過(guò)ApplicationContext我們就可以獲取Spring IOC容器里面所有的Bean挑胸。具體實(shí)例如下所示。
/**
* 通過(guò)ApplicationContextAware實(shí)現(xiàn)宰闰,在bean實(shí)例化后茬贵,經(jīng)過(guò)Aware掃描時(shí)凸克,
* 發(fā)現(xiàn)實(shí)現(xiàn)了ApplicationContextAware接口,就會(huì)調(diào)用setApplicationContext方法注入ApplicationContext對(duì)象闷沥,
* 這也是非常經(jīng)典的一種獲取上下文的方法。
*/
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
/**
* 從靜態(tài)變量applicationContext中得到Bean, 自動(dòng)轉(zhuǎn)型為所賦值對(duì)象的類型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 從靜態(tài)變量applicationContext中得到Bean, 自動(dòng)轉(zhuǎn)型為所賦值對(duì)象的類型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext為Null.
*/
public static void clearHolder() {
applicationContext = null;
}
/**
* 實(shí)現(xiàn)ApplicationContextAware接口, 注入Context到靜態(tài)變量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 檢查ApplicationContext不為空.
*/
private static void assertContextInjected() {
Preconditions.checkArgument(applicationContext != null, "applicaitonContext屬性未注入, 請(qǐng)?jiān)赼pplicationContext.xml中定義SpringContextHolder.");
}
}
也非常推薦大家在每個(gè)應(yīng)用里面都可以嘗試去建立這么一個(gè)這樣的類備用咐容。
1.2 ApplicationEventPublisherAware
? ? ? ?通過(guò)實(shí)現(xiàn)ApplicationEventPublisherAware接口舆逃,我們可以獲取到ApplicationEventPublisher對(duì)象。ApplicationEventPublisher可以用來(lái)發(fā)布事件戳粒。我們也可以通過(guò)@Autowired把ApplicationEventPublisher對(duì)象注入進(jìn)來(lái)路狮。
@Service
public class LoadConfigFileService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
/**
* 我們模擬一個(gè)這樣的場(chǎng)景,比如,我們Spring啟動(dòng)的時(shí)候需要加載兩部分的配置
* 1. 配置文件里面的配置
* 2. 數(shù)據(jù)庫(kù)里面的配置
* 但是這兩部分的配置我們又是在兩個(gè)Service里面實(shí)現(xiàn)的.
*/
public boolean loadConfigFIle() {
// TODO:加載配置文件里面的配置
// 發(fā)布事件
applicationEventPublisher.publishEvent(new ConfigFIleLoadSuccessEvent());
return true;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
1.3 BeanNameAware
? ? ? ?BeanNameAware接口的作用就是讓實(shí)現(xiàn)這個(gè)接口的Bean知道自己在Spring IOC容器里的名字。而且聽(tīng)官方的意思是這個(gè)接口更多的使用在spring的框架代碼中蔚约,實(shí)際開(kāi)發(fā)環(huán)境應(yīng)該不建議使用奄妨,因?yàn)閟pring認(rèn)為bean的名字與bean的聯(lián)系并不是很深。
1.4 BeanFactoryAware
? ? ? ?BeanFactoryAware用于獲取BeanFactory(負(fù)責(zé)生產(chǎn)和管理Bean的工廠)苹祟。拿到BeanFactory之后砸抛,我們就可以對(duì)Spring IOC容器做各種操作了,比如從容器里面獲取Bean树枫,判斷對(duì)應(yīng)的Bean是否在容器里面等等直焙。
1.5 BeanClassLoaderAware
? ? ? ?BeanClassLoaderAware的作用是讓受管Bean本身知道它是由哪一類裝載器負(fù)責(zé)裝載的。
1.6 ImportAware
? ? ? ?ImportAware的作用是用來(lái)處理自定義注解的砂轻,比如將注解的某些屬性值賦值給其他bean的屬性奔誓。有一點(diǎn)要注意的就是ImportAware也是要配合@Import()注解一起使用。
? ? ? ?我們通過(guò)一個(gè)簡(jiǎn)單的實(shí)例來(lái)說(shuō)明搔涝,通過(guò)ImportAware來(lái)修改某個(gè)Bean的屬性厨喂。把ChangeAttribute注解上的值設(shè)置給BeanImportAware組件的name屬性。
/**
* 注意這個(gè)類上的兩個(gè)注解的使用
* 1. @Import(BeanImportAware.class)庄呈,BeanImportAware類實(shí)現(xiàn)了ImportAware接口
* 2. @ChangeAttribute是我們自定義的一個(gè)注解蜕煌,用來(lái)帶參數(shù)的。會(huì)在BeanImportAware類里面去獲取這個(gè)主句
*/
@Configuration
@Import(BeanImportAware.class)
@ChangeAttribute(value = "tuacy")
public class ImportAwareConfig {
}
@Configuration
public class BeanImportAware implements ImportAware {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(ChangeAttribute.class.getName()));
if (annoAttrs != null) {
// 獲取到ChangeAttribute注解里面value對(duì)應(yīng)的值
name = (String) annoAttrs.get("value");
}
}
}
1.7 EnvironmentAware
? ? ? ?實(shí)現(xiàn)EnvironmentAware接口可以獲取到系統(tǒng)的環(huán)境變量信息抒痒。更加具體的信息可以去瞧一瞧Spring里面關(guān)于Environment的使用幌绍。我們也可以通過(guò)@Autowired把Environment注入進(jìn)來(lái)。
1.8 EmbeddedValueResolverAware
? ? ? ?實(shí)現(xiàn)該接口用于獲取接口獲取StringValueResolver對(duì)象故响。StringValueResolver可以獲取Spring讀取properties文件里面的內(nèi)容傀广。和@Value注解讀取配置文件內(nèi)容的作用類似。所以我們也可以用@Value注解來(lái)替代EmbeddedValueResolverAware的使用彩届。
1.9 LoadTimeWeaverAware
? ? ? ?組件實(shí)現(xiàn)該接口用于獲取LoadTimeWeaver對(duì)象伪冰,用于在加載時(shí)處理類定義。也可以通過(guò)@Autowired注入LoadTimeWeaver來(lái)替代LoadTimeWeaverAware接口的使用樟蠕。關(guān)于LoadTimeWeaver的具體使用我還沒(méi)找到贮聂。
1.10 MessageSourceAware
? ? ? ?MessageSourceAware主要用于獲取MessageSource靠柑,用來(lái)處理國(guó)際化相關(guān)。我們一般會(huì)直接在組件類名通過(guò)@Autowired注入MessageSource來(lái)替代MessageSourceAware接口的使用吓懈。更加具體的用法可以去看下MessageSource的使用歼冰。
1.11 NotificationPublisherAware
? ? ? ?組件實(shí)現(xiàn)該接口用于獲取JMX通知發(fā)布者NotificationPublisher對(duì)象。拿到NotificationPublisher對(duì)象之后就可以在Spring里面發(fā)布消息了耻警。也可以通過(guò)@Autowired注入NotificationPublisher對(duì)象隔嫡。詳細(xì)的用法可以去看看NotificationPublisher的使用。
1.12 ResourceLoaderAware
? ? ? ?組件實(shí)現(xiàn)該接口用于獲取Spring中配置的加載程序ResourceLoader對(duì)象甘穿,用于對(duì)資源進(jìn)行訪問(wèn)腮恩。另一種方式是通過(guò)@Autowired注入ResourceLoader對(duì)象。具體可以去看看ResourceLoader的使用温兼。
1.13 ServletConfigAware
? ? ? ?web開(kāi)發(fā)過(guò)程中實(shí)現(xiàn)該接口用于獲取ServletConfig對(duì)象秸滴。獲取ServletConfig和ServletContext信息。我們也可以通過(guò)@Autowired注入ServletConfig對(duì)象募判。
1.14 ServletContextAware
? ? ? ?web開(kāi)發(fā)過(guò)程中組件實(shí)現(xiàn)給接口用于獲取ServletContext對(duì)象荡含。
二 Spring IOC擴(kuò)展點(diǎn)
? ? ? ?Spring Boot里面Bean的生成,有一個(gè)特定的順序: 聲明(定義) --> 注冊(cè) --> 創(chuàng)建(實(shí)例化Instantiation) --> 初始化(Initialization)届垫。
- 聲明: 聲明一個(gè)Bean内颗。簡(jiǎn)單來(lái)說(shuō)就是做一個(gè)標(biāo)記,讓Spring知道他是一個(gè)Bean敦腔。比如我們經(jīng)常見(jiàn)到的@Component均澳、@Service、@Controller符衔、@Repository找前、@Bean這些注解都是在聲明定義一個(gè)Bean。
- 注冊(cè): Spring Boot通過(guò)讀取配置文件獲取各個(gè)Bean的聲明(定義)信息判族,并且對(duì)這些信息進(jìn)行注冊(cè)的過(guò)程躺盛。所有的Bean注冊(cè)在BeanDefinitioin中。(其實(shí)就是根據(jù)各個(gè)Bean的聲明形帮,把每個(gè)聲明的Bean轉(zhuǎn)換成BeanDefinitioin的一個(gè)過(guò)程)
- 創(chuàng)建(實(shí)例化--Instantiation): Bean的實(shí)例化則指的是Spring Boot通過(guò)Bean的注冊(cè)信息(BeanDefinitionin)對(duì)各個(gè)Bean進(jìn)行實(shí)例化的過(guò)程槽惫。
- 初始化(Initialization): 在Bean創(chuàng)建之后,有些Bean可能設(shè)置了創(chuàng)建之后需要執(zhí)行的方法辩撑。比如@PostConstruct注解界斜,或者@Bean注解里面的initMethod屬性。我們把這一部分操作當(dāng)做初始化合冀。
? ? ? ?Spring Boot對(duì)IOC的擴(kuò)展點(diǎn)各薇,就是讓我們可以侵入到Bean生成過(guò)程的各個(gè)階段。加入一些我們自定義的邏輯君躺。
IOC擴(kuò)展接口 | 解釋 |
---|---|
BeanFactoryPostProcessor | 是針對(duì)于BeanFactory的擴(kuò)展點(diǎn)峭判,可以在BeanFactory初始化之后做一些操开缎,這個(gè)時(shí)候Bean已經(jīng)注冊(cè)了,但是Bean還未創(chuàng)建前調(diào)用林螃,一般在這里做修改Bean屬性處理 |
BeanDefinitionRegistryPostProcessor | 是BeanFactoryPostProcessor的子接口奕删,即Spring會(huì)在調(diào)用BeanFactoryPostProcessor之前調(diào)用他。這個(gè)時(shí)候疗认,所有的BeanDefinition已經(jīng)被加載了急侥。一般在這里增加一些Spirng掃描不到的Bean,比如第三方的Bean |
InstantiationAwareBeanPostProcessor | 也是BeanPostProcessor的子接口侮邀,讓我們可以在Bean創(chuàng)建的前后加入自定義的邏輯 |
SmartInstantiationAwareBeanPostProcessor | 智能實(shí)例化Bean后置處理器(繼承InstantiationAwareBeanPostProcessor)。這個(gè)相當(dāng)于InstantiationAwareBeanPostProcessor的擴(kuò)展版本贝润,增加了一個(gè)對(duì)Bean類型預(yù)測(cè)的回調(diào) |
BeanPostProcessor | 讓我們可以在Bean的初始化前后加入自定義的邏輯绊茧。這個(gè)時(shí)候Bean已經(jīng)創(chuàng)建好了 |
InitializingBean | 在Bean所有的屬性都被賦值后調(diào)用,屬性會(huì)在Bean初始化的時(shí)候賦值 |
MergedBeanDefinitionPostProcessor | 在合并處理Bean定義的時(shí)候的回調(diào) |
DestructionAwareBeanPostProcessor | 讓我們可以在Bean銷毀前加入自定義的邏輯 |
2.1 BeanFactoryPostProcessor
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* 這個(gè)方法被調(diào)用的時(shí)候, 所有的Bean已經(jīng)被注冊(cè)了打掘,但是Bean還沒(méi)有被創(chuàng)建出來(lái)华畏。我們一般在這個(gè)里面修改一些Bean的屬性
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
? ? ??BeanFactoryPostProcessor是針對(duì)BeanFactory的擴(kuò)展點(diǎn),是BeanFactory的后處理類尊蚁。BeanFactoryPostProcessor提供了個(gè)postProcessBeanFactory()方法亡笑,這個(gè)方法被調(diào)用的時(shí)候,所有的Bean已經(jīng)被注冊(cè)了(所有的Bean都已經(jīng)轉(zhuǎn)換成BeanDefinitioin了)横朋,但是要記住這個(gè)時(shí)候Bean還沒(méi)有被創(chuàng)建仑乌。也就是說(shuō),通過(guò)它我們可以在初始化任何Bean之前琴锭,做各種操作晰甚,甚至讀取并修改BeanDefinition(Bean定義的元數(shù)據(jù))。特別強(qiáng)調(diào)一點(diǎn)千萬(wàn)不要在這個(gè)方法里面獲取Bean决帖。因?yàn)槲以?jīng)在這個(gè)方法里面獲取Bean厕九,雖然我們能獲取到Bean但是會(huì)導(dǎo)致@PostConstruct失效
BeanFactory,我們可以稍微解釋下地回。BeanFactory的地位相當(dāng)高扁远,它是各種Bean的工廠,它里面提供了一系列的getBean()方法刻像。常用的ApplicationContext就繼承了它畅买。
? ? ??我們用一個(gè)簡(jiǎn)單的實(shí)例來(lái)看下BeanFactoryPostProcessor的使用。找到helloFactoryPostProcessorService名字對(duì)應(yīng)的Bean的定義BeanDefinition细睡,然后修改他的屬性皮获。
/**
* 使用BeanFactoryPostProcessor實(shí)現(xiàn)一個(gè)簡(jiǎn)單的功能,
* 找到helloFactoryPostProcessorService對(duì)應(yīng)的bean修改desc屬性對(duì)應(yīng)的值
*/
@Component
public class CustomizeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("helloFactoryPostProcessorService");
MutablePropertyValues pv = abstractBeanDefinition.getPropertyValues();
pv.addPropertyValue("desc", "hello BeanFactoryPostProcessor");
abstractBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
}
}
2.2 BeanDefinitionRegistryPostProcessor
/**
* 繼承BeanFactoryPostProcessor,BeanFactoryPostProcessor能做的BeanDefinitionRegistryPostProcessor也能做
*/
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* 這個(gè)方法被調(diào)用的時(shí)候, 所有的BeanDefinition已經(jīng)被加載了(定義,注冊(cè)好了), 但是所有的Bean還沒(méi)被創(chuàng)建
* 在這個(gè)方法里面我們可以通過(guò)BeanDefinitionRegistry加入第三方的Bean
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
? ? ? ?BeanDefinitionRegistryPostProcessor接口繼承了BeanFactoryPostProcessor接口。BeanFactoryPostProcessor能做的事情BeanDefinitionRegistryPostProcessor也能做纹冤。額外BeanDefinitionRegistryPostProcessor多了一個(gè)方法postProcessBeanDefinitionRegistry()洒宝,這個(gè)方法會(huì)在Bean注冊(cè)的時(shí)候執(zhí)行(在Bean被定義但還沒(méi)被創(chuàng)建的時(shí)候執(zhí)行)购公。
? ? ? ?BeanDefinitionRegistryPostProcessor的使用場(chǎng)景。我們可以通過(guò)BeanDefinitionRegistryPostProcessor把第三方Bean添加到IOC容器里面去雁歌。比如如下的實(shí)例我們就添加了一個(gè)Bean到IOC容器里面去了宏浩。
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
/**
* 這個(gè)方法被調(diào)用的時(shí)候, 所有的BeanDefinition已經(jīng)被加載了, 但是所有的Bean還沒(méi)被創(chuàng)建
* 定義 --> 實(shí)例化 --> 初始化
* 在Bean被定義但還沒(méi)被實(shí)例化的時(shí)候執(zhí)行。
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 創(chuàng)建一個(gè)Bean然后添加到BeanDefinitionRegistry里面去
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(CustomBean.class).addConstructorArgValue("tuacy").addConstructorArgValue(18);
//設(shè)置屬性值
builder.addPropertyValue("age", 28);
//設(shè)置可通過(guò)@Autowire注解引用
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
//注冊(cè)到BeanDefinitionRegistry
registry.registerBeanDefinition("customBean", builder.getBeanDefinition());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
2.3 InstantiationAwareBeanPostProcessor
* InstantiationAwareBeanPostProcessor繼承BeanPostProcessor
*/
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* Bean實(shí)例化前調(diào)用的
* - 如果postProcessBeforeInstantiation方法返回了Object是null;那么就直接返回靠瞎,調(diào)用doCreateBean方法();
* - 如果postProcessBeforeInstantiation返回不為null;說(shuō)明修改了bean對(duì)象;
* 然后這個(gè)時(shí)候就立馬執(zhí)行postProcessAfterInitialization方法
* (注意這個(gè)是初始化之后的方法,也就是通過(guò)這個(gè)方法實(shí)例化了之后比庄,直接執(zhí)行初始化之后的方法;中間的實(shí)例化之后 和 初始化之前都不執(zhí)行);
*/
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
/**
* 實(shí)例化Bean之后調(diào)用
* 返回值要注意,因?yàn)樗姆祷刂凳菦Q定要不要調(diào)用postProcessPropertyValues方法的其中一個(gè)因素(因?yàn)檫€有一個(gè)因素是mbd.getDependencyCheck())乏盐;
* 如果該方法返回false,并且不需要check佳窑,那么postProcessPropertyValues就會(huì)被忽略不執(zhí)行;如果返回true父能,postProcessPropertyValues就會(huì)被執(zhí)行
*/
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
/**
* 調(diào)用時(shí)機(jī)為postProcessAfterInstantiation執(zhí)行之后并返回true,
* 返回的PropertyValues將作用于給定bean屬性賦值.
* spring 5.1之后出現(xiàn)以替換@Deprecated標(biāo)注的postProcessPropertyValues
*/
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
/**
* 已經(jīng)被標(biāo)注@Deprecated神凑,后續(xù)將會(huì)被postProcessProperties取代
*/
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}
}
? ? ? ?Spring會(huì)在Bean實(shí)例化前后會(huì)調(diào)用InstantiationAwareBeanPostProcessor里面相關(guān)的方法。下面我們對(duì)InstantiationAwareBeanPostProcessor里面的每個(gè)方法做一個(gè)簡(jiǎn)單的介紹:
- InstantiationAwareBeanPostProcessor接口繼承BeanPostProcessor接口何吝,它內(nèi)部提供了3個(gè)方法溉委,再加上BeanPostProcessor接口內(nèi)部的2個(gè)方法,所以實(shí)現(xiàn)這個(gè)接口需要實(shí)現(xiàn)5個(gè)方法爱榕。InstantiationAwareBeanPostProcessor接口的主要作用在于目標(biāo)對(duì)象的實(shí)例化過(guò)程中需要處理自定義邏輯瓣喊,包括實(shí)例化對(duì)象的前后過(guò)程以及實(shí)例的屬性設(shè)置。
- postProcessBeforeInstantiation()方法是最先執(zhí)行的方法黔酥,它在目標(biāo)對(duì)象實(shí)例化之前調(diào)用藻三,該方法的返回值類型是Object,我們可以返回任何類型的值跪者。由于這個(gè)時(shí)候目標(biāo)對(duì)象還未實(shí)例化趴酣,所以這個(gè)返回值可以用來(lái)代替原本該生成的目標(biāo)對(duì)象的實(shí)例(比如返回一個(gè)代理對(duì)象)。如果該方法的返回值代替原本該生成的目標(biāo)對(duì)象坑夯,后續(xù)只有BeanPostProcessor#postProcessAfterInitialization()方法會(huì)調(diào)用岖寞,其它方法不再調(diào)用。
- postProcessAfterInstantiation()方法在目標(biāo)對(duì)象實(shí)例化之后調(diào)用柜蜈,這個(gè)時(shí)候?qū)ο笠呀?jīng)被實(shí)例化仗谆,但是該實(shí)例的屬性還未被設(shè)置,都是null淑履。因?yàn)樗姆祷刂凳菦Q定要不要調(diào)用postProcessPropertyValues()方法隶垮;如果該方法返回false,那么postProcessPropertyValues()就會(huì)被忽略不執(zhí)行秘噪;如果返回true狸吞,postProcessPropertyValues()就會(huì)被執(zhí)行。
- postProcessPropertyValues()方法對(duì)屬性值進(jìn)行修改(這個(gè)時(shí)候?qū)傩灾颠€未被設(shè)置,但是我們可以修改原本該設(shè)置進(jìn)去的屬性值)蹋偏。注意如果postProcessAfterInstantiation()方法返回false便斥,該方法可能不會(huì)被調(diào)用。
- 父接口BeanPostProcessor的2個(gè)方法postProcessBeforeInitialization()和postProcessAfterInitialization()都是在目標(biāo)對(duì)象被實(shí)例化之后威始,并且屬性也被設(shè)置之后調(diào)用的枢纠。
2.4 SmartInstantiationAwareBeanPostProcessor
/**
* SmartInstantiationAwareBeanPostProcessor繼承InstantiationAwareBeanPostProcessor
*/
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
/**
* 預(yù)測(cè)Bean的類型,返回第一個(gè)預(yù)測(cè)成功的Class類型黎棠,如果不能預(yù)測(cè)返回null
*/
@Nullable
default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
/**
* 選擇合適的構(gòu)造器晋渺,比如目標(biāo)對(duì)象有多個(gè)構(gòu)造器,在這里可以進(jìn)行一些定制化脓斩,選擇合適的構(gòu)造器
* beanClass參數(shù)表示目標(biāo)實(shí)例的類型木西,beanName是目標(biāo)實(shí)例在Spring容器中的name
* 返回值是個(gè)構(gòu)造器數(shù)組,如果返回null随静,會(huì)執(zhí)行下一個(gè)PostProcessor的determineCandidateConstructors方法八千;否則選取該P(yáng)ostProcessor選擇的構(gòu)造器
*/
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {
return null;
}
/**
* 獲得提前暴露的bean引用。主要用于解決循環(huán)引用的問(wèn)題,只有單例對(duì)象才會(huì)調(diào)用此方法
*/
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
}
? ? ? ?智能實(shí)例化Bean后置處理器(繼承InstantiationAwareBeanPostProcessor)挪挤。這個(gè)相當(dāng)于InstantiationAwareBeanPostProcessor的擴(kuò)展版本,增加了一個(gè)對(duì)Bean類型預(yù)測(cè)的回調(diào)关翎,這個(gè)接口主要是Spring框架內(nèi)部用的扛门,在實(shí)際中我們一般還是用InstantiationAwareBeanPostProcessor來(lái)實(shí)現(xiàn)我們一些自定義邏輯。
2.5 BeanPostProcessor
public interface BeanPostProcessor {
/**
* Bean創(chuàng)建(實(shí)例化)完成,初始化之前調(diào)用
* 注意需要把Bean返回回去
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* Bean創(chuàng)建(實(shí)例化)完成,初始化之后調(diào)用
* 注意需要把Bean返回回去
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
? ? ? ?BeanPostProcessor可以讓我們?cè)贐ean創(chuàng)建完成之后纵寝,初始化的前后做一些處理论寨。日經(jīng)常可以拓展該接口對(duì)Bean初始化做定制化處理爽茴。
2.6 InitializingBean
public interface InitializingBean {
/**
* 所有屬性被賦值之后調(diào)用.
* 結(jié)合BeanPostProcessor來(lái)說(shuō)就是在postProcessBeforeInitialization()和postProcessAfterInitialization()方法之間執(zhí)行
*/
void afterPropertiesSet() throws Exception;
}
? ? ? ?InitializingBean接口只有一個(gè)方法:afterPropertiesSet()葬凳,當(dāng)一個(gè)Bean實(shí)現(xiàn)InitializingBean接口,afterPropertiesSet()方法在所有的屬性都被賦值后調(diào)用室奏。屬性被賦值是在初始化的時(shí)候做的火焰,與BeanPostProcessor結(jié)合來(lái)看,afterPropertiesSet()方法將在postProcessBeforeInitialization()和postProcessAfterInitialization()之間被調(diào)用執(zhí)行胧沫。
2.7 MergedBeanDefinitionPostProcessor
/**
* MergedBeanDefinitionPostProcessor繼承BeanPostProcessor
*/
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
/**
* 在bean實(shí)例化完畢后調(diào)用 可以用來(lái)修改merged BeanDefinition的一些properties 或者用來(lái)給后續(xù)回調(diào)中緩存一些meta信息使用
* 執(zhí)行Bean定義的合并
* 且在實(shí)例化完Bean之后執(zhí)行
*/
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
/**
* 已重置指定名稱的bean定義的通知昌简,
* 并且此后處理器應(yīng)清除受影響B(tài)ean的任何元數(shù)據(jù)
*/
default void resetBeanDefinition(String beanName) {
}
}
? ? ? ?合并Bean定義后置處理器(繼承BeanPostProcessor)。MergedBeanDefinitionPostProcessor這個(gè)接口绒怨,這個(gè)接口對(duì)@Autowired和@Value的支持起到了至關(guān)重要的作用纯赎。當(dāng)某個(gè)bean在實(shí)例化的時(shí)候就會(huì)調(diào)到所有的實(shí)現(xiàn)了MergedBeanDefinitionPostProcessor接口的實(shí)例。其中就有一個(gè)非常關(guān)鍵的類:AutowiredAnnotationBeanPostProcessor南蹂。大家可以去瞧下AutowiredAnnotationBeanPostProcessor類的具體實(shí)現(xiàn)邏輯犬金。
2.8 DestructionAwareBeanPostProcessor
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
/**
* 實(shí)現(xiàn)銷毀對(duì)象的邏輯
*/
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
/**
* 判斷是否需要處理這個(gè)對(duì)象的銷毀
*/
default boolean requiresDestruction(Object bean) {
return true;
}
}
? ? ? ?處理對(duì)象銷毀的前置回調(diào)散休。具體實(shí)現(xiàn)我們可以參考ApplicationListenerDetector里面實(shí)現(xiàn)邏輯。這個(gè)類是用來(lái)注冊(cè)ApplicationListener實(shí)例的尸执,而如果銷毀一個(gè)對(duì)象新翎,不解除這里的引用會(huì)導(dǎo)致無(wú)法進(jìn)行回收,因此在銷毀對(duì)象時(shí)音同,會(huì)判斷如果是ApplicationListener要執(zhí)行從監(jiān)聽(tīng)器列表中移除掉词爬。
? ? ? ?稍微總結(jié)下IOC各個(gè)類的調(diào)用順序。
- BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()
- BeanFactoryPostProcessor.postProcessBeanFactory()
- InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()該方法在創(chuàng)建對(duì)象之前會(huì)先掉用权均,如果有返回實(shí)例則直接使用不會(huì)去走下面創(chuàng)建對(duì)象的邏輯顿膨,并在之后執(zhí)行BeanPostProcessor.postProcessAfterInitialization()。
- SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors()如果需要的話叽赊,會(huì)在實(shí)例化對(duì)象之前執(zhí)行恋沃。
- MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()在對(duì)象實(shí)例化完畢 初始化之前執(zhí)行。
- InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()在bean創(chuàng)建完畢實(shí)例化之前執(zhí)行必指。
- InstantiationAwareBeanPostProcessor.postProcessPropertyValues()在bean的property屬性注入完畢 向bean中設(shè)置屬性之前執(zhí)行囊咏。
- BeanPostProcessor.postProcessBeforeInitialization()在Bean初始化(自定義init或者是實(shí)現(xiàn)了InitializingBean.afterPropertiesSet())之前執(zhí)行。
- BeanPostProcessor.postProcessAfterInitialization()在bean初始化(自定義init或者是實(shí)現(xiàn)了InitializingBean.afterPropertiesSet())之后執(zhí)行塔橡。
- DestructionAwareBeanPostProcessor.postProcessBeforeDestruction()會(huì)在銷毀對(duì)象前執(zhí)行梅割。
三 需要配合@Import使用的擴(kuò)展類
3.1 ImportBeanDefinitionRegistrar
/**
* ImportBeanDefinitionRegistrar,我們一般會(huì)實(shí)現(xiàn)ImportBeanDefinitionRegistrar類葛家,然后配合一個(gè)自定義的注解一起使用户辞。而且在注解類上@Import我們的這個(gè)實(shí)現(xiàn)類。
* 通過(guò)自定義注解的配置癞谒,拿到注解的一些元數(shù)據(jù)底燎。然后在ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類里面做相應(yīng)的邏輯處理,比如把自定義注解標(biāo)記的類添加到Spring IOC容器里面去弹砚。
*/
public interface ImportBeanDefinitionRegistrar {
/**
* 根據(jù)注解的給定注釋元數(shù)據(jù)双仍,根據(jù)需要注冊(cè)bean定義
* @param importingClassMetadata 可以拿到@Import的這個(gè)class的Annotation Metadata
* @param registry BeanDefinitionRegistry 就可以拿到目前所有注冊(cè)的BeanDefinition,然后可以對(duì)這些BeanDefinition進(jìn)行額外的修改或增強(qiáng)桌吃。
*/
void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
? ? ??ImportBeanDefinitionRegistrar朱沃,常規(guī)手段是實(shí)現(xiàn)ImportBeanDefinitionRegistrar類,然后配合自定義的注解一起使用茅诱。而且在注解類上@Import我們的這個(gè)實(shí)現(xiàn)類为流。通過(guò)自定義注解的配置,拿到注解的一些元數(shù)據(jù)(這個(gè)就相當(dāng)于是參數(shù)了)让簿。然后在ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類里面做相應(yīng)的邏輯處理敬察,比如把自定義注解標(biāo)記的類添加到Spring IOC容器里面去。
3.1.1 借助ImportBeanDefinitionRegistrar實(shí)現(xiàn)Bean的注入
? ? ??把指定包下(通過(guò)BeanIocScan注解指定)添加了BeanIoc注解的類尔当,添加到IOC容器里面去莲祸。
BeanIocScan注解蹂安,注意@Import(BeanIocScannerRegister.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(BeanIocScannerRegister.class)
public @interface BeanIocScan {
String[] basePackages() default "";
}
BeanIoc注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface BeanIoc {
}
BeanIocScannerRegister實(shí)現(xiàn)
public class BeanIocScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private final static String PACKAGE_NAME_KEY = "basePackages";
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//1. 從BeanIocScan主機(jī)獲取到元數(shù)據(jù),指定搜索路徑
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(BeanIocScan.class.getName()));
if (annoAttrs == null || annoAttrs.isEmpty()) {
return;
}
String[] basePackages = (String[]) annoAttrs.get(PACKAGE_NAME_KEY);
// 2. 找到指定路徑下所有添加了BeanIoc注解的類锐帜,添加到IOC容器里面去
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, false);
scanner.setResourceLoader(resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(BeanIoc.class));
scanner.scan(basePackages);
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
? ? ??最后使用的時(shí)候田盈,在啟動(dòng)類上添加@BeanIocScan(basePackages = "com.tuacy.study.springboot.hook.importBeanDefinitionRegistrar.beanioc")就OK。
3.1.2 借助ImportBeanDefinitionRegistrar以及ApplicationRunner實(shí)現(xiàn)Spring Boot啟動(dòng)完成之后,啟動(dòng)我們自定義的一些邏輯
? ? ??我們實(shí)現(xiàn)這樣的一個(gè)小功能,在Spring Boot啟動(dòng)完成之后.我們需要啟動(dòng)添加了RunStart注解,并且實(shí)現(xiàn)了IRunStart接口的類.我們主要分兩步來(lái)實(shí)現(xiàn):
- 通過(guò)ImportBeanDefinitionRegistrar,找到指定包下面添加了RunStart注解,并且實(shí)現(xiàn)了IRunStart接口的類.找到的這些類我們會(huì)保存在RunStartManager單例里面.
- 通過(guò)ApplicationRunner,啟動(dòng)找到的這些類.
RunStartScan注解指定搜索路徑,注意@Import(RunStartScannerRegister.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RunStartScannerRegister.class)
public @interface RunStartScan {
String[] basePackages() default "";
}
ImportBeanDefinitionRegistrar的使用,拿到RunStartScan注解拿到指定搜索路徑.然后去該路徑下找到所有添加了RunStart注解,并且實(shí)現(xiàn)了IRunStart接口的類.保存到單例類里面
public class RunStartScannerRegister implements ImportBeanDefinitionRegistrar {
private final static String PACKAGE_NAME_KEY = "basePackages";
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(RunStartScan.class.getName()));
if (annoAttrs == null || annoAttrs.isEmpty()) {
return;
}
String[] basePackages = (String[]) annoAttrs.get(PACKAGE_NAME_KEY);
RunStartManager.INSTANCE.autoStartScan(basePackages);
}
}
ApplicationRunner啟動(dòng)我們找到的添加了RunStart注解,并且實(shí)現(xiàn)了IRunStart接口的類.
@Component
public class ApplicationRunnerManager implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
RunStartManager.INSTANCE.autoStartInvoke();
}
}
3.2 ImportSelector
public interface ImportSelector {
/**
* 用于指定需要注冊(cè)為bean的Class名稱
* 當(dāng)在@Configuration標(biāo)注的Class上使用@Import引入了一個(gè)ImportSelector實(shí)現(xiàn)類后缴阎,會(huì)把實(shí)現(xiàn)類中返回的Class名稱都定義為bean允瞧。
*
* 通過(guò)其參數(shù)AnnotationMetadata importingClassMetadata可以獲取到@Import標(biāo)注的Class的各種信息,
* 包括其Class名稱蛮拔,實(shí)現(xiàn)的接口名稱述暂、父類名稱、添加的其它注解等信息建炫,通過(guò)這些額外的信息可以輔助我們選擇需要定義為Spring bean的Class名稱
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
? ? ??ImportSelector使用方法和ImportBeanDefinitionRegistrar類似畦韭,也是通過(guò)@Import來(lái)引入生效的。ImportSelector主要作用是收集需要導(dǎo)入的配置類肛跌,如果該接口的實(shí)現(xiàn)類同時(shí)實(shí)現(xiàn)EnvironmentAware艺配,BeanFactoryAware,BeanClassLoaderAware或者ResourceLoaderAware衍慎,那么在調(diào)用其selectImports方法之前先調(diào)用上述接口中對(duì)應(yīng)的方法转唉,如果需要在所有的@Configuration處理完在導(dǎo)入時(shí)可以實(shí)現(xiàn)DeferredImportSelector接口。
? ? ??一個(gè)簡(jiǎn)單的例子,比如如下類稳捆,會(huì)把所有實(shí)現(xiàn)了HelloService接口的類放到IOC容器里面去赠法。
ImportSelector一定要配合@Import使用
@Configuration
@ComponentScan("com.tuacy.study.springboot.hook.importSelector")
@Import(DynamicSelectImport.class)
public class DynamicSelectConfigure {
}
具體的實(shí)現(xiàn)類,首先拿到@ComponentScan指定的路徑眷柔,在指定的路徑下搜索實(shí)現(xiàn)了HelloService接口的類期虾,最終實(shí)現(xiàn)了HelloService接口的類會(huì)被添加到IOC容器里面去原朝。
public class DynamicSelectImport implements ImportSelector {
/**
* DynamicSelectImport需要配合@Import()注解使用
* <p>
* 通過(guò)其參數(shù)AnnotationMetadata importingClassMetadata可以獲取到@Import標(biāo)注的Class的各種信息驯嘱,
* 包括其Class名稱,實(shí)現(xiàn)的接口名稱喳坠、父類名稱鞠评、添加的其它注解等信息,通過(guò)這些額外的信息可以輔助我們選擇需要定義為Spring bean的Class名稱
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String[] basePackages = null;
// @Import注解對(duì)應(yīng)的類上的ComponentScan注解
if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
basePackages = (String[]) annotationAttributes.get("basePackages");
}
if (basePackages == null || basePackages.length == 0) {
//ComponentScan的basePackages默認(rèn)為空數(shù)組
String basePackage = null;
try {
// @Import注解對(duì)應(yīng)的類的包名
basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
basePackages = new String[] {basePackage};
}
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);
scanner.addIncludeFilter(helloServiceFilter);
Set<String> classes = new HashSet<>();
for (String basePackage : basePackages) {
scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
}
return classes.toArray(new String[0]);
}
}
四 SmartLifecycle
/**
* 在使用Spring開(kāi)發(fā)時(shí)壕鹉,我們都知道剃幌,所有bean都交給Spring容器來(lái)統(tǒng)一管理,其中包括沒(méi)一個(gè)bean的加載和初始化晾浴。
* 有時(shí)候我們需要在Spring加載和初始化所有bean后负乡,接著執(zhí)行一些任務(wù)或者啟動(dòng)需要的異步服務(wù),這樣我們可以使用 SmartLifecycle 來(lái)做到
*/
interface SmartLifecycle extends Lifecycle, Phased {
/**
* 根據(jù)該方法的返回值決定是否執(zhí)行start方法脊凰。<br/>
* 返回true時(shí)start方法會(huì)被自動(dòng)執(zhí)行抖棘,返回false則不會(huì)。
*/
default boolean isAutoStartup() {
return true;
}
/**
* 1. 只有該方法返回false時(shí),start方法才會(huì)被執(zhí)行切省。<br/>
* 2. 只有該方法返回true時(shí)最岗,stop(Runnable callback)或stop()方法才會(huì)被執(zhí)行。
*/
@Override
default boolean isRunning() {
return false;
}
/**
* 1. 我們主要在該方法中啟動(dòng)任務(wù)或者其他異步服務(wù)朝捆,比如開(kāi)啟MQ接收消息<br/>
* 2. 當(dāng)上下文被刷新(所有對(duì)象已被實(shí)例化和初始化之后)時(shí)般渡,將調(diào)用該方法,默認(rèn)生命周期處理器將檢查每個(gè)SmartLifecycle對(duì)象的isAutoStartup()方法返回的布爾值芙盘。
* 如果為“true”驯用,則該方法會(huì)被調(diào)用,而不是等待顯式調(diào)用自己的start()方法何陆。
*/
@Override
default void start() {
}
/**
* 接口Lifecycle的子類的方法晨汹,只有非SmartLifecycle的子類才會(huì)執(zhí)行該方法。
* 1. 該方法只對(duì)直接實(shí)現(xiàn)接口Lifecycle的類才起作用贷盲,對(duì)實(shí)現(xiàn)SmartLifecycle接口的類無(wú)效淘这。
* 2. 方法stop()和方法stop(Runnable callback)的區(qū)別只在于,后者是SmartLifecycle子類的專屬巩剖。
* 如果我們使用的是SmartLifecycle改方法忽視掉
*/
@Override
default void stop() {
}
/**
* SmartLifecycle子類的才有的方法铝穷,當(dāng)isRunning方法返回true時(shí),該方法才會(huì)被調(diào)用佳魔。
*/
default void stop(Runnable callback) {
stop();
callback.run();
}
/**
* 如果工程中有多個(gè)實(shí)現(xiàn)接口SmartLifecycle的類曙聂,則這些類的start的執(zhí)行順序按getPhase方法返回值從小到大執(zhí)行。<br/>
* 例如:1比2先執(zhí)行鞠鲜,-1比0先執(zhí)行宁脊。 stop方法的執(zhí)行順序則相反,getPhase返回值較大類的stop方法先被調(diào)用贤姆,小的后被調(diào)用榆苞。
*/
@Override
default int getPhase() {
return DEFAULT_PHASE;
}
}
? ? ??在使用Spring開(kāi)發(fā)時(shí),我們都知道霞捡,所有Bean都交給Spring容器來(lái)統(tǒng)一管理坐漏。有時(shí)候我們需要在Spring加載和初始化所有Bean后,接著執(zhí)行一些任務(wù)或者啟動(dòng)需要的異步服務(wù)碧信,這種情況下我們可以使用SmartLifecycle來(lái)實(shí)現(xiàn)赊琳。
@Component
public class CustomizeLifeCycle implements SmartLifecycle {
private boolean isRunning = false;
/**
* 根據(jù)該方法的返回值決定是否執(zhí)行start方法。
* 返回true時(shí)start方法會(huì)被自動(dòng)執(zhí)行砰碴,返回false則不會(huì)躏筏。
*/
@Override
public boolean isAutoStartup() {
System.out.println("start");
// 默認(rèn)為false
return true;
}
/**
* 1. 只有該方法返回false時(shí),start方法才會(huì)被執(zhí)行呈枉。<br/>
* 2. 只有該方法返回true時(shí)趁尼,stop(Runnable callback)或stop()方法才會(huì)被執(zhí)行檐什。
*/
@Override
public boolean isRunning() {
System.out.println("isRunning");
// 默認(rèn)返回false
return isRunning;
}
/**
* 1. 我們主要在該方法中啟動(dòng)任務(wù)或者其他異步服務(wù),比如開(kāi)啟MQ接收消息<br/>
* 2. 當(dāng)上下文被刷新(所有對(duì)象已被實(shí)例化和初始化之后)時(shí)弱卡,將調(diào)用該方法乃正,默認(rèn)生命周期處理器將檢查每個(gè)SmartLifecycle對(duì)象的isAutoStartup()方法返回的布爾值。
* 如果為“true”婶博,則該方法會(huì)被調(diào)用瓮具,而不是等待顯式調(diào)用自己的start()方法。
*/
@Override
public void start() {
System.out.println("start");
// 執(zhí)行完其他業(yè)務(wù)后凡人,可以修改 isRunning = true
isRunning = true;
}
/**
* 接口Lifecycle的子類的方法名党,只有非SmartLifecycle的子類才會(huì)執(zhí)行該方法。<br/>
* 1. 該方法只對(duì)直接實(shí)現(xiàn)接口Lifecycle的類才起作用挠轴,對(duì)實(shí)現(xiàn)SmartLifecycle接口的類無(wú)效传睹。<br/>
* 2. 方法stop()和方法stop(Runnable callback)的區(qū)別只在于,后者是SmartLifecycle子類的專屬岸晦。
*/
@Override
public void stop() {
System.out.println("stop");
isRunning = false;
}
/**
* SmartLifecycle子類的才有的方法欧啤,當(dāng)isRunning方法返回true時(shí),該方法才會(huì)被調(diào)用启上。
*/
@Override
public void stop(Runnable callback) {
System.out.println("stop(Runnable)");
// 如果你讓isRunning返回true邢隧,需要執(zhí)行stop這個(gè)方法,那么就不要忘記調(diào)用callback.run()冈在。
// 否則在你程序退出時(shí)倒慧,Spring的DefaultLifecycleProcessor會(huì)認(rèn)為你這個(gè)TestSmartLifecycle沒(méi)有stop完成,程序會(huì)一直卡著結(jié)束不了包券,等待一定時(shí)間(默認(rèn)超時(shí)時(shí)間30秒)后才會(huì)自動(dòng)結(jié)束纫谅。
callback.run();
isRunning = false;
}
/**
* 如果工程中有多個(gè)實(shí)現(xiàn)接口SmartLifecycle的類,則這些類的start的執(zhí)行順序按getPhase方法返回值從小到大執(zhí)行溅固。<br/>
* 例如:1比2先執(zhí)行付秕,-1比0先執(zhí)行。 stop方法的執(zhí)行順序則相反发魄,getPhase返回值較大類的stop方法先被調(diào)用盹牧,小的后被調(diào)用俩垃。
*/
@Override
public int getPhase() {
return 0;
}
}
五 ApplicationListener
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 處理應(yīng)用程序事件励幼。
*/
void onApplicationEvent(E event);
}
? ? ??ApplicationListener是SpringBoot的監(jiān)聽(tīng)器。為Bean和Bean之間的消息通信提供了支持口柳。當(dāng)Bean處理完一個(gè)事件之后苹粟,希望另一個(gè)Bean能夠知道并做相應(yīng)的處理。這時(shí)另一個(gè)Bean監(jiān)聽(tīng)當(dāng)前Bean所發(fā)送的事件,就需要用到ApplicationListener跃闹。
? ? ??Spring事件分為事件發(fā)布者(EventPublisher)嵌削、事件監(jiān)聽(tīng)者(EventListener),還包括一個(gè)事件廣播者(這個(gè)是Spring實(shí)現(xiàn)相關(guān))毛好。
? ? ??Spring事件分為兩類:一個(gè)是Spring自帶的一些事件,另一個(gè)是我們自定義的事件.
5.1 Spring自帶的事件
Spring一些常用事件 | 解釋 |
---|---|
ApplicationStartedEvent | spring boot啟動(dòng)開(kāi)始時(shí)執(zhí)行的事件 |
ApplicationEnvironmentPreparedEvent | spring boot 對(duì)應(yīng)Enviroment已經(jīng)準(zhǔn)備完畢,但此時(shí)上下文context還沒(méi)有創(chuàng)建 |
ApplicationPreparedEvent | spring boot上下文context創(chuàng)建完成苛秕,但此時(shí)spring中的bean是沒(méi)有完全加載完成的 |
ApplicationReadyEvent | 上下文已經(jīng)準(zhǔn)備完畢的時(shí)候觸發(fā) |
ApplicationFailedEvent | spring boot啟動(dòng)異常時(shí)執(zhí)行事件 |
ContextRefreshedEvent | 當(dāng)ApplicationContext被初始化或刷新的時(shí)候會(huì)觸發(fā) |
5.2 自定義事件
? ? ??自定義Spring事件需要繼承ApplicationEvent肌访。
我們自定義一個(gè)事件CustomerEvent,繼承ApplicationEvent
public class CustomerEvent extends ApplicationEvent {
/**
* 事件內(nèi)容
*/
private String content;
public CustomerEvent(Object source, String content) {
super(source);
this.content = content;
}
public String getContent() {
return content;
}
}
在Bean里面發(fā)送事件,先拿到ApplicationContext對(duì)象,在把消息發(fā)送出去.
applicationContext.publishEvent(new CustomerEvent(this, "CommandLineRunner"));
接受事件,兩種方式:一種實(shí)現(xiàn)ApplicationListener<CustomerEvent>接口,另一種是使用@EventListener注解監(jiān)聽(tīng)事件.
Bean實(shí)現(xiàn)ApplicationListener接口,接聽(tīng)到我們發(fā)送過(guò)來(lái)的CustomerEvent事件
@Component
public class CustomerApplicationListener implements ApplicationListener<CustomerEvent> {
@Override
public void onApplicationEvent(CustomerEvent event) {
System.out.println(event.getSource());
}
}
Bean里面通過(guò)@EventListener注解監(jiān)聽(tīng)到我們發(fā)送過(guò)來(lái)的CustomerEvent事件
@Component
public class AnnotationListener {
/**
* 通過(guò)@EventListener組件監(jiān)聽(tīng)到我們發(fā)送的CustomerEvent事件
*/
@EventListener
public void customerEvent(CustomerEvent event) {
System.out.println("收到是消息:" + event.getSource());
}
}
六 CommandLineRunner、ApplicationRunner
? ? ? ?在使用SpringBoot構(gòu)建項(xiàng)目時(shí)艇劫,我們通常有一些預(yù)先數(shù)據(jù)的加載(比如:讀取配置文件信息吼驶,數(shù)據(jù)庫(kù)連接,刪除臨時(shí)文件店煞,清除緩存信息)蟹演。Spring Boot中我們可以通過(guò)CommandLineRunner、ApplicationRunner兩個(gè)接口實(shí)現(xiàn)這一要求顷蟀,我們需要時(shí)酒请,只需實(shí)現(xiàn)該接口就行。如果存在多個(gè)加載的數(shù)據(jù)鸣个,我們也可以使用@Order注解或者在實(shí)現(xiàn)類上實(shí)現(xiàn)Ordered來(lái)標(biāo)識(shí)來(lái)排序(數(shù)字越小優(yōu)先級(jí)越高羞反,越先執(zhí)行)。它們的執(zhí)行時(shí)機(jī)是容器啟動(dòng)完成的時(shí)候囤萤。
? ? ? ?他兩唯一的不同點(diǎn)在于ApllicationRunner中run方法的參數(shù)為ApplicationArguments苟弛,而CommandLineRunner接口的run方法的參數(shù)為String數(shù)組。其實(shí)對(duì)我們使用來(lái)說(shuō)這兩個(gè)接口間沒(méi)有很大的區(qū)別,咱們隨便用一個(gè)就行阁将,如果想要更詳細(xì)地獲取命令行參數(shù)膏秫,那就使用ApplicationRunner接口。
ApplicationRunner優(yōu)先于CommandLineRunner執(zhí)行做盅。
七 ClassPathBeanDefinitionScanner
? ? ??ClassPathBeanDefinitionScanner(類掃描器分析)作用就是將指定包下的類通過(guò)一定規(guī)則過(guò)濾后將Class信息包裝成BeanDefinition的形式注冊(cè)到IOC容器中缤削。
? ? ??舉一個(gè)例子,比如如下的代碼配合ImportBeanDefinitionRegistrar的使用,我們會(huì)去找添加了BeanIoc注解的類吹榴,并且他找到的類添加到IOC容器里面去亭敢。
public class BeanIocScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private final static String PACKAGE_NAME_KEY = "basePackages";
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(BeanIocScan.class.getName()));
if (annoAttrs == null || annoAttrs.isEmpty()) {
return;
}
// 搜索路徑
String[] basePackages = (String[]) annoAttrs.get(PACKAGE_NAME_KEY);
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, false);
scanner.setResourceLoader(resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(BeanIoc.class));
scanner.scan(basePackages);
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
更加詳細(xì)的使用可以看看上文關(guān)于ImportBeanDefinitionRegistrar的使用
八 ClassPathScanningCandidateComponentProvider
? ? ??ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner的基類,ClassPathScanningCandidateComponentProvider本身主要作用是包掃描,就是根據(jù)指定的規(guī)則掃描到指定的類.
ClassPathScanningCandidateComponentProvider和ClassPathBeanDefinitionScanner的區(qū)別在于ClassPathBeanDefinitionScanner會(huì)在根據(jù)指定的規(guī)則掃描到類之后,把掃描到的類添加到IOC容器里面去.
? ? ??一個(gè)簡(jiǎn)單的例子,比如搜索指定包(com.tuacy.study.springboot.hook.importSelector)下,實(shí)現(xiàn)了HelloService接口的類图筹。
/**
* 搜索指定包(com.tuacy.study.springboot.hook.importSelector)下,實(shí)現(xiàn)了HelloService接口的類
*/
@Test
public void test() {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);
scanner.addIncludeFilter(helloServiceFilter);
Set<BeanDefinition> classes = scanner.findCandidateComponents("com.tuacy.study.springboot.hook.importSelector");
if (!classes.isEmpty()) {
classes.forEach(beanDefinition -> System.out.println(beanDefinition.getBeanClassName()));
}
}
? ? ??關(guān)于Spring Boot基礎(chǔ)用法相關(guān)的一些類,我們就介紹到這里.在實(shí)際開(kāi)發(fā)中,這些類也特別有用.文章中涉及到所有實(shí)例可以在 https://github.com/tuacy/java-study springboot文件夾下面找到.