文章参考来源:Spring Framework官方文档
本节介绍如何在Java代码中使用注释来配置Spring容器。它包括以下主题:
Spring 3.0中开始引入的AnnotationConfigApplicationContext,这个通用的ApplicationContext实现不仅能够接受@Configuration类作为输入,还能够接受普通的@Component类和用JSR-330元数据注释的类。
(1)当接受@Configuration类作为输入去实例化容器时:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
// ...
}
(2)同样,也可以接受@Component 或JSR-330 注解的类实例化容器
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
在初始化AnnotationConfigApplicationContext时,甚至可以使用它的无参构造方法,然后调用register()方法进行配置,用scan(…)进行包扫描。
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
@Bean注解作用于:对方法实例化、配置和初始化新对象,而这些对象会被Spring IoC容器管理。对于那些熟悉Spring的 XML配置的人来说,@Bean注释扮演着与< beans />相同的角色。可以在任何Spring 的@Component中使用@ bean注解。不过,@Bean最常与@Configuration一起使用。
一个@Bean和@Configuration同使用的例子:
@Configuration
public class AppConfig {
/**默认情况下,该bean的id名称为:transferService(方法名),返回值为:TransferServiceImpl
不过也可以自定义名称,比如自定义为:myTransferService*/
// @Bean("myTransferService")
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
等效于:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
@ bean注释的方法甚至可以有任意数量的参数,用于描述构建该bean所需的依赖项。比如transferService依赖AccountRepository ,如下:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
Spring默认bean作用域为单例,但是也可以覆盖自定义,如下:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
在bean命名上,也允许多个别名存在:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
bean允许添加描述:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
用@Configuration注释类表明它的主要用途是作为bean定义的源。此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean间的依赖关系。最简单的@Configuration类如下所示:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上述等价于XML的配置如下:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
所有的@Configuration类在启动时都是用CGLIB子类化的。
CGLIB在启动时会动态地添加特性,因此对于非final的配置类存在一些限制。然而,从4.3开始,配置类上允许使用任何构造函数,包括使用@Autowired或单个非默认构造函数声明来进行默认注入。
如果想避免cglib进行强加的限制,可以考虑在non-@Configuration类上声明@Bean方法(例如,在普通的@Component类上声明)。然后@Bean方法之间的跨方法调用不会被拦截,因此必须在构造函数或方法级别上完全依赖依赖注入。
(1)@Import注解的使用
@Import可以将其他配置加载到当前配置类中,比如:
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
//配置类A被加载到配置B中
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
容器实例化时,只加载配置B,就可以使用Bean A和Bean B:简化了配置加载
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
一个稍微复杂点的例子(涉及跨配置类的bean引用):
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
被@Configuration标注的配置类也只是容器中的一个bean,这也就意味着其他Bean能用的@Autowired 和@Value,@Configuration标注类也可以使用/被使用。
@Configuration
public class ServiceConfig {
//@Configuration标注类被@Autowired“使用”,同时ServiceConfig是一个@Configuration标注类,它使用到了“@Autowired”
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
// navigate 'through' the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
即使@Configuration类是配置容器的主要机制,但或多或少还是会使用到一些XML配置文件。注解@ImportResource的使用,可以将XML配置文件纳入到容器实例化过程中。下面给的就是一个使用到:@Configuration配置类,@ImportResource注解将XML引入配置类的例子:
@Configuration配置类
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties文件
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
启动main方法:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}