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

}
相关文章
相关标签/搜索