spring classpath掃描

對受管組件的Classpath掃描

到目前爲止本章中的大多數例子都使用XML來指定配置元數據, 這些元數據會生成Spring容器中的每個BeanDefinition。 上一節(第 3.11 節 “基於註解(Annotation-based)的配置”)演示瞭如何使用代碼級註解來提供大量配置元數據。 然而,即使是在那些例子中,“基礎”bean定義還是顯式地定義在XML文件中,註解只是用來驅動依賴注入的。 本節中會介紹一種方法,通過掃描classpath並匹配過濾器來隱式地檢測候選組件 (candidate components)。

 @Component和更多典型化註解

從Spring 2.0開始,引入了@Repository註解, 用它來標記充當儲存庫(又稱 Data Access Object或DAO)角色或典型的類。利用這個標記可以做很多事, 其中之一就是對第 12.6.4 節 “異常轉化”中描述的異常進行自動轉換。

Spring 2.5引入了更多典型化註解(stereotype annotations): @Component@Service@Controller@Component是所有受Spring管理組件的通用形式; 而@Repository@Service@Controller則是@Component的細化, 用來表示更具體的用例(例如,分別對應了持久化層、服務層和表現層)。也就是說, 你能用@Component來註解你的組件類, 但如果用@Repository@Service@Controller來註解它們,你的類也許能更好地被工具處理,或與切面進行關聯。 例如,這些典型化註解可以成爲理想的切入點目標。當然,在Spring Framework以後的版本中, @Repository@Service@Controller也許還能攜帶更多語義。如此一來,如果你正在考慮服務層中是該用 @Component還是@Service, 那@Service顯然是更好的選擇。同樣的,就像前面說的那樣, @Repository已經能在持久化層中進行異常轉換時被作爲標記使用了。

 自動檢測組件

Spring可以自動檢測“被典型化”(stereotyped)的類,在ApplicationContext 中註冊相應的BeanDefinition。例如,下面的這兩個類就滿足這種自動檢測的要求:

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

要檢測這些類並註冊相應的bean,需要在XML中包含以下元素,其中'basePackage'是兩個類的公共父包 (或者可以用逗號分隔的列表來分別指定包含各個類的包)。

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
               
     <context:component-scan base-package="org.example"/>
     
</beans>

此外,在使用組件掃描元素時,AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor會隱式地被包括進來。 也就是說,連個組件都會被自動檢測織入 - 所有這一切都不需要在XML中提供任何bean配置元數據。

注意

通過加入值爲'false'的annotation-config屬性可以禁止註冊這些後置處理器。

 使用過濾器自定義掃描

默認情況下,用@Component @Repository@Service @Controller (或本身使用了@Component註解的自定義註解) 註解的類是唯一會被檢測到的候選組件。但是可以很方便地通過自定義過濾器來改變並擴展這一行爲。 可以用'component-scan'的include-filterexclude-filter子元素來進行添加。 每個過濾器元素都要求有'type'和'expression'屬性。 下面給出了四個已有的可選過濾器。

3.7. 過濾器類型

過濾器類型 表達式範例
annotation

org.example.SomeAnnotation

assignable

org.example.SomeClass

regex

org\.example\.Default.*

aspectj

org.example..*Service+


下面這個XML配置會忽略所有的@Repository註解並用“stub”儲存庫代替。

<beans ...>

     <context:component-scan base-package="org.example">
        <context:include-filter type="regex" expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
     </context:component-scan>

</beans>

注意

你也可以用<component-scan/>元素的use-default-filters="false" 屬性來禁用默認的過濾器。這會關閉對使用了@Component@Repository@Service@Controller的類的自動檢測。

 自動檢測組件的命名

當一個組件在某個掃描過程中被自動檢測到時,會根據那個掃描器的BeanNameGenerator 策略生成它的bean名稱。默認情況下,任何包含name值的Spring“典型”註解 (@Component@Repository@Service@Controller) 會把那個名字提供給相關的bean定義。如果這個註解不包含name值或是其他檢測到的組件 (比如被自定義過濾器發現的),默認bean名稱生成器會返回小寫開頭的非限定(non-qualified)類名。 例如,如果發現了下面這兩個組件,它們的名字會是'myMovieLister'和'movieFinderImpl':

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

注意

如果你不想使用默認bean命名策略,可以提供一個自定義的命名策略。首先實現 BeanNameGenerator 接口,確認包含了一個默認的無參數構造方法。然後在配置掃描器時提供一個全限定(fully-qualified)類名:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             name-generator="org.example.MyNameGenerator" />

</beans>

作爲一條常規,當其他組件可能會顯式地引用一個組件時可以考慮用註解來指定名稱。 另一方面,當容器負責織入時,自動生成的名稱就足夠了。

爲自動檢測的組件提供一個作用域

通常受Spring管理的組件,默認或者最常用的作用域是“singleton”。然而,有時也會需要其他的作用域。 因此Spring 2.5還引入了一個新的@Scope註解。只要在註解中提供作用域的名稱就行了, 比方說:

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

注意

如果你想提供一個自定義的作用域解析策略,不使用基於註解的方法,實現ScopeMetadataResolver 接口,確認包含一個默認的沒有參數的構造方法。然後在配置掃描器時提供全限定類名:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             scope-resolver="org.example.MyScopeResolver" />
     
</beans>

當使用某些非singleton的作用域時,可能需要爲這些作用域中的對象生成代理。 原因在標題爲第 3.4.4.5 節 “作用域bean與依賴”的章節中已經說過了。 爲了這個目的,'component-scan'元素有一個scoped-proxy屬性。 三個可能值是:'no'、'interfaces'和'targetClass'。比方說,下面的配置會產生標準的JDK動態代理:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             scoped-proxy="interfaces" />
     
</beans>

用註解提供限定符元數據

在名爲第 3.11.2 節 “基於註解的自動連接微調”的章節裏引入了@Qualifier註解。 那節的例子中演示了@Qualifier註解的用法,以及如何用自定義限定符註解在自動織入解析時提供精細控制。 那些例子是基於XML bean定義的,所以限定符元數據是在XML中由'bean'元素的 'qualifier'或'meta'子元素提供。使用classpath掃描來自動檢測組件時, 限定符元數據可以由候選類上的類別級(type-level)註解來提供。下面的三個例子就演示了這個技術。

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}

注意

和其他大多數基於註解的東西一樣,請牢記註解元數據是綁定到類定義上的, 而XML則允許多個相同類型的bean根據限定符元數據提供多種選擇, 因爲元數據是由每個實例提供的,而不是每個類。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章