Spring Ioc 基于Java的容器配置

@Bean和@Configuration注解

Spring中基于Java配置的核心内容是@Configuration注解的类和@Bean注解的方法

@Bean注解用于表名方法的实例化,配置和初始化都是由Spring Ioc容器管理的新对象,就像在XML配置的元素一样,开发者可以在任意的Spring @Component中使用@Bean注解方法,但是大多数情况下@Bean是配合@Configuration使用的。

使用@Configuration注解类时,这个类的目的就是作为bean定义的地方。此外,@Configuration类内部的bean可以调用本类定义的其它bean作为依赖。

  1. 使用AnnotationConfigApplicationContext初始化Spring容器

Spring中的AnnotationConfigApplicationContext是Spring3.0总新增的内容,这是一个强大的ApplicationContext实现,它不仅能解析@Configuration注解类,也能解析@Component注解的类和使用JSR-330注解的类。

当使用@Configuration类作为输入时,@Configuration类本身被注册为一个bean定义,类中所有声明的@Bean方法也被注册为bean定义。

当使用@Component或者JSR-330类时,他们被注册为bean定义,并且假定在必要时在这些类中使用DI元数据,比如@Autowried 或 @Inject

Spring以XML作为数据配置实例化一个ClassPathXmlApplicationContext,以@Configuration类作为元数据配置时,Spring以差不多的方式实例化一个AnnotationConfigApplicationContext,因此,Spring容器可以实现零XIML配置

如下:AnnotationConfigApplicationContext不限于只使用@Configuration注解的类,任何添加@Component或JSR-330注解的类都能被提供给这个构造方法

   public static void main(String[] args) {
        //AppConfig是添加了@Configuration或 @Component 或 JSR-330注解的类
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }
  public static void main(String[] args) {
        //假设MyServiceImpl Dependency1和Dependency2使用Spring依赖注入,例如@Autowired
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(MyServiceImpl.class,
                        Dependency1.class,Dependency2.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }

使用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.register(MyServiceImpl.class);
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }

使用scan(String...)扫描组件

要启用组件扫描,只要向下面这样配置@Configuration类即可

    @Configuration
    @ComponentScan(basePackages = "com.acme")
    public class AppConfig{
        
    }

上面的配置等同于如下的xml配置

<beans>
	<context:component-scan base-package="com.acme"/>
</beans>

上面的例子中com.acme包会被扫描,只要使用了@Component注解的类,都会被注册进容器中,同样的AnnotationConfigApplicationContext公开的scan(String,,,)方法也允许扫描类完成同样的功能。需要注意的是,@Configuration类是@Component的元注解的类,所以也会被扫描到

使用AnnonationConfigWebApplicationContext支持Web应用

WebApplicationContext与AnnonationConfigApplicationContext的变种是AnnonationConfigWebApplicationContext配置,这个实现可以用于配置SpringContextLoaderListenerservlet监听器,Spring MVC的DispatcherServlet等等

    2.使用Bean注解

@Bean是一个方法级别注解,他与XIML中的<bean/>元素类似,注解支持<bean/>提供的一些属性,例如 init-method destory-method ,autowiring 和 name

开发者可以在@Configuration类或者@Component类中使用@Bean注解

要申明一个bean,只需要使用@Bean注解方法即可,使用此方法,将会在ApplicationContext内注册一个bean,bean的类型时方法的返回值类型,默认情况下,bean名称将于方法名称相同。

