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

基本概念: @Bean@Configuration

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

@Bean注解用來表示一個方法會實例化,配置,并初始化一個新的由Spring IoC容器所管理的對象。其作用等于XML配置中的<beans>標簽下的<bean>子標簽。開發(fā)者可以用@Bean注解來和任何的Spring@Component來聯(lián)合使用,但是,最常見的情況下,@Bean注解還是應用到注解了@Configuration的類下面的。

注解了@Configuration的類就表示這個類的首要目的是用來管理Bean的配置的。而且,@Configuration注解的類允許inter-bean之類的依賴在類中通過方法調(diào)用來引用。最簡單的配置如下:

@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方法不應該調(diào)用另一個@Bean。
只有在注解了@Configuration的類中使用@Bean方法才是全模式。這回防止很多@Bean方法多次調(diào)用,并且消除一些細微的bug。這些bug在輕模式很難被跟蹤。

@Bean@Configuration注解將會在本文中詳細討論,首先我們需要了解各種通過基于Java的配置創(chuàng)建容器的方法。

實例化Spring容器

本節(jié)描述的是使用Spring的AnnotationConfigApplicationContext。在Spring 3.0后,各式各樣的ApplicationContext實現(xiàn)都可用了,不只是@Configuration注解的類,還有一些注解了@Component的組件類以及注解了JSR-330元數(shù)據(jù)的類等。

當注解了@Configuration的類作為輸入的時候,注解了@Configuration的類本身也會被注冊為一個Bean,其中注解了@Bean的方法都會被注冊為Bean。

當注解了@Component的類或者JSR-330的類作為輸入的時候,他們同樣會被注冊為Bean,而且可以通過DI來注入到其他的類里面去,比如通過@Autowired或者@Inject注解。

簡單構造

在Spring使用XML作為輸入的時候,實例的ApplicationContextClassPathXmlApplicationContext,而通過@Configuration注解的類,實例化的ApplicationContextAnnotationConfigApplicationContext。下面的代碼可以完全去除XML的配置就使用了Spring容器。

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();
}

上面這段代碼讓MyServiceImpl,Dependency1以及Dependency2都可以使用Spring的依賴注入注解進行裝載,比如@Autowired

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

AnnotationConfigApplicationContext類除了通過類來初始化,也可以通過無慘構造函數(shù)來進行構造,之后通過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包中的內(nèi)容會被掃描,來查找其中注解了@Component的類,這些類都會被注冊為Spring的Bean實例。AnnotationConfigApplicationContext也可以通過scan(String ...)方法來通過函數(shù)調(diào)用的方式來進行掃描配置:

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()方法調(diào)用后,其中的@Bean注解的方法都會作為Bean實例被注冊到容器之中。

對于Web應用的支持

WebApplicationContext接口關于AnnotationConfigApplicationContext的一個變化的版本就是AnnotationConfigWebApplicationContext。這一實現(xiàn)可以在配置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。開發(fā)者通過這個方法將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注解的方法可以有任意數(shù)量的參數(shù)來描述其依賴。距離來說,如果TransferService的其中一個依賴為AccountRepository的話,我們可以通過方法參數(shù)來構造:

@Configuration
public class AppConfig {

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

}

解析的機制是和基于構造的依賴注入基本一致的,可以參考前文Spring的依賴及其注入了解相關內(nèi)容。

接收生命周期回調(diào)

任何通過@Bean注解了的方法返回的類,都支持常規(guī)的生命周期回調(diào),并可以通過使用JSR-250中的@PostContruct以及@PreDestroy注解。

基本的Spring生命周期也是同樣支持的,如果Bean實現(xiàn)了InitializingBeanDisposableBean或者是Lifecycle接口的話,這些方法都會被容器調(diào)用。

標準的*Aware接口比如說BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware等接口也是支持的。
@Bean也支持指定任意的初始化以及銷毀回調(diào)函數(shù),跟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方法來作為自動的銷毀回調(diào)。如果開發(fā)者聲明了close方法或者是shutdown方法,但是不希望由容器來調(diào)用的話,可以在注解中標記為@Bean(destroyMethod="")來代替默認的行為。

當然,在上面的Foo例子當中,也可以直接調(diào)用init()方法:

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

    // ...

}

當直接使用Java的配置的時候,開發(fā)者可以任意操作對象,而不僅僅是依賴于容器的聲明周期

指定Bean的作用域

使用@Scope注解

開發(fā)者也可以通過Java配置的方式來指定@Bean的作用域,開發(fā)者可以使用所有的標準的作用域,關于作用域的信息,可以在Spring中Bean的作用域一文中了解。

默認的作用域是singleton,但是開發(fā)者可以通過@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數(shù)組類型的值:

@Configuration
public class AppConfig {

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

}

Bean的描述

有的時候為Bean提供額外的文本描述可以讓別人更了解該Bean的作用。這一點尤其在監(jiān)視容器中的Bean的時候很有效。

可以通過使用@Description注解來做到:

@Configuration
public class AppConfig {

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

}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容