@Bean注解用于方法上,返回的实例将由Spring IOC管理,当在@Configuration注解的类中使用@Bean注解时,@Bean相当于
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上述代码等价于:
当我们在非@Configuration的类中使用@Bean时,每个被@Bean注解的方法都相当于一个工厂方法,每次都会返回一个新的实例,原因是非@Configuration注解的类在使用@Bean注解时没有使用CGLIB代理,不会被容器拦截,因此不会查找容器中是否已经存在实例化的bean,直接进行实例化
AnnotationConfigApplicationContext可以解析Spring以及JSR-330的注解,对于ClassPathCmlApplicationContext来说,XML文件作为构造函数的参数,对于AnnotationConfigApplicationContext来说,使用@Configuration、@Component、JSR-330的类可以作为构造函数的参数,其余的使用方式与ClassPathCmlApplicationContext一致,AnnotationConfigApplicationContext会将@Configuration注解的类解析为BeanDefinition,其内部用@Bean注解的方法也将被注册为BeanDefinition
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
AnnotationConfigApplicationContext可以通过无参构造函数实例化,此时可以通过register()方法注册BeanDefinition,需要用refresh()方法刷新容器,以便完成注册
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();
}
有三种方式启动spring的自动组件扫描:
1、使用@ComponentScan注解
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
2、使用xml
3、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);
}
@Bean注解作用于方法上,支持设置
@Bean注解可以在@Component、@Configuration注解的类中使用,也可以在普通的类中使用(不建议)
默认情况下,创建的bean的id和@Bean注解的method的名字一样
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
上述例子创建的bean的id为transferService
也可以用@Bean修饰返回接口类型的方法:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
容器接收到这个bean时,会认为它是TransferService类型,假设有某个Bean依赖于TransferServiceImpl,在实例化这个bean时,容器才会意识到名为transferService的bean的bean,其类型为TransferServiceImpl,对于某个继承了许多接口的类来说,实例化该bean的方法的返回类型应该尽可能精确,至少和依赖注入点的类型相同
通过@Bean注解实例化的bean也可以具有依赖,将依赖作为方法的参数即可:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
任何使用了@Bean注解的方法生成的bean都支持常规的生命周期回调,也可以使用@PostConstruct、@PreDestroy注解,@Bean注解支持指定initialization和destruction生命周期回调:
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();
}
}
默认情况下,bean的close或是shutdown方法在destruction回调时将会被自动调用,如果我们不想调用,可以使用@Bean(destroyMethod="")
使用@Scope注解
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
生命周期不一致的情况下可以考虑使用代理,@Bean注解也可以使用代理,通过设置ScopedProxyMode即可,默认情况下是不使用代理(ScopedProxyMode.No),也可以指定ScopedProxyMode.TARGET_CLASS
(CGLIB代理)或是 ScopedProxyMode.INTERFACES(JDK代理)
@Bean注解提供了name属性用于设置bean的名字:
@Configuration
public class AppConfig {
@Bean(name = "myFoo")
public Foo foo() {
return new Foo();
}
}
有时候可以给定一个bean多个名字:
@Configuration
public class AppConfig {
@Bean(name = { "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 Foo foo() {
return new Foo();
}
}
@Configuration用于类上,相当于xml中的
@Bean注解的bean依赖于其他bean时,可以通过下列方式获得容器管理的依赖bean:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
这种声明bean间依赖关系的方法只在@Bean方法在@Configuration类中声明时有效。不能使用纯@Component类声明bean间依赖关系(获得不是容器管理的bean,而是新的依赖bean)
@Confiuration注解的类会在启动时成为CGLIB的子类,即使用CGLIB代理,在获取bean时,首先会检查容器中是否有现成的bean,如果没有,则会调用代理的方法实例化bean,否则,直接进行依赖注入
@import注解允许从其他的类中导入@Bean定义:
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
使用时,只需要在AnnotationConfigApplicationContext中注册ConfigB即可:
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);
}
@import注解也支持导入普通的java类,并将其声明成一个bean
@Configuration注解的类会被注册成bean,也可以使用@Autowired、@Value注解,由于@Configuration注解的类会比较早初始化,因此在其中使用@Autowired、@Value注解可能会导致某些类提前初始化,对于BeanPostProcessor和BeanFactoryPostProcessor类,由于需要比所有bean都先初始化,一般会用@Bean注解静态方法生成实例
可以使用@Lazy实现懒加载,@DependsOn用于指定依赖项
有时根据系统环境,我们需要选择启动或是关闭某些@Configuration注解的类,可以使用@Profile或是@Conditional注解
xml配置有时能完成java注解无法完成的事情,两者使用的Application不同,我们可以结合两者一起使用
一、如果我们打算使用ClassPathXmlApplicationContext,可以在xml中的
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
system-test-config.xml
jdbc.properties:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
由于xml配置了识别注解,因此@Configuration注解会被识别出来,从而注册其中的bean,由于@Component注解是@Configuration注解的元注解,因此在xml中使用
二、如果我们使用AnnotationConfigApplicationContext,可以通过@ImportResource注解引入xml配置
@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
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}