spring 基於java的配置

7.10 Classpath scanning and managed componets

文檔地址: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-classpath-scanning 本章中大部分例子是用xml來指定配置元數據,以生成容器裏的每一個bean定義.上個部分描述瞭如何經過資源註解來提供大量的配置元數據.可是,這不少例子中,不少bean定義是在xml文件中完成的,而註解只負責依賴注入.本節描述如何經過掃描路徑來檢測可選組件.候選組件都是一些能匹配一些條件的類或者在容器中註冊了相應的bean定義.這移除了使用xml註冊bean的必要,所以你可用使用註解,AspectJ類型表達,又或者你的自定義攔截條件 來選擇容器中註冊的bean定義.html

從spring3.0,spring的java配置項目提供了的不少功能已是spring核心功能的一部分.這容許你使用java而不是xml文件來定義beans.學習@Configuration,@Bean,@Import,@Dependson註解,並學習如何使用.java

7.10.1 @Component and further sterertype annotations

@Repository註解是一個任何能完成repository(也就是DAO)角色或模板的類的標誌.使用這些標誌能夠自動轉譯異常,如Section 20.2.2,"Exception translation";web

spring提供了不少固定註解:@Component,@Service,@Controller,@Repository.@Component是一個spring管理組件的基本模板類型.@Repository,@Service,@Controller是對@Componet的特殊化,應用於特定的場合,例如,他們分別在持久化,服務層,表現層中使用.所以,你能夠用@Component來標誌你的組件,但你最好用@Repository,@Service,@Controller來替換,由於這樣可使這些類更好的而配合工做操做或者和切面協同.例如,這些模板註解都是切入點的理想目標.spring框架在之後的發佈版中@Repository,@Service,@Controller可能會攜帶額外的語法.所以,當你考慮用@Service或@Component來標註你的服務層是,@Service是更好的選擇.同上文所示,@Repository已被當爲了一個支持持久層自動異常轉譯的標誌.算法

7.10.2 Meta-annotations (元註解)

不少spring提供的註解能夠做爲元註解在你的代碼裏使用.元註解是指能夠在其餘註解中使用的註解.例如,上文中提到的@Service註解中就有@Component元註解.spring

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Spring will see this and treat @Service in the same way as @Component
public @interface Service {

    / / ....
}

元註解能夠被聚合生成組合註解.例如,spring mvc的@RestController註解就是@Controller和@ResponseBody組合而成的.express

另外,組合註解能夠從新申明元註解的屬性來容許用戶自定義.當你只打算暴露一部分元註解屬性時這將很是有用.例如,spring的@SessionScope以硬編碼的方式控制scope的名字爲session,但扔容許自定義proxyMode的值.安全

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

    /**
     * Alias for {@link Scope#proxyMode}.
     * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
     */
    @AliasFor(annotation = Scope.class)
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

@SessionScope不申明proxyMode屬性仍能夠以下使用session

@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    // ...
}

更多細節,請查看spring annotaion programming Modelmvc

7.10.3 Automatically detectin classes and registering bean definitions(自動檢測類並註冊bean定義)

spring 能夠自動檢測模板類並經過ApplicationContext來註冊相應的bean定義.例如,一下兩個類將會代理以實現自動檢測.app

@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,你須要在你的@Configuration類中添加@ComponentScan註解,@ComponentScan中的basePackages屬性應該是兩個類的共同父包.(另外,你能夠制定一個逗號,分號,空白分割的列表來包含每一個類的父包);

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

爲了簡化,上面的能夠直接使用註解你的value屬性,如 @ComponentScan("org.example")

如下是相應的xml

<?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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

還有,當你使用component-scan元素時,AutowiredAnnotaionBeanPostProcessor,CommonAnnotationBeanPostProcessor都是隱式包含的.這意味着這兩個組件會自動檢測被一塊兒註冊,不須要在xml裏提供任何bean配置元數據.

另,你也能夠不註冊AutowiredAnnotaionBeanPostProcessor,CommonAnnotationBeanPostProcessor,這須要你在annotation-config元素中將值設爲false;

7.10.4 使用過濾器來自定義掃描

通常而言,用@Component,@Repository,@Service,,@Controller,或其餘自己標記了@Component註解的註解才能被檢測爲可選組件.可是,你能夠簡單的經過使用自定義過濾器來修改並擴展這個行爲.將他們做爲includeFilters或excludeFilters的參數添加到@ComponentScan註解裏.下面的表將描述攔截選項.

攔截器類型

攔截器類型

annotaion,assignable,aspectj,regex,custom五種. 下面的例子代表全部的@Repository註解都會被排除,並使用以stub結尾的repository替代.

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

等同於

<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>

@Filter,屬性時type,pattern,默認是annotation;你也能夠經過設置@ComponentScan的useDefaultFileters=false或設置<component-scan>元素中的use-default-filters="false"來禁用默認過濾器.而受影響的類是標註了@Componnet,@Repository,@Service,@Controller,或者@Configuration的類.

7.10.5 Defining bean medata within components(在components中定義bean的元數據)

spring 的components也能夠向容器提供bean定義.你能夠像在@Configuration註解類中同樣使用@Bean註解來定義bean的元數據.下面是一個簡單的例子:

@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }

}

這個類是一個spring組件類,在它的doWork()方法裏有應用特定的代碼.然,它也提供了一個bean定義,用一個工廠方法來指向方法publicInstance().@Bean註解標記着工廠方法或其餘bean定義屬性.例如經過@Qualifier提供的一個qualifier值.其餘方法級別的註解能夠被@Scope,@Lazy或其餘自定義註解等來指定.

  • 可使用@Lazy同@Autowired,@Inject標誌懶加載.在本上下文中,它將注入懶加載代理.

