spring学习(十一)——spring官方文档阅读(5.0.7)——spring的@Bean与@Configuration注解

@Bean与@Configuration注解

@Bean注解用于方法上,返回的实例将由Spring IOC管理,当在@Configuration注解的类中使用@Bean注解时,@Bean相当于元素,@Configuration相当于元素

@Configuration
public class AppConfig {

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

上述代码等价于:


    

 

当我们在非@Configuration的类中使用@Bean时,每个被@Bean注解的方法都相当于一个工厂方法,每次都会返回一个新的实例,原因是非@Configuration注解的类在使用@Bean注解时没有使用CGLIB代理,不会被容器拦截,因此不会查找容器中是否已经存在实例化的bean,直接进行实例化

 

使用AnnotationConfigApplicationContext初始化Spring容器

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注解作用于方法上,支持设置元素的某些属性,例如init—method、destory—method、autowired以及name

@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="")

 

指定bean的生命周期 

使用@Scope注解

@Configuration
public class MyConfiguration {

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

生命周期不一致的情况下可以考虑使用代理,@Bean注解也可以使用代理,通过设置ScopedProxyMode即可,默认情况下是不使用代理(ScopedProxyMode.No),也可以指定ScopedProxyMode.TARGET_CLASS (CGLIB代理)或是 ScopedProxyMode.INTERFACES(JDK代理)

 

指定bean的名字

@Bean注解提供了name属性用于设置bean的名字:

@Configuration
public class AppConfig {

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

 

指定bean的别名

有时候可以给定一个bean多个名字:

@Configuration
public class AppConfig {

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

bean的描述

有时候我们会对bean进行一些功能的描述:

@Configuration
public class AppConfig {

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

 

使用@Configuration注解

@Configuration用于类上,相当于xml中的元素,通过public、@Bean注解的函数来声明bean

@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注解

@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注解

 

结合java注解和xml配置

xml配置有时能完成java注解无法完成的事情,两者使用的Application不同,我们可以结合两者一起使用

 

一、如果我们打算使用ClassPathXmlApplicationContext,可以在xml中的标签描述@Configuration注解的类,并且使用

@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中使用 也可以扫描到@Configuration注解

 

二、如果我们使用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);
    // ...
}

你可能感兴趣的:(spring)