@Configuration

​ Spring3.0提供了Java配置管理。
​ Spring3.0为不喜欢使用XML来配置管理Bean的开发者提供了另外一种管理方式,即:使用Java类进行配置管理。
​ 假如有一个类 Chinese 实现了Person 接口,并含有两个成员变量 String name、Axe axe,此 Chinese Bean 如果通过配置文件注入,格式如下:

<bean id="chin"class="com.yuand.Chinese">
	<property name="name" value="zhangsan"/>
	<property name="axe"ref="steelAxe"/>
</bean>
<bean id="steelAxe" class="com.yuand.SteelAxe"/>

如果开发者不喜欢使用XML配置文件,Spring3.0允许开发者使用Java类进行配置。
上面的XML配置文件可以替换成如下Java配置类。
import org.springframework.beans.factory.annotation.Value:
import org.springframework.context.annotation.Bean;

@Configuration
public class AppConfig {

    @Bean(name = "bbb")
    public B bb(){
        B b = new B();
        b.setBName("zhangsan");
        return b;
    }

    @Bean(name = "aaa")
    public A aa(){
        A a = new A();
        a.setAName("lisi");
        a.setB(bb()); //ref
        return a;
    }
}
<context:annotation-config/>
<bean class="com.origin.d702.config.AppConfig"/>

​ 上面的配置文件中使用了Java配置类的三个常用的Spring的Annotation

  • @Configuration:用于修饰一个Java配置类。
  • @Bean用于修饰一个方法,将该方法的返回值定义成容器中的一个Bean。
  • @Value:用于修饰一个Field,用于为该Field配置一个值,相当于配置一个变量。

​ 一旦使用Java配置类来管理Spring容器中的Bean及其依赖关系,此时就需要使用如下方式来创建Spring容器:

ApplicationContext ctx = new  AnnotationConfigApplicationContext(AppConfig.class)

​ AnnotationConfigApplicationContext类会根据Java配置类来创建Spring容器。该类还提供了一个register方法用于添加Java配置类。
​ 使用Java配置类时,还有如下常用的Annotation。

  • @Import:修饰一个Java配置类,用于向当前Java配置类中导入其他Java配置类。
  • @Scope:用于修饰一个方法,指定该方法返回的对应的Bean的生命周期。
  • @Lazy:用于修饰一个方法,指定该方法返回的对应的Bean是否需要延迟初始化。
  • @DependsOn:用于修饰一个方法,指定在初始化该方法对应的Bean之前,先初始化指定的Bean

Bean的作用域

​ 当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用
域。Spring支持如下5中作用域。

  • singleton:单例模式,在整个SpringloC容器中,singleton作用域的Bean将只生成一个实例。
  • prototype:每次通过容器的 getBean方法获取 prototype作用域的Bean时,都将生成一个新的Bean实例。
  • request:对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求Bean,得到的是同一个实例。只有在Web应用中使用Spring时,该作用域才有效。session:对于一次HTTP会话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP会话内,程序每次请求该Bean,得到的是同一个实例。只有在Web应用中使用Spring时,该作用域才有效。
  • global session:每个全局的HTTPSession对应一个Bean实例。只有在Web应用中使用Spring时,该作用域才真正有效。

​ 比较常用的是singleton和 prototype两种作用域,对于singleton作用域的 Bean,每次请求该 Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为:如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new关键字创建Bean实例,创建完成后就不再跟踪、维护。如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申
请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建完成可以反复使用。因此尽量避免将Bean设置成 prototype作用域。

​ Spring配置文件通过 scope属性指定 Bean的作用域.该属性可以接收 singleton、prototype、request、session和globalSession五个值,分别代表上面的5个作用域。

bean后置处理器

​ Spring框架提供了很好的扩展性,除了可以与各种第三方框架良好整合外,其loC容器也允许开发者进行扩展,这种扩展甚至无须实现BeanFactory或ApplicationContext接口,而是允许通过两个后处理器对loC容器进行扩展。Spring提供了两种常用的后处理器。

  • Bean后处理器:这种处理器会对容器中的Bean进行后处理,对Bean进行额外加强。
  • 容器后处理器:这种后处理器对loC容器进行后处理,用于增强容器功能。

bean后处理器

​ BeanPostProcessor是Bean级处理器,用于在bean实例化后、初始化后自定义修改bean实例,如属性校验、针对自定义bean做统一处理等。
​ BeanPostProcessor接口中定义了两个方法:

  • postProcessBeforelnitialization方法,在任何bean初始化回调「如InitializingBean接口的afterPropertiesSet方法、自定义的初始化方法」之前,使BeanPostProcessor应用于给定的bean实例,其返回值可以是bean本身或其包装类。
  • postProcessAfterInitialization方法,在 bean回调初始化方法后调用。

