Spring4.x 笔记(5):Bean 在容器中的装配-注解、Java Config、动态添加

了解 Bean

  1. Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,根据注册表实例化 Bean,装配好 Bean 之间的依赖关系。

  2. Bean 配置信息是 Bean 的元数据信息,主要有4个方面:

  • Bean 的实现类
  • Bean 的属性信息,如数据源的连接数、用户名、密码等
  • Bean 的依赖关系,Spring 根据依赖关系配置完成 Bean 之间的装配
  • Bean 的行为配置,如生命周期范围、生命周期各过程的回调函数等
  1. Bean 元数据信息在 Spring 容器中由 org.springframework.beans.factory.config.BeanDefinition 接口形成的 Bean 注册表,Spring 支持多种形式的 Bean 的配置方式。基于XML配置、基于注解配置、基于 Java Config 类配置、基于Croovy 动态语言配置

基于注解的配置

了解注解

实现的前提
  1. 需要配置注解 Bean 的扫描器,如xml配置的扫描器
<context:component-scan base-package="com.learning.spring.ioc.bean">
注解了解
  1. @Component:标注一个Bean,可以设置名称,默认是类名首字母小写。
  2. @Service、@Repository、@Controller 三个内部组合了 @Component,为了区分不同 Bean 的功能
@Component(value = "annotationBean")
public class AnnotationBean {}
依赖注入
  1. 依赖注入的注解:标注在属性或者setXXX方法都生效
  2. @javax.annotation.Resource:默认按名字属性,如果属性为空,指定采用变量名或者方法名
  3. @org.springframework.beans.factory.annotation.Autowired:默认按类型,如果要使用按名字,需要添加注解 @Qualifier(value = "名字")
  4. Autowired 标注集合:spring 会将容器中所有类型为实现了某个接口的 Bean 注入集合变量。默认加载的顺序是不固定的,可以使用 @Order 注解实现加载顺序(见下面例子)
  5. Autowired 支持Spring4X的泛型的注入,集合标注(list、map)等,功能比 Resource 更强大

自动扫描器详解

XML(Schema) 配置:context:component-scan
  1. 标签的属性
属性与子标签描述
base-package:标注基本的需要扫描的路径,默认是该路径下全部的
resource-pattern:对base-package路径下进行过滤,默认是基类包中的所有类,其值为 “**\/*.class”
use-default-filters:默认 true,表示默认会对标注了 @Component,@Service、@Repository、@Controller 的Bean 进行扫描。如需要使用 context:include-filter ,需要设置默认的这个属性为 false
include-filter:子标签。要包含的目标类;可以设置多个
exclude-filter:子标签。要排除的目标类。可以设置多个
  1. include-filter、exclude-filter 的 type 属性
属性描述
annotation标注了 XXannotation 的类(最常用)
assignable标记继承扩展类
aspectj采用 aspectj 表达式
regex正则表达式
custom自定义实现,必须实现 org.springframework.core.type.filter.TypeFilter 接口
注解配置:ComponentScan
  1. Java Config 的配置方法会大量使用该注解,如使用 SpringBoot
  2. 这个注解标注了 @Repeatable 元注解,可以多次配置使用,参考Java 进阶注解的相关博文
@ComponentScan(basePackages = {"com.learning.spring.ioc.bean"},
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
延迟加载、作用范围、生命过程方法
  1. @Lazy:对延迟依赖的支持。在容器启动的时候,对于 Bean 上面标注了 @Lazy、@Autowired 注解的属性,不会立即注入属性值,而是延迟到调用此属性的时候才会注入属性值。注意:@Lazy 必须标注在属性及目标 Bean 两个地方
  2. @Scope:作用范围
  3. @PostConstruct、@PreDestroy:生命过程方法,与 init-method、destroy-method 属性指定方法功能一致
代码示例
  1. Bean 实现类
@Component(value = "annotationBean")
@Getter
public class AnnotationBean {
    @Autowired
    @Qualifier(value = "injectionBean")
    private AnnotationInjectionBean injectionBean;

    void done() {
        injectionBean.done();
    }

    /**
     * spring 会将容器中所有类型为实现了 InjectionBean 接口的Bean 注入这个变量
     * 默认加载的顺序是不固定的,可以使用 @Order 注解实现加载顺序
     */
    @Autowired(required = false)
    private List<InjectionBean> list;

    /**
     * spring 会将实现了 InjectionBean 接口的Bean 注入Map
     * key 为 Bean 的名字
     * value 为所有实现了 InjectionBean 的 Bean
     */
    @Autowired(required = false)
    private Map<String, InjectionBean> map;

    // @Lazy 需要生效,属性这边需要标注,类那边也需要标注
    @Autowired
    @Lazy
    private AnnotationInjectionBean3 annotationInjectionBean3;

}

interface InjectionBean {
    void done();
}

@Component(value = "injectionBean")
@Order(value = 1)
class AnnotationInjectionBean implements InjectionBean {

    public AnnotationInjectionBean() {
        System.out.println("AnnotationInjectionBean 被实例化了");
    }

    @Override
    public void done() {
        System.out.println("调用了 AnnotationInjectionBean.done() 方法");
    }
}

@Component(value = "injectionBean2")
@Order(value = 2)
class AnnotationInjectionBean2 implements InjectionBean {

    public AnnotationInjectionBean2() {
        System.out.println("AnnotationInjectionBean2 被实例化了");
    }

    @Override
    public void done() {
        System.out.println("调用了 AnnotationInjectionBean2.done() 方法");
    }
}

// @Lazy 需要生效,类这边需要标注,属性那边也需要标注,
@Component(value = "injectionBean3")
@Order(value = 3)
@Lazy
class AnnotationInjectionBean3 implements InjectionBean {

