Springboot @Enable*注解的工作原理

先看一個(gè)簡(jiǎn)單的demo,我們定義一個(gè)springboot項(xiàng)目电禀,最簡(jiǎn)單的依賴:

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
</parent>

<dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
     </dependency>
</dependencies>

定義一個(gè)實(shí)體類:ServerBean

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "instance")
public class ServerBean {

    private String ip;

    private Integer port;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return "ServerBean{" +
                "ip='" + ip + '\'' +
                ", port=" + port +
                '}';
    }
}

之前的博客springboot配置詳解,詳細(xì)的講解了@ConfigurationProperties的使用拍柒。

配置文件配置:

instance.ip=192.168.1.111
instance.port=8090

啟動(dòng)類啟動(dòng)灯节,

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        System.out.println(context.getBean(ServerBean.class));
    }
}

打又印:

ServerBean{ip='192.168.1.111', port=8090}

我們發(fā)現(xiàn)這個(gè)程序自動(dòng)的會(huì)把配置文件注入到bean中史隆,原因在哪里呢魂务?

之前我們講過啟動(dòng)類上的注解@SpringBootApplication是一個(gè)復(fù)合注解,其由@SpringBootConfiguration泌射,@EnableAutoConfiguration粘姜,@ComponentScan三個(gè)注解組成,@SpringBootConfiguration和@ComponentScan我們?cè)?br> springboot快速入門及@SpringBootApplication注解分析
分析過了魄幕,那么故名思義相艇,我們知道肯定是@EnableAutoConfiguration注解的作用了。

修改啟動(dòng)類纯陨,

@EnableAutoConfiguration
@ComponentScan
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        System.out.println(context.getBean(ServerBean.class));
    }
}

發(fā)現(xiàn)打印結(jié)果還是一樣坛芽。然后再去修改一下啟動(dòng)類留储,使用@EnableConfigurationProperties也可以替換@EnableAutoConfiguration

@EnableConfigurationProperties
@ComponentScan
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        System.out.println(context.getBean(ServerBean.class));
    }
}

@EnableConfigurationProperties注解一般和ConfigurationProperties注解搭配使用,可以將配置文件屬性注入到bean中咙轩。

第二個(gè)demo

定義一個(gè)類UserRunnable获讳,納入到spring容器中,

package com.zhihao.miao.enable.bean;