Spring中Bean的实例化过程图示:

图片

@Component
@Data
public class MyBean {
    private String myProperty = "";
    public void doSomething() {
        System.out.println("MyBean.doSomething()执行,myProperty="+myProperty);
    }
}
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean , String beanName) throws BeansException {
        if (bean instanceof MyBean){
            MyBean myBean = (MyBean)bean;
            myBean.setMyProperty(myBean.getMyProperty()+"自定义属性-before");
        }
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean , String beanName) throws BeansException {
        if (bean instanceof MyBean){
            MyBean myBean = (MyBean)bean;
            myBean.setMyProperty(myBean.getMyProperty()+"自定义属性-after");
        }
        return bean;
    }
}
<bean class="com.origin.d702.MyBeanPostProcessor"/>

注意
1、接口中的两个方法都要将传入的bean返回,而不能返回null、如果返回的是null那么我们通过getBean方法将得不到目标。
2、BeanFactory和ApplicationContext 对待 bean后置处理器稍有不同。ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它,因此部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean后置处理器必须通过代码显式地去注册,在loC容器继承体系中的ConfigurableBeanFactory接口中定义了注册方法。
另外,不要将BeanPostProcessor标记为延迟初始化。因为如果这样做,Spring容器将不会注册它们,自定义逻辑也就无法得到应用。假如你在元素的定义中使用了’default-lazy-init’属性,请确信你的各个BeanPostProcessor 标记为’lazy-init=”false”。

BeanPostProcessor实际应用分析

  • 属性注入:可以通过实现BeanPostProcessor接口,在Bean实例化后、初始化之前,对Bean的属性进行自定义注入。例如,可以在BeanPostProcessor的postProcessBeforelnitialization方法中,对Bean的属性进行修改或赋值,从而实现属性注入的定制化需求。
  • AOP预处理:可以通过实现BeanPostProcessor接口,在Bean实例化后、初始化之前,对Bean进行AOP相关的预处理。例如,可以在BeanPostProcessor的postProcessBeforelnitialization方法中,为Bean动态生成代理对象,实现AOP面向切面的功能。
  • 定制初始化逻辑:可以通过实现BeanPostProcessor接口,在Bean初始化阶段,对Bean进行自定义的初始化逻辑。例如,可以在BeanPostProcessor的postProcessAfterInitialization方法中,执行一些初始化操作,例如数据初始化、资源加载等。
  • 数据校验:可以通过实现BeanPostProcessor接口,在Bean初始化后,对Bean中的数据进行校验。例如,可以在BeanPostProcessor的postProcessAfterInitialization方法中,对 Bean中的数据进行验证,确保数据的合法性和完整性。
  • 资源回收:可以通过实现BeanPostProcessor接口,在Bean销毁前,对Bean中的资源进行回收操作。例如,可以在BeanPostProcessor的postProcessBeforeDestruction方法中,执行资源的释放、关闭等操作

容器后置处理器

​ Spring还提供了一种容器后处理器,容器后处理器负责处理容器本身。容器后处理器必须实现BeanFactoryPostProcessor接口,该接口声明了如下一个方法:

  • postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

​ 该方法由Spring容器自动调用,在此方法中可以对Spring容器进行自定义扩展,也可以对Spring容器不做任何处理。

​ 同BeanPostProcessor类似,ApplicationContext可自动检测到容器中的容器后处理器,并且自动注册容器后处理器。但如果使用BeanFactory作为Spring容器、则必须手动调用该容器后处理器来处理BeanFactory容器。
​ 实现了BeanFactoryPostProcessor接口的容器后处理器不仅可以对BeanFactory执行后处理,也可以对ApplicationContext容器执行后处理。容器后处理器还可以用来注册额外的属性编辑器。

​ Spring提供了如下几个常用的容器后处理器:

  • PropertyPlaceholderConfigurer属性占位符配置器。
  • PropertyOverrideConfigurer重写占位符配置器。
  • CustomAutoWrieConfigurer:自定义自动装配的配置器。
  • CustomScopeConfigurer:自定义作用域的配置器。

​ 容器后处理器通常用于对于Spring容器进行处理,并且总是在容器实例化其他Bean之前,读取配置文件的元数据,也可以修改这些元数据。

​ 在Spring的配置文件中可以配置多个容器后处理器,在每个容器后处理器中可使用order属性来控制容器后处理器的执行次序。容器后处理器的作用域范围是容器级,它只能对容器本身进行处理,而不对容器中的Bean 进行处理。

属性占位符配置器

