Spring源碼(九)-SpringBoot中的注解

前言

上一篇寫了Spring相關(guān)的注解,由于我的源碼分析主要采用的是Springboot的方式醋虏,所以這里也順便把springboot相關(guān)的注解也進行一些簡單的分析寻咒。

1、配置類

Springboot比Spring之前的xml那種配置方式比較優(yōu)秀的方式颈嚼,我覺得最大就在于毛秘,減少了大量的配置文件,提供了很多spring-boot-starter-*的包,提供這些開箱即用的方式叫挟。當(dāng)然配置也改用這種注解式的艰匙。

1.1、@SpringBootApplication

Springboot最核心的包抹恳,配置在啟動類上员凝,main方法作為server的啟動入口,先看下他的內(nèi)部實現(xiàn)奋献。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
················
}

從源碼中可以看到該注解就是@Configuration健霹,@EnableAutoConfiguration和@ComponentScan這三個注解的集合,優(yōu)化使用秽荞,那么接下來延伸解析下這三個注解骤公。

1.2、@Configuration

先看源碼

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

Spring是給予IOC的扬跋,在4.0之前的版本阶捆,通常都是程序依賴上下文xml文件來管理bean,盡管有了掃描配置后相對簡單钦听,然而java配置的方式不同于xml洒试,通過注解能夠更簡單。下面我們通過這兩種方式比較下朴上。

  • xml中bean的定義
<beans>
    <bean id="course" class="demo.Course">
        <property name="module" ref="module"/>
    </bean>
    <bean id="module" class="demo.Module">
        <property name="assignment" ref="assignment"/>
    </bean>
    <bean id="assignment" class="demo.Assignment" />
</beans>

  • 注解配置類
@Configuration
public class AppContext {
    @Bean
    public Course course() {
        Course course = new Course();
        course.setModule(module());
        return course;
    }
    @Bean
    public Module module() {
        Module module = new Module();
        module.setAssignment(assignment());
        return module;
    }
    @Bean
    public Assignment assignment() {
        return new Assignment();
    }
}

@Configuration垒棋,該注解配置在類上,告知Spring這個類是一個擁有bean定義和依賴項的配置類痪宰。@Bean注釋用于定義Bean叼架,該注解位于實例化bean并設(shè)置依賴項的方法上。方法名默認通beanId活默認名稱相同衣撬,該方法返回類型是Spring注冊的bean乖订。總體來說就是告訴Spring容器加載這個配置具练,相對于xml乍构,這個注解就是將*.xml配置進web.xml

1.3、@EnableAutoConfiguration

  • spring-boot 定義的注解扛点,屬于包 org.springframework.boot.autoconfigure哥遮,先看源代碼:
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    String[] excludeName() default {};
}

自己動手寫注解,請參考動手寫注解
這個注解告訴Spring Boot 根據(jù)添加的jar依賴猜測你想如何配置Spring陵究。比如spring-boot-starter-web添加了Tomcat和Spring MVC眠饮,所以auto-configuration將假定你正在開發(fā)一個web應(yīng)用并相應(yīng)地對Spring進行設(shè)置。

1.4铜邮、@ComponentScan

先看源碼

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};
    @AliasFor("value")
    String[] basePackages() default {};
    //還有一些屬性并不常用君仆,所以不列舉了。。返咱。。牍鞠。咖摹。
}

  • @ComponentScan 注解的作用就是開啟spring的注解掃描,與xml配置方式下的 作用一樣难述。
    可以設(shè)置一個值指定basePackages萤晴,就是開始掃描的包。如果沒有設(shè)置 默認從定義這個注解的類所屬包開始一直到所有子包胁后。

1.5店读、@Import

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}
  • @Import 與xml配置方式下的 作用一樣。支持導(dǎo)入的類型有:
    一個或多個擁有 @Configuration 注解的配置類
  • ImportSelector 接口的實現(xiàn)類
  • ImportBeanDefinitionRegistrar 的實現(xiàn)類

1)攀芯、如果Import注解中Class為ImportSelector子類屯断,通過invokeAwareMethods(selector)設(shè)置aware值,如果類型為DeferredImportSelector則添加到deferredImportSelectors集合中侣诺,待前面的parser.parse(configCandidates)
方法中processDeferredImportSelectors()處理殖演;如果不是,則執(zhí)行selectImports方法年鸳,將獲取到的結(jié)果遞歸調(diào)用processImports趴久,解析selectImports得到的結(jié)果

2)、如果Import注解中Class為ImportBeanDefinitionRegistrar子類搔确,則添加到importBeanDefinitionRegistrars中彼棍,注意該部分的數(shù)據(jù)在執(zhí)行完parser.parse(configCandidates)后調(diào)用this.reader.loadBeanDefinitions(configClasses)解析,否則執(zhí)行配置信息的解析操作膳算。

1.6座硕、@Conditional

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
    Class<? extends Condition>[] value();
}

可以看出value的值必須是實現(xiàn)了Condition接口的類,Condition接口定義如下:

public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

matchs()返回true 表明該bean(注解了@Conditional的類)需要被創(chuàng)建畦幢,否則不創(chuàng)建坎吻。
延伸幾個 spring-boot提供的 @Conditional的子集,具體了Conditional的條件:

  • @ConditionalOnClass: 等同于 @Conditional(OnClassCondition.class)宇葱,表示存在對應(yīng)的Class文件時才會去創(chuàng)建該bean
  • @ConditionalOnMissingBean: 等同于 @Conditional(OnBeanCondition.class)瘦真,表示spring上下文里缺失某個bean時才會去創(chuàng)建該bean
  • @ConditionalOnWebApplication: 等同于 - @Conditional(OnWebApplicationCondition.class),表示只有在WEB應(yīng)用時才會創(chuàng)建該bean
    更多請參考 org.springframework.boot.autoconfigure.condition 包下面的類