import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class UserRunnable implements Runnable{

    @Override
    public void run() {
        try{
            for (int i = 0; i <10 ; i++) {
                System.out.println("============"+i);
                TimeUnit.SECONDS.sleep(1);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

啟動(dòng)類:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        UserRunnable userRunnable = context.getBean(UserRunnable.class);
        userRunnable.run();
        System.out.println("end");
    }
}

打印結(jié)果:

============0
============1
============2
============3
============4
============5
============6
============7
============8
============9
end

我們發(fā)現(xiàn)執(zhí)行過程是一個(gè)同步的過程活喊,只有userRunnable.run方法執(zhí)行完畢之后才執(zhí)行下面的打印過程丐膝。

修改一下代碼,將UserRunnable的run方法上加入@Async注解

@Component
public class UserRunnable implements Runnable{

    @Override
    @Async
    public void run() {
        try{
            for (int i = 0; i <10 ; i++) {
                System.out.println("============"+i);
                TimeUnit.SECONDS.sleep(1);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

修改啟動(dòng)類钾菊,

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        context.getBean(Runnable.class).run();
        System.out.println("end");
    }
}

執(zhí)行結(jié)果:

============0
end
============1
============2
============3
============4
============5
============6
============7
============8
============9

我們查看上面二個(gè)demo的Enable*注解的源碼帅矗,

@EnableAsync注解

@EnableAsync
AsyncConfigurationSelector注解

最后實(shí)現(xiàn)ImportSelector接口

@EnableAutoConfiguration注解

都使用到了@Import注解,最后也是實(shí)現(xiàn)ImportSelector接口煞烫。

@Import注解

@Import注解

@Import其實(shí)就是引入一個(gè)或多個(gè)配置浑此,可以導(dǎo)入普通類,也可以導(dǎo)入配置類滞详。
@Import用來導(dǎo)入一個(gè)或多個(gè)類(會(huì)被spring容器管理)凛俱,或者配置類(配置類里的@Bean標(biāo)記的類也會(huì)被spring容器管理)

看一個(gè)demo,定義四個(gè)實(shí)體類料饥,User蒲犬,People,Cat岸啡,Dog

public class User {
}
public class People{  
}
public class Cat {
}
public class Dog {
}
public class MyConfig {

    @Bean
    public Dog dog(){
        return new Dog();
    }

    @Bean
    public Cat cat(){
        return new Cat();
    }
}

我們要將這四個(gè)類納入到spring容器中原叮,我們之前的做法是在User,People上加上了@Component注解(或者@Service凰狞,@Controller)或者在MyConfig類上加上@Configuration注解篇裁。很顯然我們這邊并沒有這般做,使用@Import注解也可以加對(duì)象納入到spring容器中赡若。

啟動(dòng)類:

package com.zhihao.miao.imp;

import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

@Import({User.class,People.class, MyConfig.class})
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }
}

ImportSelector接口

ImportSelector接口

Interface to be implemented by types that determine which @{@link Configuration}
class(es) should be imported based on a given selection criteria, usually one or more
annotation attributes.
接口被實(shí)現(xiàn)那些Configuration的類被導(dǎo)入到spring容器根據(jù)指定的一些條件达布,通常是一個(gè)到多個(gè)導(dǎo)入類的注解屬性。

An ImportSelector may implement any of the following
org.springframework.beans.factory.Aware Aware interfaces, and their respective methods will be called prior to selectImports:

  • org.springframework.context.EnvironmentAware EnvironmentAware
  • org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware
  • org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware
  • org.springframework.context.ResourceLoaderAware ResourceLoaderAware

ImportSelectors are usually processed in the same way as regular @Import
annotations, however, it is also possible to defer selection of imports until all
@Configuration classes have been processed (see DeferredImportSelector
for details).
實(shí)現(xiàn)ImportSelectors接口的類通常與常規(guī)的@Import注解作用相同逾冬,然而黍聂,它也可能被延遲處理直到所有被@Configuration標(biāo)記的類處理完之后采取處理。

定義一個(gè)MyImportSelector繼承ImportSelector身腻,ImportSelector返回的String[]數(shù)組是類的全類名會(huì)被納入到spring容器內(nèi)产还。

/**
 * selectImports方法的返回值,必須是一個(gè)class(全稱)嘀趟,該class會(huì)被spring容器所托管起來
 */
public class MyImportSelector implements ImportSelector{

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //獲取注解的屬性信息
        System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
       //這里可以獲取到注解的詳細(xì)信息脐区,然后根據(jù)信息去動(dòng)態(tài)的返回需要被spring容器管理的bean
        return new String[]{"com.zhihao.miao.imp.bean.User",People.class.getName(),MyConfig.class.getName()};
    }

}

定義一個(gè)EnableLog注解,可以得到屬性的值她按,@Import(MyImportSelector.class)牛隅,可以在MyImportSelector中獲取name屬性值炕柔。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportSelector.class)
public @interface EnableLog {
    String name();
}

啟動(dòng)類,在啟動(dòng)類上加入@EnableLog(name="com.zhihao.miao")注解媒佣,
我們知道@EnableLog中@Import(MyImportSelector.class)會(huì)將MyImportSelector對(duì)象納入到容器中匕累。

package com.zhihao.miao.imp;
import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

@EnableLog(name="com.zhihao.miao")
public class Application2 {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application2.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }

}

打印結(jié)果:

{name=[com.zhihao.miao]}
2017-07-20 11:27:08.446  INFO 11551 --- [           main] com.zhihao.miao.imp.Application2         : Started Application2 in 11.754 seconds (JVM running for 12.614)
com.zhihao.miao.imp.bean.User@1e4d3ce5
com.zhihao.miao.imp.bean.Dog@3ddc6915
com.zhihao.miao.imp.bean.Cat@704deff2
com.zhihao.miao.imp.bean.People@379614be

ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar接口

