自動偵測spring元件

zhaosoft1982發表於2010-03-09

從2.0版本開始,Spring引入了構造型(stereotype)註解的概念以及將@Repository註解作為資料訪問程式碼的標記的方法。在此基礎上,Spring2.5又加入了兩個新的註解 —— @Service@Controller 來完成為通常的三層架構(資料訪問物件、服務、web控制器)角色委任。Spring2.5也引入了泛型@Component註解,其他構造型可從邏輯上對其進行擴充套件。通過清晰地指明應用程式的角色,這些構造型方便了Spring AOP和post-processor的使用,這些post-processor給基於這些角色的加了註解的物件提供了附加行為。比如,Spring2.0引入了PersistenceExceptionTranslationPostProcessor對任何帶有@Repository 註解的物件自動啟用其資料訪問異常轉換。

這些註解同樣可以結合Spring2.5其他一些新效能來使用:自動偵測classpath上的元件。儘管XML已經成為最常見的Spring後設資料的格式,但它決不是唯一選擇。實際上,Spring容器內的後設資料是由純Java來表示的,當XML被用來定義Spring管理物件時,在例項化過程之前,那些定義會被解析並轉化成Java物件。Spring2.5的一個巨大的新功能是支援從原始碼層註解讀取後設資料。因而,上文描述的自動裝配機制使用註解的後設資料來注入依賴,但它仍然需要註冊至少一個bean定義以便提供每個Spring管理物件的實現類。元件掃描功能則使得這個XML中最起碼的bean 定義都不再存在需求性。

正如上面所示,Spring註解驅動的自動裝配可以在不犧牲細粒度控制的前提下極大程度地減少XML的使用。元件偵測機制將這個優點更發揚光大。全面替代XML中的配置不再必要,元件掃描反而可以處理XML後設資料來簡化整體配置。結合XML和註解驅動技術可以得到一個平衡優化的方法,這在2.5版本的PetClinic範例中有詳細闡述。在該範例中,基礎構架元件(資料來源、事務管理等)結合上文提到的外化屬性在XML中定義。資料訪問層物件也有部分在XML中定義,它們的配置也都利用了@Autowired註解來簡化依賴注入。最後,web層控制器完全不在XML中顯式定義,相反,下面提供的這段配置被用來觸發所有web控制器的自動偵測:

<context:component-scan base-package="org.springframework.samples.petclinic.web"/>

需要注意到的是這段示例中使用到了base-package屬性。元件掃描的預設匹配規則會遞迴偵測該包(多個包可以以逗號分隔的list方式提供)內的所有類的所有Spring構造型註解。正因為如此,PetClinic應用程式範例中的各類控制器的實現都採用了@Controller註解(Spring的內建構造型之一)。請看下面這個例子:

@Controller
public class ClinicController {

private final Clinic clinic;

@Autowired
public ClinicController(Clinic clinic) {
this.clinic = clinic;
}
...

自動偵測元件在Spring容器中註冊,就像它們在XML中被定義一樣。如上所示,那些物件可以輪流利用註解驅動的自動裝配。

元件掃描的匹配規則可以通過過濾器(filter)來自定義,以根據型別、AspectJ表示式、或針對命名模式的正規表示式來決定包含或不包含哪些元件。預設的構造型也可以被禁用。比如這裡有一個配置的例子,這個配置會忽略預設的構造型,但會自動偵測名字以Stub打頭或者包含@Mock註解的所有類:

<context:component-scan base-package="example" use-default-filters="false">
<context:include-filter type="aspectj" expression_r="example..Stub*"/>
<context:include-filter type="annotation" expression_r="example.Mock"/>
</context:component-scan>

型別匹配的限制性也可以用排他的過濾器控制。例如,除了@Repository註解外其他都依賴於預設過濾器,那麼就需要加入一個排他過濾器(exclude-filter)。

<context:component-scan base-package="example">
<context:exclude-filter type="annotation" expression_r="org.springframework.stereotype.Repository"/>
</context:component-scan>

很明顯,有很多方法可以擴充套件元件掃描來註冊自定義的型別。構造型註解是最簡單的選擇,所以構造型概念本身也是可擴充套件的。像先前提到的,@Component泛型模型,@Repository@Service,和@Controller註解都從該構造型邏輯擴充套件而得。正因為如此,@Component可被用來作為元註解(也就是說,在另外的註解上宣告的註解),所有具有@Component元註解的自定義註解都會被預設掃描匹配規則自動偵測到。一個例子就有希望讓你領會到其實它根本沒有聽起來那麼難。

讓我們回想一下在講@PostConstruct@PreDestroy生命週期註解的時候的假想的後臺任務。也許一個應用程式有很多很多這樣的後臺任務,這些任務例項需要XML bean定義以便在Spring context裡註冊並使它們自己的生命週期方法在正確時候被呼叫。利用元件掃描就不再需要這些顯式的XML bean定義。如果這些後臺任務都實現一個相同的介面或者都沿用同樣的命名慣例,那麼可以用include-filters。然而,更簡單的方法是為這些任務物件建立一個註解並提供@Component元註解。

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

然後在所有後臺任務的類定義中提供自定義構造型註解。

@BackgroundTask
public class FilePoller {

@PostConstruct
public void startPolling() {
...
}

@PreDestroy
public void stopPolling() {
...
}
...
}

泛型@Component註解可以像例子中提供的那樣簡單使用,自定義註解技術則提供了一個使用更具涵義的、領域特定的名字的機會。這些領域特定註解提供更深入的機會,比如使用AspectJ切點表示式來識別所有後臺任務,以便增加advice來監控這些任務的活動性。

預設的,元件被偵測到的時候,Spring會自動生成一個沒有修飾符的類名作為bean名字。上一個例子中,生成的bean名字會是filePoller。但是,任何加註了Spring構造型註解(@Component@Repository@Service@Controller)或是加註了其他的以@Component作為元註解的註解(比如上面例子中的@BackgroundTask )的類,構造型註解的value屬性可以被顯式指定,例項將該值作為它的bean名字註冊到context中。接下來的例子裡,例項名應該是petClinic而不是預設生成的名字simpleJdbcClinic。

@Service("petClinic")
public class SimpleJdbcClinic {
...
}

同樣的,在下面修正版的FilePoller例子裡,生成的bean名字應該是poller而不是filePoller。

@BackgroundTask("poller")
public class FilePoller {
...
}

雖然所有Spring管理物件都被預設地當作單例例項來處理,但有些時候還是有必要為某個物件指明一個備用的範圍(scope)。舉個例子來說,在web層,一個Spring管理物件可能捆綁到request或session的範圍。對於2.0版本,Spring 的scope機制更具延展性,這樣一來,自定義scope可以被註冊到應用程式上下文(application context)。在XML配置中,僅僅是簡單地包含進scope屬性及該scope的名字就可以了。

<bean id="shoppingCart" class="example.ShoppingCart" scope="session">
...
</bean>

Spring2.5中,為被掃描的元件提供@Scope註解可以起到同樣的作用。

@Component
@Scope("session")
public class ShoppingCart {
...
}

這裡要指出的最後一點是使用元件掃描時qualifier註解應用是多麼的簡單。在上一節,下面這個物件曾被作為使用自定義qualifier註解進行自動裝配的例子:

@VetSpecialty("dentistry")
private Clinic dentistryClinic;

同樣的例子接著展現了在XML內使用‘qualifier’元素為依賴提供指定目標bean定義。在使用元件掃描時,XML後設資料不是必須的。但自定義修飾符也許在目標類定義中被作為型別層註解而引入。另一個將被掃描的@Repository例項作為依賴的例子如下:

@Repository
@VetSpecialty("dentistry")
public class DentistryClinic implements Clinic {
...
}

最終,因為前面的例子展現了自定義註解及其屬性的例子,相等同的非XML表示依賴目標的方法如下:

@Repository
@SpecializedClinic(species="dog", breed="poodle")
public class PoodleClinic implements Clinic {
...
}

相關文章