被各種注解搞暈了?那快來看看Spring Bean注解詳解冈止!

前言

本篇博客中狂票,我們將會(huì)討論用于聲明不同類型 Beans 的幾種最常用的 Spring Bean 注解。

眾所周知熙暴,Spring 容器中有許多配置 Bean 的方法闺属,我們既可以通過 XML 配置,也可以在配置類中使用 @Bean 注解來聲明 Beans周霉。此外掂器,我們還可以使用 org.springframework.stereotype 包中的一個(gè)注解來對(duì)類進(jìn)行標(biāo)記,然后把其余工作交給組件掃描即可俱箱。

組件掃描

在 Spring 中国瓮,一旦我們啟用了組件掃描, Spring 就會(huì)自動(dòng)掃描包中的 Bean狞谱。

通過使用 @ComponentScan 乃摹,Spring 就會(huì)自動(dòng)去掃描那些帶有注釋配置的類,我們可以使用 basePackages 或者 value 參數(shù)(兩者是一樣的跟衅,value 只不過是 basePackages 的另一種稱呼)來直接指定我們所要掃描的包的名稱孵睬,然后 Spring 就會(huì)去掃描我們指定包下所有帶有 @Component 注解的類,然后將其自動(dòng)注冊(cè)為一個(gè) Bean伶跷。

@Configuration
// 以下兩者之一即可
@ComponentScan(basePackages = "com.cunyu.annotions")
// @ComponentScan(value = "com.cunyu.annotions")
class PetFactoryConfig{
    ……
}

此外掰读,我們還可以使用 basePackageClasses 參數(shù)來指向基礎(chǔ)包中的類。

@Configuration
@ComponentScan(basePackageClasses = PetFactoryConfig.class)
class PetFactoryConfig{
    ……
}

basePackages 和 basePackageClasses 兩個(gè)參數(shù)都是數(shù)組類型的叭莫,所以在傳參時(shí)我們可以為他們提供多個(gè)包蹈集。

而如果沒有為 @ComponentScan 指定參數(shù),那么 Spring 就只會(huì)掃描和 @ComponentScan 注釋的類位于同一個(gè)包的帶有 @Component 注解的其他類雇初,然后將它們自動(dòng)創(chuàng)建為一個(gè) Bean拢肆。

@ComponentScan 充分利用了 Java 8 中的重復(fù)注解特性,因此我們能夠用它來多次標(biāo)記一個(gè)類:

@Configuration
@ComponentScan(basePackages = "com.cunyu.annotions")
@ComponentScan(basePackageClasses = PetFactoryConfig.class)
class PetFactoryConfig{
    ……
}

除開上面的方式來標(biāo)記一個(gè)類外,我們還可以使用 @CompentScans 來將多個(gè) @ComponentScan 包含起來善榛,用于指定多個(gè) @ComponentScan 配置辩蛋。

@Configuration
@ComponentScans({
    @ComponentScan(basePackages = "com.cunyu.annotions"),
    @ComponentScan(basePackageClasses = PetFactoryConfig.class)
})
class PetFactoryConfig{
    ……
}

除開使用注解的方式來實(shí)現(xiàn)組件掃描之外,我們還可以通過配置 XML 來進(jìn)行移盆,只需要在我們的配置文件中如下內(nèi)容即可:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans";
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
       xmlns:context="http://www.springframework.org/schema/context";
       xmlns:c="http://www.springframework.org/schema/c";
       xmlns:p="http://www.springframework.org/schema/p";
       xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">;

    <context:component-scan base-package="com.cunyu.dao" />
    <context:component-scan base-package="com.cunyu.service" />
    <context:component-scan base-package="com.cunyu.controller" />
</beans>

context:component-scan 元素將實(shí)現(xiàn)同注解 @ComponentScan 一樣的效果悼院,即掃描 com.cunyu 包下所有帶有 @Component 注解的類,并將它們注冊(cè)創(chuàng)建為 Bean咒循。

@Component

上一小節(jié)中我們講了 @ComponentScan 會(huì)自動(dòng)掃描組件過程中會(huì)去掃描那些帶有 @Component 注解的類据途,并將其注冊(cè)創(chuàng)建為 Bean,比如下面的例子:

@Component
public class Cat{
    ……
}

其中 Cat 類中帶有 @Component 注解叙甸,當(dāng) Spring 自動(dòng)掃描時(shí)颖医,就會(huì)去掃描 Cat 這個(gè)類,并創(chuàng)建一個(gè)名為 cat 的 Bean 實(shí)例裆蒸。

注意:默認(rèn)情況下熔萧,使用 @ComponentScan 去掃描 @Component 注解的類,生成對(duì)應(yīng)類的 Bean 實(shí)例時(shí)僚祷,Bean 實(shí)例具有與類名相同的名稱佛致,但不同的是 Bean 實(shí)例的首字母是小寫,而一般類名首字母是大寫辙谜。