1.7黍瞧、@EnableConfigurationProperties , @ConfigurationProperties

使用@Value(“${property}”)注解注入配置屬性有時可能比較笨重诸尽,特別是需要使用多個properties或你的數(shù)據(jù)本身有層次結(jié)構(gòu)。為了控制和校驗?zāi)愕膽?yīng)用配置印颤,Spring Boot提供一個允許強類型beans的替代方法來使用properties您机。

@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings {
    private String username;
    private InetAddress remoteAddress;
}

當(dāng)@EnableConfigurationProperties注解應(yīng)用到你的@Configuration時,任何被@ConfigurationProperties注解的beans將自動被Environment屬性配置。這種風(fēng)格的配置特別適合與SpringApplication的外部YAML配置進行配合使用际看。

# application.yml
connection:
    username: admin
    remoteAddress: 192.168.1.1
# additional configuration as required

為了使用@ConfigurationProperties beans咸产,你可以使用與其他任何bean相同的方式注入它們。

@Service
public class MyService {
    @Autowired
    private ConnectionSettings connection;
    @PostConstruct
    public void openConnection() {
        Server server = new Server();
        this.connection.configure(server);
    }
}


//@Component 這樣ConnectionSettings類上面就不用標(biāo)示 @Component注解了
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings {
    private String username;
    private InetAddress remoteAddress;
    // ... getters and setters
}

2仲闽、自己動手寫注解

先動手寫注解

2.1脑溢、注解類編寫

@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface UserDoc {
    String author() default "Sunliangliang";
    int revision() default 1;
    String comments() default "written";
}

  • 注解方法不能有參數(shù)。
  • 注解方法的返回類型局限于原始類型赖欣,字符串屑彻,枚舉,注解顶吮,或以上類型構(gòu)成的數(shù)組社牲。
  • 注解方法可以包含默認值。
  • 注解可以包含與其綁定的元注解悴了,元注解為注解提供信息搏恤,有四種元注解類型:
  • 該注解在運行時使用,只能應(yīng)用于方法上

2.2让禀、注解的使用

public class AnnotationExample {
    public static void main(String[] args) {
    }

    @UserDoc()
    public static void oldMethod() {
        System.out.println("old method, don't use it.");
    }

}

2.3挑社、Java注解解析

我們將使用Java反射機制從一個類中解析注解,請記住巡揍,注解保持性策略應(yīng)該是RUNTIME痛阻,否則它的信息在運行期無效,我們也不能從中獲取任何數(shù)據(jù)腮敌。

public class AnnotationParsing {

    public static void main(String[] args) {
        try {
            for (Method method : AnnotationParsing.class
                    .getClassLoader()
                    .loadClass(("com.liangliang.demo.AnnotationExample"))
                    .getMethods()) {
                if (method
                        .isAnnotationPresent(UserDoc.class)) {
                    try {
                        // iterates all the annotations available in the method
                        for (Annotation anno : method.getDeclaredAnnotations()) {
                            System.out.println("Annotation in Method :'"
                                    + method + "' : " + anno);
                        }
                        UserDoc methodAnno = method
                                .getAnnotation(UserDoc.class);
                        if (methodAnno.revision() == 1) {
                            System.out.println("Method with revision no 1 = "
                                    + method);
                        }

                    } catch (Throwable ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } catch (SecurityException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

以上程序的輸出結(jié)果是:

Annotation in Method 'public static void com.liangliang.demo.AnnotationExample.oldMethod()' : @com.liangliang.annotations.UserDoc(author=Sunliangliang, revision=1, comments=written)
Method with revision no 1 = public static void com.liangliang.demo.AnnotationExample.oldMethod()

注解API非常強大阱当,被廣泛應(yīng)用于各種Java框架,如Spring糜工,Hibernate弊添,JUnit“颇荆可以查看《Java中的反射》獲得更多信息油坝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刨裆,隨后出現(xiàn)的幾起案子澈圈,更是在濱河造成了極大的恐慌,老刑警劉巖帆啃,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬女,死亡現(xiàn)場離奇詭異,居然都是意外死亡努潘,警方通過查閱死者的電腦和手機诽偷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門坤学,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人报慕,你說我怎么就攤上這事深浮。” “怎么了卖子?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵略号,是天一觀的道長。 經(jīng)常有香客問我洋闽,道長,這世上最難降的妖魔是什么突梦? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任诫舅,我火速辦了婚禮,結(jié)果婚禮上宫患,老公的妹妹穿的比我還像新娘刊懈。我一直安慰自己,他們只是感情好娃闲,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布虚汛。 她就那樣靜靜地躺著,像睡著了一般皇帮。 火紅的嫁衣襯著肌膚如雪卷哩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天属拾,我揣著相機與錄音将谊,去河邊找鬼。 笑死渐白,一個胖子當(dāng)著我的面吹牛尊浓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纯衍,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼栋齿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了襟诸?” 一聲冷哼從身側(cè)響起瓦堵,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎励堡,沒想到半個月后谷丸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡应结,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年刨疼,在試婚紗的時候發(fā)現(xiàn)自己被綠了泉唁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡揩慕,死狀恐怖亭畜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情迎卤,我是刑警寧澤拴鸵,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站蜗搔,受9級特大地震影響劲藐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜樟凄,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一聘芜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缝龄,春花似錦汰现、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炼绘,卻和暖如春嗅战,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饭望。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工仗哨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铅辞。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓厌漂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斟珊。 傳聞我的和親對象是個殘疾皇子苇倡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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