Spring Boot基礎(chǔ)用法

? ? ? ?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)用順序。

  1. BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()
  2. BeanFactoryPostProcessor.postProcessBeanFactory()
  3. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()該方法在創(chuàng)建對(duì)象之前會(huì)先掉用权均,如果有返回實(shí)例則直接使用不會(huì)去走下面創(chuàng)建對(duì)象的邏輯顿膨,并在之后執(zhí)行BeanPostProcessor.postProcessAfterInitialization()。
  4. SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors()如果需要的話叽赊,會(huì)在實(shí)例化對(duì)象之前執(zhí)行恋沃。
  5. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()在對(duì)象實(shí)例化完畢 初始化之前執(zhí)行。
  6. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()在bean創(chuàng)建完畢實(shí)例化之前執(zhí)行必指。
  7. InstantiationAwareBeanPostProcessor.postProcessPropertyValues()在bean的property屬性注入完畢 向bean中設(shè)置屬性之前執(zhí)行囊咏。
  8. BeanPostProcessor.postProcessBeforeInitialization()在Bean初始化(自定義init或者是實(shí)現(xiàn)了InitializingBean.afterPropertiesSet())之前執(zhí)行。
  9. BeanPostProcessor.postProcessAfterInitialization()在bean初始化(自定義init或者是實(shí)現(xiàn)了InitializingBean.afterPropertiesSet())之后執(zhí)行塔橡。
  10. 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文件夾下面找到.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帅刀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子远剩,更是在濱河造成了極大的恐慌扣溺,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓜晤,死亡現(xiàn)場(chǎng)離奇詭異锥余,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)痢掠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門驱犹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嘲恍,“玉大人,你說(shuō)我怎么就攤上這事雄驹〉枧#” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵医舆,是天一觀的道長(zhǎng)吁脱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)彬向,這世上最難降的妖魔是什么兼贡? 我笑而不...
    開(kāi)封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮娃胆,結(jié)果婚禮上遍希,老公的妹妹穿的比我還像新娘。我一直安慰自己里烦,他們只是感情好凿蒜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著胁黑,像睡著了一般废封。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丧蘸,一...
    開(kāi)封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天漂洋,我揣著相機(jī)與錄音,去河邊找鬼力喷。 笑死刽漂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弟孟。 我是一名探鬼主播贝咙,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拂募!你這毒婦竟也來(lái)了庭猩?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤陈症,失蹤者是張志新(化名)和其女友劉穎蔼水,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體爬凑,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡徙缴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年试伙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘁信。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片于样。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖潘靖,靈堂內(nèi)的尸體忽然破棺而出穿剖,到底是詐尸還是另有隱情,我是刑警寧澤卦溢,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布糊余,位于F島的核電站,受9級(jí)特大地震影響单寂,放射性物質(zhì)發(fā)生泄漏贬芥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一宣决、第九天 我趴在偏房一處隱蔽的房頂上張望蘸劈。 院中可真熱鬧,春花似錦尊沸、人聲如沸威沫。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棒掠。三九已至,卻和暖如春屁商,著一層夾襖步出監(jiān)牢的瞬間烟很,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工蜡镶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溯职,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓帽哑,卻偏偏與公主長(zhǎng)得像谜酒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妻枕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容