@Component 是任意 Spring 管理組建的通用構(gòu)造型俺榆,當(dāng)組件不好歸類時(shí),一般使用該注解装哆,又可以分為如下幾個(gè)常用元注解:

  • @Repository:位于持久層罐脊,能將數(shù)據(jù)庫操作跑出的原生異常轉(zhuǎn)換為 Spring 持久層異常,用于標(biāo)注數(shù)據(jù)訪問組件蜕琴,即 DAO 組件萍桌;
  • @Service:位于業(yè)務(wù)邏輯層,只是標(biāo)注該類位于業(yè)務(wù)層邏輯奸绷;
  • @Configuration:用于定義配置類梗夸,可替換 XML 配置文件,被注解的類內(nèi)部包含一個(gè)或多個(gè)被 @Bean 注解的方法号醉,這些方法將會(huì)被 AnnotationConfigApplicaitonContext 或者 AnnotaionConfigWebApplicationContext 類掃描,并用于構(gòu)建 Bean 定義辛块,初始化 Spring 容器畔派;
  • @Controller:屬于 Spring MVC 的注解,進(jìn)行前端請(qǐng)求的處理润绵、轉(zhuǎn)發(fā)线椰、重定向;用于標(biāo)注控制層組件尘盼;

它們是針對(duì)不同使用場(chǎng)景而采取的帶有特定功能化的注解組件憨愉,其實(shí)質(zhì)功能其實(shí)和 @Component 一樣烦绳。因此,如果一個(gè)類被 @Component 注解了配紫,那么就可以根據(jù)這個(gè)類的實(shí)際功能径密,利用 @Repository、@Service … 等代替躺孝,而且代替后的注解會(huì)具備更多的功能享扔。

@Repository

DAO(Data Access Object,數(shù)據(jù)訪問對(duì)象植袍,為某種類型的數(shù)據(jù)庫或其他持久性機(jī)制提供一個(gè)抽象接口的對(duì)象) 或者 Repository 類通常代表應(yīng)用程序中的數(shù)據(jù)訪問層惧眠,我們一般傾向于使用 @Repository 注解

@Repository
public class PetRepository{
    ……
}

通過使用 @Repository 注解,它將啟用自動(dòng)持久化異常轉(zhuǎn)換于个。此時(shí)氛魁,當(dāng)我們使用一些持久化框架,比如 Hibernate厅篓、MyBatis……時(shí)秀存,當(dāng)帶有 @Repository 注解的類在拋出本地異常時(shí),就會(huì)自動(dòng)將其轉(zhuǎn)換為 Spring 中的 DataAccessException 的子類贷笛。

而要啟動(dòng)異常轉(zhuǎn)換应又,我們就需要自己去聲明我們 PersistenceExceptionTranslationPostProcessor 的實(shí)例,聲明的方式可以分為注解性和 XML 配置型乏苦。但是一般而言株扛,Spring 都會(huì)自動(dòng)幫我們?nèi)ネ瓿蛇@個(gè)過程,所以我們就不再需要自己去手動(dòng)聲明了汇荐。

  • 注解型
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
    return new PersistenceExceptionTranslationPostProcessor();
}

  • XML 配置型
<bean class="org.springframework.dao.annotion.PersistenceExceptionTranslationPostProcessor" />

@Service

對(duì)于應(yīng)用程序中的業(yè)務(wù)邏輯洞就,一般都是位于服務(wù)層,因此我們使用 @Service 注解一個(gè)類掀淘,來指明該類屬于服務(wù)層旬蟋。表示定義一個(gè) Bean,自動(dòng)根據(jù)所標(biāo)注的組件實(shí)例化一個(gè)首字母為小寫的 Bean革娄。實(shí)例如下:

@Service
public class PetService{
    ……
}

上述代碼中 PetService 類被標(biāo)注為一個(gè) Bean倾贰,其名稱為 petServie。

@Configuration

一般用于配置類拦惋,而且還可以包含用 @Bean 所注解的 Bean 定義方法匆浙,實(shí)例如下:

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

既然說了使用 @Confgiuration 可以和 XML 配置文件互換,那么以上的配置類等價(jià)于如下配置:

<beans>
    <bean id="dog" class = "com.cunyu.dao.Dog"/>
</beans>

注意:使用 @Configuration 注解時(shí)厕妖,一般需要遵循如下原則:

  1. @Configuration 注解的類不可以是 final 類型首尼;
  2. @Configuration 注解的類不可以是匿名類;
  3. 嵌套的 @Configuration 必須是靜態(tài)類;

@Controller

@Controller 注解是一個(gè)類級(jí)別的注解软能,當(dāng)把它用在類上時(shí)迎捺,表示該類在 Spring MVC 中充當(dāng)控制器,該類將被 Spring 自動(dòng)掃描查排,一般我們?cè)谠擃愔屑尤?@RequestMapping("…")凳枝,就可以直接使用瀏覽器來訪問對(duì)應(yīng)界面進(jìn)行邏輯處理了。實(shí)例如下:

@Controller
public class PetController{
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String sayHello(){
        return "你好";
    }
    ……
}

此時(shí)雹嗦,當(dāng)我們?nèi)g覽器中訪問 localhost:8080/hello 時(shí)范舀,頁面中就會(huì)顯示 你好 這條信息。

注意了罪,@Controller 注解的類下锭环,我們又發(fā)現(xiàn)了 RequestMapping 注解,該注解主要 6 個(gè)屬性泊藕,分別介紹如下:

  • value :用于指定瀏覽器指定的地址辅辩;
  • method,指定請(qǐng)求的 method 類型娃圆,一般有 GET玫锋、POST、PUT讼呢、DELETE撩鹿,而現(xiàn)在我們一般使用對(duì)應(yīng)的注解 @GetMapping、@PostMapping悦屏、@PutMapping节沦、@DeleteMapping;
  • consumes:用于指定處理請(qǐng)求的提交內(nèi)容類別(Content-Type)础爬,如 application/json甫贯、test/html;
  • produces:指定返回的內(nèi)容類別看蚜;
  • params:指定 request 中必須包含某些引用數(shù)值時(shí)叫搁,才能讓該方法處理;
  • headers:指定 request 中必須包含某些指定 header 值供炎,才能讓該方法處理請(qǐng)求渴逻;

原型注解及 AOP

當(dāng)我們使用 Spring 原型注解時(shí),可以十分容易地創(chuàng)建一個(gè)指向所有具有特定構(gòu)造型的類的切入點(diǎn)音诫。

比如我們?nèi)绻胍饬恳粋€(gè)方法在 DAO 層中的執(zhí)行時(shí)間裸卫,就可以充分利用 @Repository 注解的特點(diǎn)。

@Aspect
@Component
public class PerformanceAspect {
    @Pointcut("within(@org.springframework.stereotype.Repository *)")
    public void repositoryClassMethods() {};

    @Around("repositoryClassMethods()")
    public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint) 
      throws Throwable {
        long start = System.nanoTime();
        Object returnValue = joinPoint.proceed();
        long end = System.nanoTime();
        String methodName = joinPoint.getSignature().getName();
        System.out.println(
          "Execution of " + methodName + " took " + 
          TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
        return returnValue;
    }
}

以上實(shí)例中纽竣,我們創(chuàng)建了一個(gè)切入點(diǎn),該切入點(diǎn)會(huì)去匹配帶有 @Repository 注解的類中的所有方法。然后我們用 @Around 通知來定位切入點(diǎn)蜓氨,并確定被攔截的方法調(diào)用的執(zhí)行時(shí)間聋袋。通過使用這個(gè)方式,我們就可以輕松地給每個(gè)應(yīng)用程序添加日志記錄穴吹、性能管理幽勒、審計(jì)以及其他行為。

總結(jié)

好了港令,以上就主要介紹了 Spring 中的原型注釋啥容,并了解了它們各自所代表的意義。此外顷霹,還學(xué)習(xí)了如何使用組件掃描功能咪惠,從而告知 Spring 容器在何處能找到帶有注解的類。

如果你有更多的見解淋淀,歡迎評(píng)論留言遥昧,一起交流呀!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末朵纷,一起剝皮案震驚了整個(gè)濱河市炭臭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袍辞,老刑警劉巖鞋仍,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搅吁,居然都是意外死亡威创,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門似芝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來那婉,“玉大人,你說我怎么就攤上這事党瓮∠昃妫” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵寞奸,是天一觀的道長(zhǎng)呛谜。 經(jīng)常有香客問我,道長(zhǎng)枪萄,這世上最難降的妖魔是什么隐岛? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮瓷翻,結(jié)果婚禮上聚凹,老公的妹妹穿的比我還像新娘割坠。我一直安慰自己,他們只是感情好妒牙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布彼哼。 她就那樣靜靜地躺著,像睡著了一般湘今。 火紅的嫁衣襯著肌膚如雪敢朱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天摩瞎,我揣著相機(jī)與錄音拴签,去河邊找鬼。 笑死旗们,一個(gè)胖子當(dāng)著我的面吹牛蚓哩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚪拦,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼杖剪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了驰贷?” 一聲冷哼從身側(cè)響起盛嘿,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎括袒,沒想到半個(gè)月后次兆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锹锰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年芥炭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恃慧。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡园蝠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痢士,到底是詐尸還是另有隱情彪薛,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布怠蹂,位于F島的核電站善延,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏城侧。R本人自食惡果不足惜易遣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫌佑。 院中可真熱鬧豆茫,春花似錦侨歉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肤京,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茅特,已是汗流浹背忘分。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留白修,地道東北人妒峦。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像兵睛,于是被迫代替她去往敵國和親肯骇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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