    public AnnotationInjectionBean3() {
        System.out.println("AnnotationInjectionBean3 被实例化了");
    }

    @Override
    public void done() {
        System.out.println("调用了 AnnotationInjectionBean3.done() 方法");
    }

    /**
     * 注解可以标注多个方法
     */
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct 注解标注初始化方法");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("@PreDestroy 注解标注销毁方法");
    }

}
  1. xml 装配自动扫描
<!--扫描注解定义的Bean-->
<context:component-scan base-package="com.learning.spring.ioc.bean" resource-pattern="**/*.class" use-default-filters="true"/>

  1. 简单测试
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");

AnnotationBean annotationBean = context.getBean("annotationBean", AnnotationBean.class);
System.out.println(annotationBean.getList());
System.out.println(annotationBean.getMap());

基于Java Config 的配置

提供 Bean 的定义

了解Java Config 的配置
  1. @Configuration:普通的类只需要标注 @Configuration 注解,就可以为 Spring 容器提供 Bean 定义的信息
  2. @Bean:每个标注了@Bean 的类方法都相当于提供一个 Bean 的定义信息。
  • Bean 的类型为方法返回值类型
  • Bean 的名字默认为方法名,可以通过 name 属性定义
  • Spring 会被Bean 标注的方法进行 aop 增强。调用获取 @Bean 标注的方法,不是简单的运行方法的逻辑,而是从容器中获取 Bean
  • 在 @Bean 标注的方法处,还可以标注 @Scope 控制 Bean 的生命周期
  1. 多个 @Configuration 配置类中的Bean,如要相互引用?
  • @Configuration 组合了注解 @Component,本身也是一个Bean,可以直接注入,然后使用
  1. Java Config 与 Xml 配置相互引用
  • Java Config 引用 Xml:
@ImportResource("classpath:bean-java-config.xml")
  • Xml 引用 Java Config
<context:component-scan base-package="com.learning.spring.ioc.bean.jbased" resource-pattern="AppConfig.class"/>
  1. @Import 注解组装多个 @Configuration 标注的配置,启动容器的时候只需要存入组装后的。
代码示例
@Configuration
@Import(AppInjectionConfig.class)
@ImportResource("classpath:bean-java-config.xml")
public class AppConfig {

    //  @Configuration 组合了注解 @Component,本身也是一个Bean,可以直接注入,然后使用
    @Autowired
    private AppInjectionConfig appInjectionConfig;

    /**
     * Bean 的类型为返回值类型
     * Bean 的名字默认为方法名,可以通过 name 属性定义
     *
     * @return
     */
    @Bean
    public ConfigBean configBean() {
        ConfigBean configBean = new ConfigBean();
        // Spring 会被Bean 标注的方法进行aop增强
        // 调用 configInjectionBean() 不是简单的运行方法的逻辑,而是从容器中获取 Bean
        configBean.setConfigInjectionBean(appInjectionConfig.configInjectionBean());
        return configBean;
    }

}


@Setter
class ConfigBean {

    private ConfigInjectionBean configInjectionBean;

    public void done() {
        System.out.println("调用了 ConfigBean.done 方法");
    }

    public void injection() {
        System.out.println("调用了 ConfigBean.injection 方法");
        configInjectionBean.injection();
    }
}

// 被注入的 @Configuration 标注配置
@Configuration
public class AppInjectionConfig {

    @Bean
    public ConfigInjectionBean configInjectionBean() {
        return new ConfigInjectionBean();
    }
}

class ConfigInjectionBean {
    public void injection() {
        System.out.println("调用了 ConfigInjectionBean.injection 方法");
    }
}


启动 Spring 容器

  1. 使用 AnnotationConfigApplicationContext 启动 Spring 容器
// 也可以使用 @Import(AppInjectionConfig.class) 组装到一个配置类中
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class, AppInjectionConfig.class);
System.out.println("初始化 AnnotationConfigApplicationContext 完成, 同时实例化 bean");

ConfigBean configBean = context.getBean("configBean", ConfigBean.class);
configBean.done();
context.close();

/*
 初始化 AnnotationConfigApplicationContext 完成, 同时实例化 bean
 调用了 ConfigBean.done 方法
 */

通过编码方式动态添加

  1. 实现在Spring 容器启动阶段动态的注入自定义的 Bean,保证动态注入的 Bean 也可以被 AOP 所增强,需要实现 Bean 工厂后置处理器
  2. 编码实现
  • DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory接口,提供了可扩展配置、循环枚举的功能,可以通过此类实现 Bean 的动态注入
  • 可以使用 registerBeanDefinition注册 Bean 定义,也可以使用 registerSingleton 直接注册 Bean 实例
@Component
public class CodeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        // DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory接口
        // 提供了可扩展配置、循环枚举的功能,可以通过此类实现 Bean 的动态注入
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;

        // 通过 BeanDefinitionBuilder 创建 Bean 定义
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CodeBean.class);

        // 注入。设置属性 codeReferenceBean,此属性引用已经定义的 Bean:codeReferenceBean
        builder.addPropertyReference("codeReferenceBean", "codeReferenceBean");

        // 注册 Bean 定义
        factory.registerBeanDefinition("codeBean", builder.getRawBeanDefinition());

        // 也直接注册一个 Bean 实例
        factory.registerSingleton("codeBean2", new CodeBean());

    }
}

class CodeBean {
    public CodeBean() {
        System.out.println("CodeBean 被实例化了~~");
    }
}

@Component
class CodeReferenceBean {
    public CodeReferenceBean() {
        System.out.println("CodeReferenceBean 被实例化了~~");
    }
}

参考

  1. 源码地址

Fork me on Gitee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值