Spring核心技術(十一)——基於Java的容器配置(一)

基本概念: @Bean@Configuration

Spring中新的基於Java的配置的核心就是支持@Configuration註解的類以及@Bean註解的方法。java

@Bean註解用來表示一個方法會實例化,配置,並初始化一個新的由Spring IoC容器所管理的對象。其做用等於XML配置中的<beans>標籤下的<bean>子標籤。開發者能夠用@Bean註解來和任何的Spring@Component來聯合使用,可是,最多見的狀況下,@Bean註解仍是應用到註解了@Configuration的類下面的。web

註解了@Configuration的類就表示這個類的首要目的是用來管理Bean的配置的。並且,@Configuration註解的類容許inter-bean之類的依賴在類中經過方法調用來引用。最簡單的配置以下:spring

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

上面的配置將等價於以下的XML配置:編程

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

@Configuration對比輕@Bean模式
@Bean註解的方法聲明到了沒有配置@Configuration的類中時,這些方法就會做爲輕@Bean模式來處理。舉例來講,Bean方法若是聲明到了@Component註解的類,或者普通的類之中,就是輕@Bean模式。
和使用@Configuration不一樣,輕@Bean方法不可以聲明inter-bean的依賴,一般一個@Bean方法不該該調用另外一個@Bean
只有在註解了@Configuration的類中使用@Bean方法纔是全模式。這回防止不少@Bean方法屢次調用,而且消除一些細微的bug。這些bug在輕模式很難被跟蹤。數組

@Bean@Configuration註解將會在本文中詳細討論,首先咱們須要瞭解各類經過基於Java的配置建立容器的方法。markdown

實例化Spring容器

本節描述的是使用Spring的AnnotationConfigApplicationContext。在Spring 3.0後,各式各樣的ApplicationContext實現均可用了,不僅是@Configuration註解的類,還有一些註解了@Component的組件類以及註解了JSR-330元數據的類等。app

當註解了@Configuration的類做爲輸入的時候,註解了@Configuration的類自己也會被註冊爲一個Bean,其中註解了@Bean的方法都會被註冊爲Bean。ide

當註解了@Component的類或者JSR-330的類做爲輸入的時候,他們一樣會被註冊爲Bean,並且能夠經過DI來注入到其餘的類裏面去,好比經過@Autowired或者@Inject註解。函數

簡單構造

在Spring使用XML做爲輸入的時候,實例的ApplicationContextClassPathXmlApplicationContext,而經過@Configuration註解的類,實例化的ApplicationContextAnnotationConfigApplicationContext。下面的代碼能夠徹底去除XML的配置就使用了Spring容器。url

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

如前面所述,AnnotationConfigApplicationContext不只僅能夠和註解了@Configuration的類配合,任何註解了@Component或者是JSR-330的類一樣能夠做爲輸入來構造Sprign容器,好比:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

上面這段代碼讓MyServiceImplDependency1以及Dependency2均可以使用Spring的依賴注入註解進行裝載,好比@Autowired

經過register(Class<?>..)來構建容器

AnnotationConfigApplicationContext類除了經過類來初始化,也能夠經過無慘構造函數來進行構造,以後經過register()方法來配置。這種方法在經過編程的方式來構建AnnotationConfigApplicationContext的過程頗有用。

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

使能組件掃描

使能組件掃描在前文中也略有說起,只須要在@Configuration註解的類上配置便可:

@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig  {
    ...
}

在XML等效的配置中,配置以下:

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

在上面的例子中,com.acme包中的內容會被掃描,來查找其中註解了@Component的類,這些類都會被註冊爲Spring的Bean實例。AnnotationConfigApplicationContext也能夠經過scan(String ...)方法來經過函數調用的方式來進行掃描配置:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

@Congirufation註解的類和@Component註解的類都是掃描的候選者。在上面的例子中,若是AppConfig類在com.acme包中(或者是其子包之中),AppConfig都會被scan方法掃描到,在refresh()方法調用後,其中的@Bean註解的方法都會做爲Bean實例被註冊到容器之中。

對於Web應用的支持

WebApplicationContext接口關於AnnotationConfigApplicationContext的一個變化的版本就是AnnotationConfigWebApplicationContext。這一實現能夠在配置Spring的ContextLoaderListener這個Servlet的listener或者Spring MVC的DispatcherServlet的時候使用。下面就是web.xml中配置Spring MVC程序的一個配置:

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited fully-qualified @Configuration classes. Fully-qualified packages may also be specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