如下:

    @Configuration
    public class AppConfig {

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

同时5.0版本还可以使用接口返回类型申明@Bean方法

    @Configuration
    public class AppConfig {

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

但是。这会将预先类型预测的可见性限制为指定的接口类型(TransferService),然后在实例化受影响的单一bean时,只知道容器的完整类型(TransferServiceImpl)非延迟的单例bean根据他们的申明顺序进行实例化,因此开发者可能会看到不同类型的匹配结果。

Bean之间的依赖

一个使用@Bean注解的方法可以具有任意数量的参数描述构建该bean所需的依赖,例如如果TransferService需要AccountRepository依赖,那么可以通过注入方法参数来实现该依赖

    @Configuration
    public class AppConfig {

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

bean接受生命周期回调

使用Bean注解定义的任意类都支持产常规的生命周期回调,并且可以使用JSR-250的@PostConstruct和@PreDestory注解

注解完全支持常规Spring生命周期回调,如果bean实现了InitializingBean,DisposableBean或者Lifecycle,他们各自的方法将由容器调用。

同样的,标准的*Aware接口,如BeanFactoryAware,BeanNameAware,MessageSourceAware以及ApplicationContextAware等等都完全支持

默认情况下,使用Java Config定义的bean中close方法或者shutdown方法会作为销毁回调而自动调用,若bean中有close,shutdown方法,却不是作为销毁回调使用,通过设置@Bean(destroyMethod="")即可关闭默认的自动匹配销毁回调模式

使用@Configuration注解

@Configuration注解是一个类级别的注解,表明该类将作为bean定义的元数据配置,@Configuration类会将有@Bean注解的公开方法声明为bean

注入内部依赖

@Bean
public Foo foo(){
  return new Foo(bar());
}

查找方法注入

查找方法注入是一种高级功能,开发者应该很少使用,但是在一个单例bean依赖原型作用域bean的场景中,这种方法将会非常有用。

public abstract class CommandManager{
  public Object process(Object commandState){
    Command command=createCommand();
    command.setState(commandState);
    return command.execute();
  }

  protected abstract Command createCommand();
}

使用Java配置支持,开发者可以创建一个CommandManager的子类,其中抽象方法createCommand()方法将会被覆盖,这样就可以让它查找到新的原型command对象。

@Bean
@Scope("propotype")
public AsyncCommand asyncCommand(){
  AsyncCommand command=new AsyncCommand();
  return command;
}

@Bean
public CommandManager commandManager(){
  
  return new CommandManager(){
    protected Command createCommand(){
      return asyncCommand();
    }
  }
}

更多关于Java配置内部工作的信息

下面的实例中,展示了@Bean注解的方法被调用了两次

@Configuration
public class AppConfig{
  @Bean
  public ClientService clientService1(){
    ClientServiceImpl clientService=new ClientServiceImpl();
    clientService.setClientDao(clientDao());
    return clientService;
  }

  @Bean
  public ClientService clientService2(){
    ClientServiceImpl clientService=new ClientServiceImpl();
    clientService.setClientDao(clientDao());
    return clientService;
  }

  @Bean
  public ClientDao clientDao(){
    return new ClientDaoImpl();
  }
}

clientDao()在clientService1()实例中调用一次,在clientService2()中调用一次,由于这个方法创建一个新的ClientDaoImpl实例并返回,通常期望有两个实例(每个服务创建一个实例)这肯定会引起生命周期的问题。

在Spring中,实例化bean默认情况下都是单例作用域的。这就是他的神奇之处,所有@Configuration类在启动时,都会通过CGLIB创建一个子类,在子类中,在调用父类的方法并创建一个新的实例之前,子类中的方法首先会检查是否缓存过该bean实例。

注意这种行为可以根据bean的作用域而变化,这里我们只讨论单例。

实际上还会有一些限制,因为CGLIB是在启动的时候动态额添加这些特性,所以配置类不能是final的,但是从4.3开始,允许在配置类上使用任何构造函数,包括使用@Autowired或一个非默认构造函数声明作为注入。

如果想避免因CGLIB带来的限制,请考虑声明非@Configuration类的@Bean方法,如果在纯的@Component类,这样在@Bean方法之间的交叉方法调用将会被被拦截,此时必须在构造函数或方法级别上进行依赖注入。

bean在配置类之间相互依赖,@Bean方法可以使用任意的参数,用于描述其依赖,现在考虑一个真实的场景,使用多个@Configuration类,每个配置都依赖其它配置中的bean声明。

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

还有另一种方法来实现相同的结果,记住@Configuration类也是容器中的一个bean,这意味着他们可以向任何其它bean一样使用@Autowired和@Value注解。

警告:确保你的注入依赖关系是最简单的类型,@Configuration类会在上下文初始化的早期被处理,所以他的依赖会在早期被初始化,如果可能的话,请像上面那样使用参数化注入。

同样的,对于通过@Bean声明的BeanPostProcessor和BeanFactoryPostProcessor请谨慎对待,这些通常应该声明为“静态的@Bean”方法,不会触发包含他们的配置类的实例化,否则@Autowired和@Value在配置类本身上是不起作用的,因为他们过早的被实例化。

有条件的包含@Configuration类或@Bean方法

根据一些任意的系统状态,有条件的启用或禁用一个完整的@Configuration类,甚至单个的@Bean方法通常都是有用的,一个常见的例子是,只有在Spring环境中启用了特定的配置文件时,才使用@Profile注释来激活bean。

@Profile注解实际上是使用一个更灵活的注解--@Conditional实现的。@Conditional注解表示特定的Condition实现,她表明@Bean被注册之前会先询问@Condition注解。

Condition接口的实现只有一个返回true或false的matches(...)方法

基于@Configuration混合XML的@ImportResource

在@Configuration类为配置容器的主要方式的应用程序中,也需要一些XML的配置,在这种情况下,只需要使用@ImportResource,并自定义所需的XML,这样做实现了一种以Java为主,的方法来配置容器并将尽可能少的使用XML。

@Configuration
@ImportResource("classpath:/com/../properties-config.xml")
public class AppConfig{
  @Value("${jdbc.url}")
  private String url;

  @Value("${jdbc.password}")
  private String password;
 
  @Bean
  public DataSource datasource(){
    return new DriverManagerDataSource(url,password);
  }
}
<beans>
  <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

jdbc.properties

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.password=123




















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值