自動裝配的字段和方法支持上文已經討論了,另外討論的是對@Bean方法自動裝配的支持:

@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters

    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }

}

上面的例子會自動裝配字符串類型的方法參數country的值爲一個名爲privateInstance的bean的Age屬性.一個spring表達式語言元素經過#{<expression>}的形式定義了表達式的值.對於@Value註解,一個表達式解析器會會再解析表達文本以前前查看bean的名稱. 在spring的Component裏的@Bean方法不一樣於@Configuration類裏的@Bean方法的處理.這個不一樣在於@Component的類沒有經過CGLIB來攔截方法或屬性的調用.CGLIB代理是經過在@Configueration類中定義了指向其餘協做對象的bean的元數據@Bean方法來調用相應的方法或字段;這些方法並無經過正常的java語義而是經過容器調用的,爲的就是當經過調用@Bean方法來引用其餘beans時能夠提供正常的生命週期管理和spring的代理.相反的,在一個簡單的@Component類裏調用@Bean方法的字段或方法會有正常的java語法,而不是一個特殊的CGLIB處理或其餘限制條件.

若是你吧一個@bean的方法宣佈爲static,這能夠容許你在bean未實例化以後調用它們.當你定義了後處理器類時,這會很是有意義.由於這些類會再容器生命週期中很早啓動,這樣會避免在此時觸發容器配置的其餘部分.

調用@bean的靜態方法不會被容器攔截,即便當你在@Configuration類裏調用.這主要是技術的限制:CGLIB子類只能重寫非靜態的方法.結果,直接調用其餘的@Bean方法擁有java標準語法,因此工廠方法自身會返回一個獨立的實例.

java語言中@Bean方法的存在並不會對spring容器裏的bean定義形成直接的影響.你能夠自由的在你認爲合適的非@Configuration類裏申明工廠方法,也能夠將之設置爲靜態方法.可是,在@Configuration類裏的常規@Bean方法可能須要重寫,因此 他們不能命名爲private或final.

@Bean方法能夠在一個指定的組件類或配置類裏發現,和java 8 發現組件類和配置類中實現接口申明的默認方法同樣.這就容許在組裝複雜的配置組合時有很大的靈活性.即便經過java8的默認方法使混合繼承成爲可能.

最後,記住一個單個的class可能對於同一個bean持有多個混合的@Bean方法,混合工廠方法的安排取決於運行時那些依賴是可得到.這個算法同其餘配置場景裏選擇最貪婪的工廠方法和構造器同樣:可適依賴數量最多的種類在構造期間獲取,同容器如何選擇混合@Autowired構造器同樣.

7.10.6 命名自動檢測組件 Naming autodected components

當一個組件做爲掃描過程的一部分進行自動檢測時,它的bean的名稱是經過BeanNameGenerator策略來告知掃描器的.默認的,spring任何固定類型註解(@Componnet,@Repository,@Service,@Controller)包含一個那麼值,並將它提供給對於的bean定義.

若是一個註解沒有包含value值或其餘可檢測的組件(能夠被自定義攔截器攔截的bean).默認的bean名稱生成器返回一個小寫字母開頭的非限制的類名稱.例如,若是如下兩個組件被檢測,那麼它們的名稱應該是myMovieListener或movieFinderImpl;

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}

@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

若是你不想依賴默認的bean命名策略,你能夠提供一個自定義命名策略.首先,實現BeanNameGenerator接口,並確保它有一個無參構造器.另外,在配置掃描器時提供一個全匹配符的類名稱.

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    ...
}

或者

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

首先,思考指定註解的值,由於其餘組件可能會引用它.另外一方面,當容器進行注入時,名稱自動生成策略老是差強人意的.

7.10.7 Providing a scope for autodetected components(爲自動檢測組件提供做用域)

通常而言,spring管理的組件默認和通用的自動檢測組件的做用域通常是單例.可是,有時你須要使用@Scope來定義其餘的做用域.使用註解提供做用域的名稱來實現.

@Scope("prototype")
@Repostitory
public class MovieFinderImpl implements MovieFinder{
}

web特定的做用域細節,可查看7.4.5章,Request,session,global session,application,and WebSocket scopes

若是你想使用自定義做用域策略而不是使用基於註解方法,你須要實現ScopeMetadataResolver接口,並保證該實現類有一個無參構造器.另外,在配置掃描器時提供一個類的全路徑名稱.

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    ...
}

xml配置方法

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

當你使用特定非單例做用域,它可能須要爲做用域裏的對象提供代理. 這個緣由在"scoped beans as dependencies"一節裏已經描述.爲實現這個目標,component-scan元素提供了一個scoped-proxy元素,它有三個值:no,interfacce,targerClass.例如,下面的配置將會是標準的JDK動態代理:

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}

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

7.10.8 經過註解提供匹配符元數據

咱們在7.9.4這節裏考慮過,'Fine-tuning annotation-based autowiring with qualifier'. 本節的例子說明在你要解決自動注入條件問題時,你可使用@Qualifier註解或自定義註解來得到更好的控制.由於這些例子都是基於xml的bean定義,這個通配符元數據經過是經過候選的bean定義使用xml裏bean元素的qualifier或meta子元素來定義.當你使用類路徑掃描或組件自動檢測,你須要提供在候選類裏經過類級別的註解來提供匹配符元數據.如下三個例子說明了這個技術:

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

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

@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}

做爲大部分基於xml的可替換項,要記住註解元數據是基於類定義自身的,而使用xml能夠爲同一類型的不一樣bean定義提供它們相應的配置元數據.由於註解是類級別的,而xml裏的bean則是實例級別的.

相關文章
相關標籤/搜索