​ PropertyPlaceholderConfigurer是一个容器后处理器,负责读取属性文件里的属性值,并将这些属性设置成Spring配置文件的数据。例如配置数据库连接,或者配置数据库连接池,只需要将特定的信息,比如连接数据库的URL、用户名、密码等放在特定的属性文件中,当切换数据库时,只需要改属性文件即可,则无需改Spring 文件。

重写占位符配置器

​ PropertyOverrideConfiqurer是Spring提供的另一个容器后处理器,这个容器后处理器比PropertyPlaceholderConfigurer更加强大PropertyOverrideConfigurer的属性文件指定的信息可以直接覆盖Spring配置文件中的元数据。
​ 举例说明:假如 BeanA中有个属性name,Spring容器为其注入的值为“zhangsan”.PropertyOverrideConfigurer的属性文件中 name 的值为“lisi”,那么属性文件中的 name属性会覆盖掉 Spring 配置文件中注入的值。使用PropertyOverrideConfigurer的属性文件,每条属性应该使用如下格式:beanld.property=value针对于下面的work示例,属性文件中的书写格式为:worker.age=55 岁beanld是属性占位符试图覆盖的Bean的id,property是试图覆盖的属性的属性名。

注解配置bean类

搜索 bean类

​ 既然不再使用Spring配置文件来配置任何Bean实例,那么只能由Spring自动搜索某些路径下的Java类,并将这些Java类注册成 Bean实例。
​ Spring没有采用“约定优于配置”的策略,Spring依然要求程序员显示指定搜索哪些路径下的Java类,Spring将会把合适的Java类全部注册成Spring Bean。问题是:Spring如何知道应该把哪些Java类当成Bean类处理?
​ 这就需要使用Annotation,Spring通过使用一些特殊的Annotation来标注Bean类。如下所示:

  • @Compgnent:标注一个普通的 Spring Bean类。
  • @Controller:标注一个控制器组件类。
  • @Service:标注一个业务逻辑组件类。
  • @Repository:标注一个DAO组件类。

​ 指定了某些类可以作为SpringBean类使用后,最后还需要让Spring搜索这些Bean的路径,此时需要配置
文件中导入context命名空间,并指定一个简单的搜索路径。

		<!-自动扫描指定包及其子包下的所有的Bean类-->
<context:component-scan base-package="com.yuand.scanBean"/>
@Component("xiaohei")
@Data
public class Dog {
    @Value("小狗狗")
    private String dName;
}

指定Bean的作用域

当采用零配置方式来管理Bean实例时,可以使用@ScopeAnnotation来指定Bean实例的作用域。例如如下
Java类代码:

@Scope("prototype")

使用@Resource配置依赖

​ @Resource 位于javax.annotation包下,是来自于Java E规范下的一个Annotation,Spring直接借鉴了该Annotation,通过使用该 Annotation为目标 Bean指定协作者Bean。
​ @Resource有一个name属性,在默认情况下,Spring将这个值解释为需要被注入的的Bean实例的id。

​ pom依赖

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.2</version>
</dependency>
@Component("xiaohei")
@Data
public class Dog {
    @Value("小狗狗")
    private String dName;

//    @Autowired
    @Resource
    private Cat cat;
}

使用@PostConstruct和@PreDestroy定制声明周期行为

​ @PostConstruct 和@PreDestroy 同样位于javax.annotation.包下,也是来自于Java EE规范的两个Annotation.Spring直接借鉴了它们,用于定制Spring容器中Bean的声明周期行为。
​ 前面介绍过 Spring 生命周期时提供了bean元素的两个属性init-method、destory-method,一个是指定Bean初始化后,调用指定的方法增强初始化。一个是指定Spring容器在销毁Bean之前调用的方法,完成一些处理。

@Component
@Data
public class Cat {
    @Value("小猫猫")
    private String cName;

    @PostConstruct
    public void init(){
        System.out.println("初始化...");
    }
    @PreDestroy
    public void destory(){
        System.out.println("销毁...");
    }
}

bean生命周期

五步骤版本

  1. 实例化
  2. 依赖注入
  3. 初始化
  4. 使用Bean
  5. 销毁Bean

七步骤版本

  1. 实例化
  2. 依赖注入
  3. 初始化前 BeanPostProcessor before方法
  4. 初始化
  5. 初始化前 BeanPostProcessor after方法
  6. 使用Bean
  7. 销毁Bean

十步骤版本

  1. 实例化
  2. 依赖注入
  3. BeanNameAwareBeanFactoryAare方法执行啦
  4. 初始化前 BeanPostProcessor before方法
  5. InitialingBean接口的方法执行啦!
  6. 初始化
  7. 初始化前 BeanPostProcessor after方法
  8. 使用Bean
  9. DisposableBean接口的方法执行啦!
  10. 销毁Bean