使用@Bean註解

@Bean註解是一個方法級別的註解,和XML中的<bean />標籤的功能一直。該註解支持<bean/>的一些配置屬性,好比init-method,destroy-method,autowiring以及name等。

@Bean註解能夠在註解了@Configuration或者@Component的類中使用。

聲明Bean

經過將方法註解@Bean便可聲明實例爲Bean。開發者經過這個方法將Bean註冊到ApplicationContext,方法返回的類型就是Bean的類型。默認狀況下,方法的名字就是Bean默認的名字,參考以下Bean的聲明:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

上面的代碼徹底等價於一下XML:

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

兩種聲明均可以在ApplicationContext中聲明一個名爲transferService的Bean,實例的類型爲TransferServiceImpl:

transferService -> com.acme.TransferServiceImpl

Bean依賴

@Bean註解的方法能夠有任意數量的參數來描述其依賴。距離來講,若是TransferService的其中一個依賴爲AccountRepository的話,咱們能夠經過方法參數來構造:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

解析的機制是和基於構造的依賴注入基本一致的,能夠參考前文Spring的依賴及其注入瞭解相關內容。

接收生命週期回調

任何經過@Bean註解了的方法返回的類,都支持常規的生命週期回調,並能夠經過使用JSR-250中的@PostContruct以及@PreDestroy註解。

基本的Spring生命週期也是一樣支持的,若是Bean實現了InitializingBeanDisposableBean或者是Lifecycle接口的話,這些方法都會被容器調用。

標準的*Aware接口好比說BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware等接口也是支持的。
@Bean也支持指定任意的初始化以及銷燬回調函數,跟Spring XML配置中的init-methoddestroy-method屬性是一致的:

public class Foo {
    public void init() {
        // initialization logic
    }
}

public class Bar {
    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public Foo foo() {
        return new Foo();
    }

    @Bean(destroyMethod = "cleanup")
    public Bar bar() {
        return new Bar();
    }
}

默認狀況下,經過Java配置的Bean都會有一個close或者shutdown方法來做爲自動的銷燬回調。若是開發者聲明瞭close方法或者是shutdown方法,可是不但願由容器來調用的話,能夠在註解中標記爲@Bean(destroyMethod="")來代替默認的行爲。

固然,在上面的Foo例子當中,也能夠直接調用init()方法:

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
        return foo;
    }

    // ...

}

當直接使用Java的配置的時候,開發者能夠任意操做對象,而不只僅是依賴於容器的聲明週期

指定Bean的做用域

使用@Scope註解

開發者也能夠經過Java配置的方式來指定@Bean的做用域,開發者可使用全部的標準的做用域,關於做用域的信息,能夠在Spring中Bean的做用域一文中瞭解。

默認的做用域是singleton,可是開發者能夠經過@Scope註解來覆蓋掉默認值:

@Configuration
public class MyConfiguration {
    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}

@Scope註解以及做用域代理

Sprnig針對那些做用域的依賴是經過代理來工做的。在XML中,能夠經過使用<aop:scoped-proxy/>標籤來達到這個目的。經過Java配置的的@Scope註解也提供等價的支持,默認的沒有代理配置爲ScopedProxyMode.NO,也能夠指定爲ScopedProxyMode.TARGET_CLASS或者ScopedProxyMode.INTERFACES.

Java配置以下:

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}

自定義Bean的名字

默認狀況下,配置類會讀取@Bean方法中的方法的名字值做爲Bean的名字。固然能夠經過name屬性來覆蓋這個功能。

@Configuration
public class AppConfig {
    @Bean(name = "myFoo")
    public Foo foo() {
        return new Foo();
    }
}

Bean的別名

前文之中有針對Bean名字的描述,有時候會給一個Bean多個名字,做爲Bean的別名,@Bean註解的name屬性也支持Spring數組類型的值:

@Configuration
public class AppConfig {

    @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }

}

Bean的描述

有的時候爲Bean提供額外的文本描述可讓別人更瞭解該Bean的做用。這一點尤爲在監視容器中的Bean的時候頗有效。

能夠經過使用@Description註解來作到:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Foo foo() {
        return new Foo();
    }

}
相關文章
相關標籤/搜索