Interface to be implemented by types that register additional bean definitions when processing @{@link Configuration} classes. Useful when operating at the bean definition level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
接口實(shí)現(xiàn)可以額外的注冊(cè)類的定義到spring容器中。

Along with {@code @Configuration} and {@link ImportSelector}, classes of this type may be provided to the @{@link Import} annotation (or may also be returned from an {@code ImportSelector}).

An {@link ImportBeanDefinitionRegistrar} may implement any of the following
{@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
methods will be called prior to {@link #registerBeanDefinitions}:

{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}

See implementations and associated unit tests for usage examples.

定義MyImportBeanDefinitionRegistrar實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口默伍,將User類欢嘿,People類,Myconfig中的Dog和Cat類注入到spring容器中

package com.zhihao.miao.imp.bean;


import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * registerBeanDefinitions方法的參數(shù)有一個(gè)BeanDefinitionRegistry也糊,
 * BeanDefinitionRegistry可以用來往spring容器中注入bean
 * 如此炼蹦,我們就可以在registerBeanDefinitions方法里面動(dòng)態(tài)的注入bean
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(People.class);
        registry.registerBeanDefinition(People.class.getName(),bdb.getBeanDefinition());

        BeanDefinitionBuilder bdb2 = BeanDefinitionBuilder.rootBeanDefinition(User.class);
        registry.registerBeanDefinition(User.class.getName(),bdb2.getBeanDefinition());

        BeanDefinitionBuilder bdb3 = BeanDefinitionBuilder.rootBeanDefinition(MyConfig.class);
        registry.registerBeanDefinition(MyConfig.class.getName(),bdb3.getBeanDefinition());
    }
}

主啟動(dòng)類:

package com.zhihao.miao.imp;

import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

@Import(MyImportBeanDefinitionRegistrar.class)
public class Application3 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application3.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }
}

打印結(jié)果

...
com.zhihao.miao.imp.bean.User@3e694b3f
com.zhihao.miao.imp.bean.Dog@1bb5a082
com.zhihao.miao.imp.bean.Cat@78691363
com.zhihao.miao.imp.bean.People@41d477ed
...

當(dāng)然也可以寫成一個(gè)注解,@EnableImportConfig

package com.zhihao.miao.imp.bean;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableImportConfig {
}

啟動(dòng)類:

package com.zhihao.miao.imp;

import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

@EnableImportConfig
public class Application4 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application4.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Dog.class));
        System.out.println(context.getBean(Cat.class));
        System.out.println(context.getBean(People.class));
    }
}

也是可以將這些對(duì)象注入到spring容器的显设。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末框弛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捕捂,更是在濱河造成了極大的恐慌,老刑警劉巖斗搞,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件指攒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡僻焚,警方通過查閱死者的電腦和手機(jī)允悦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虑啤,“玉大人隙弛,你說我怎么就攤上這事∧剑” “怎么了全闷?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萍启。 經(jīng)常有香客問我总珠,道長(zhǎng),這世上最難降的妖魔是什么勘纯? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任局服,我火速辦了婚禮,結(jié)果婚禮上驳遵,老公的妹妹穿的比我還像新娘淫奔。我一直安慰自己,他們只是感情好堤结,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布唆迁。 她就那樣靜靜地躺著佳鳖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪媒惕。 梳的紋絲不亂的頭發(fā)上系吩,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音妒蔚,去河邊找鬼穿挨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肴盏,可吹牛的內(nèi)容都是我干的科盛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼菜皂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼贞绵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恍飘,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤榨崩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后章母,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體母蛛,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年乳怎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彩郊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚪缀,死狀恐怖秫逝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情询枚,我是刑警寧澤违帆,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站哩盲,受9級(jí)特大地震影響前方,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜廉油,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一惠险、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抒线,春花似錦班巩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逊桦。三九已至,卻和暖如春抑进,著一層夾襖步出監(jiān)牢的瞬間强经,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工寺渗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匿情,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓信殊,卻偏偏與公主長(zhǎng)得像炬称,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涡拘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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