Spring高级篇- BeanPostProcessor和BeanFactoryPostProcessor

Spring提供了BeanPostProcessor、BeanFactoryPostProcessor两大类后置处理器

  • BeanPostProcessor:属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能。@Autowired、@Resource、@Value 等注解的解析由不同的 Bean后处理器来完成。
  • BeanFactoryPostProcessor:主要就是补充了一些 bean 定义。@ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能,这些扩展功能由不同的 BeanFactory 后处理器来完成。

Bean后置处理器

后置处理器功能
AutowiredAnnotationBeanPostProcessor解析 @Autowired 与 @Value
CommonAnnotationBeanPostProcessor解析 @Resource、@PostConstruct、@PreDestroy
ConfigurationPropertiesBindingPostProcessor解析 @ConfigurationProperties

案例演示

准备Bean

public class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.debug("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
    }

    @Autowired
    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3) {
        log.debug("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        log.debug("@Value 生效: {}", home);
        this.home = home;
    }

    @PostConstruct
    public void init() {
        log.debug("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy() {
        log.debug("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
               "bean2=" + bean2 +
               ", bean3=" + bean3 +
               ", home='" + home + '\'' +
               '}';
    }
}

public class Bean2 {
}

public class Bean3 {
}

@ConfigurationProperties(prefix = "java")
public class Bean4 {

    private String home;

    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
               "home='" + home + '\'' +
               ", version='" + version + '\'' +
               '}';
    }
}

测试

public static void main(String[] args) {
        //GenericApplicationContext 是一个【干净】的容器, 不会注入后置处理器
        GenericApplicationContext context = new GenericApplicationContext();

        //用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        //@Value("${JAVA_HOME}") String home 作为方法参数解析 需要添加这个处理
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(
                new ContextAnnotationAutowireCandidateResolver());
        //解析 @Autowired @Value
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

        //解析 @Resource @PostConstruct @PreDestroy
        context.registerBean(CommonAnnotationBeanPostProcessor.class);

        //解析 @ConfigurationProperties
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

        //初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例
        System.out.println(context.getBean(Bean1.class));
        System.out.println(context.getBean(Bean4.class));
        //销毁容器
        context.close();
    }

@Autowired bean 后置处理器运行分析

public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //这样注册的bean不会进行依赖注入,初始化的过程
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        //不加的时候解析@Value的信息会报错
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        // 执行依赖注入 @Autowired @Value
        processor.postProcessProperties(null, bean1, "bean1");
        System.out.println(bean1);
    }
}

在这里插入图片描述
查看postProcessProperties源码,调用了InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);使用反射获取里面的信息,发现使用@Value @Autowired 的成员变量,方法参数信息都被封装到了InjectionMetadata对象里面。

Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod(
                "findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);
        // 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
        InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor,
                "bean1", Bean1.class, null);
        System.out.println(metadata);

通过上面的可以知道:AutowiredAnnotationBeanPostProcessor执行依赖注入的时候

  1. 调用postProcessProperties方法,找到标注为@Value @Autowired 的成员变量和方法
  2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值

    metadata.inject(bean1, “bean1”, null);

@Value的值没有解析

查看日志发现JAVA_HOME的信息没有被注入到home中,这是因为容器少加了一个解析器

// ${} 的解析器
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

InjectionMetadata内部处理机制

  1. InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
  2. 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找
//加了 @Autowired成员变量的依赖注入
Field bean3 = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
Object o = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(o);

//加了 @Autowired方法的依赖注入
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 =
        new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(o1);

//加了 @Value的依赖注入
Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o2);

BeanFactory后置处理器

后置处理器功能
ConfigurationClassPostProcessor可以解析@ComponentScan、@Bean、@Import、@ImportResource
MapperScannerConfigurer可以解析Mapper 接口

案例演示

pom文件

<dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

定义Bean

public class Bean1 {

    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    public Bean1() {
        log.debug("我被 Spring 管理啦");
    }
}

@Configuration
@ComponentScan("com.javaming.study.spring.heima.a05.component")
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

}

//component目录下定义的类
@Component
public class Bean2 {

    private static final Logger log = LoggerFactory.getLogger(Bean2.class);

    public Bean2() {
        log.debug("我被 Spring 管理啦");
    }
}

@Controller
public class Bean3 {

    private static final Logger log = LoggerFactory.getLogger(Bean3.class);

    public Bean3() {
        log.debug("我被 Spring 管理啦");
    }
}

测试发现:只打印了config一个bean,Bean1、Bean2这些类都没有注入。

public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        //初始化容器
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        //销毁容器
        context.close();
    }

容器中注册ConfigurationClassPostProcessor后,

// @ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);

在这里插入图片描述

如何增加@Mapper的扫描

@Mapper
public interface Mapper1 {
}

// @MapperScanner 指定扫描的包
context.registerBean(MapperScannerConfigurer.class, bd -> {
   bd.getPropertyValues().add("basePackage", "com.javaming.study.spring.heima.a05.mapper");
});

在这里插入图片描述
发现除了扫描到的@Mapper文件,还会帮我们加上其他的后置处理器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值