Day6 每日打卡 -简单可预期

BeanFactory与ApplicationContext的区别与联系

启动类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MSYAk9og-1662393502579)(${graph}/image-20220905001956519.png)]

springBoot引导类:

package com.deng;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class HeimaSpringApplication {

    public static void main(String[] args) {
        // BeanFactory 核心容器
        // ApplicationContext是BeanFactory的实现类,ApplicationContext还实现了其他功能,
        // 也就是说ApplicationContext增强了BeanFactory
        ConfigurableApplicationContext context = SpringApplication.run(HeimaSpringApplication.class, args);
        
        // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
        System.out.println(context.getClass());
    }

}

ConfigurableApplicationContext的实现类为AnnotationConfigServletWebServerApplicationContext

AnnotationConfigServletWebServerApplicationContext间接继承了GenericApplicationContext,在这个类里面可以找到beanFactory作为成员变量出现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FvcpmweW-1662393502580)(${graph}/image-20220905015033230.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9rHwkq2q-1662393502581)(${graph}/image-20220905014403662.png)]

结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRE0fZ6I-1662393502582)(${graph}/image-20220905002757537.png)]

BeanFactory的默认实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZR96sj2e-1662393502582)(${graph}/image-20220905005121319.png)]

package com.deng;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class HeimaSpringApplication {

    public static void main(String[] args) {
        // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
        ConfigurableApplicationContext context = SpringApplication.run(HeimaSpringApplication.class, args);
        // class org.springframework.beans.factory.support.DefaultListableBeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    }
}

​ 这里我们暂且不细看DefaultListableBeanFactory,先看DefaultListableBeanFactory的父类DefaultSingletonBeanFactory,可以看到有个私有的成员变量singletonObjects 存储单例对象

DefaultListableBeanFactory:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UWnNXwC9-1662393502583)(${graph}/image-20220905005255993.png)]

ApplicationContext的功能

ApplicationContext 比 BeanFactory 多点啥?

多实现了四个接口:

MessageSource: 国际化功能,支持多种语言
ResourcePatternResolver: 通配符匹配资源路径
EnvironmentCapable: 环境信息,系统环境变量,.properties、.application.yml等配置文件中的值
ApplicationEventPublisher: 发布事件对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LbSrwLIL-1662393502584)(${graph}/image-20220905143653039.png)]

MessageSource

国际化功能,支持多种语言

public interface MessageSource {
	@Nullable
	String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

	String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}

​ 在resources目录下创建四个文件messages.propertes、messages_en.properties、messages_ja.properties、messages_zh.properties,然后分别在四个文件里面定义同名的key,比如在message_en.properties中定义hi=hello,在messages_zh中定义hi=你好,这样在代码中就可以根据这个key hi和不同的语言类型获取不同的value了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nf4E4sWU-1662393502585)(${graph}/image-20220905145922331.png)]

System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HxJzPCTR-1662393502586)(${graph}/image-20220905150005362.png)]

ResourcePatternResolver

通配符匹配资源路径,返回匹配的资源

public interface ResourcePatternResolver extends ResourceLoader {
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String locationPattern) throws IOException;
}

获取jar包中资源:

//Resource[] resources = context.getResources("classpath:application.properties");
// classes* 可以取jar包下拿取资源
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
    System.out.println(resource);
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xouP62Mq-1662393502587)(${graph}/image-20220905151512155.png)]

EnvironmentCapable

获取环境信息,系统环境变量,.properties、.application.yml等配置文件中的值

public interface EnvironmentCapable {
    Environment getEnvironment();
}

获得java_home和application.properties中的内容

System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("spring.messages.encoding"));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzRnw2W1-1662393502587)(${graph}/image-20220905151651508.png)]

ApplicationEventPublisher

发布事件对象

package org.springframework.context;

@FunctionalInterface
public interface ApplicationEventPublisher {

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	void publishEvent(Object event);

}

定义一个用户注册事件类,继承自ApplicationEvent

package com.deng;
import org.springframework.context.ApplicationEvent;

public class UserRegisteredEvent extends ApplicationEvent {
    public UserRegisteredEvent(Object source) {
        super(source);
    }


发布事件:

context.publishEvent(new UserRegisteredEvent(context));

监听事件:

package com.deng;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class Component1 {

    @EventListener
    public void message(UserRegisteredEvent event) {
        System.out.println("事件处理");
    }
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jqkQd27J-1662393502588)(${graph}/image-20220905153203518.png)]

容器实现

BeanFactory实现的特点

DefaultListableBeanFactory的使用

BeanFactory后置处理器与Bean后置处理器

package com.deng;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.stream.Collectors;

public class TestBeanFactory {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(即bean的一些描述信息,包含class:bean是哪个类,scope:单例还是多例,初始化、销毁方法等)
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        // 注册config的bean定义信息
        beanFactory.registerBeanDefinition("config", beanDefinition);

        // 给 BeanFactory添加一些常用的后处理器(存储到内部容器),让它具备解析@Configuration、@Bean等注解的能力
        // BeanFactory 后处理器主要功能,补充了一些 bean 的定义
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // 从bean工厂中取出BeanFactory的后处理器
        // 比如internalConfigurationAnnotationProcessor 
        // 将解析注解,将bean定义信息添加到beanFactory
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
            System.out.println(beanFactoryPostProcessor);
            // 处理
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });
	
        // 从BeanFactory中取出Bean1,然后再从Bean1中取出它依赖的Bean2
        // 可以看到结果为null,所以@Autowired注解并没有被解析
         Bean1 bean1 = beanFactory.getBean(Bean1.class);
        
        // 要想@Autowired、@Resource等注解被解析,还要添加Bean的后处理器,可以针对Bean的生命周期的各个阶段提供扩展
        // 从bean工厂中取出Bean的后处理器,并且执行这些后处理器
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
        
  		// 提前创建对象
        beanFactory.preInstantiateSingletons();
        // 此时,@Autowired已经被解析
        Bean1 bean = beanFactory.getBean(Bean1.class);
        
        // 打印BeanFactory中Bean
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }

    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

结果:

//  AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);加入的bean
// 也就是后面的5个bean

//internalConfigurationAnnotationProcessor 去解析注解,将bean定义信息注册到BeanFactory中
// BeanFactory本身不具备解析注解的功能
// beanFactory 的后处理
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// bean的后处理器 @Autowire  针对于bean的生命周期
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
// bean的后处理器  @Resource
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory    
    
    
bean1
bean2

BeanFactory后置增强的几个实现类

@ComponentScan,@Bean,@Configuration
class org.springframework.context.annotation.ConfigurationClassPostProcessor
class org.springframework.context.event.EventListenerMethodProcessor

Bean后置增强的几个实现类

@Autowire 
class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
@Resource
class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
BeanFacory的特点
a. beanFactory 不会做的事
    1. 不会主动调用BeanFactory的后处理器
    2. 不会主动添加Bean的后处理器
    3. 不会主动初始化单例
    4. 不会解析BeanFactory,还不会解析 ${}, #{}
b. Bean后处理器会有排序的逻辑
Bean后置处理器顺序

当一个属性同时被@Autowire 与 @Resource修饰时

起决定的是他们对应的Bean后置处理器的加入顺序,早加入的优先级高,就处理。

ApplicaionContext的常见实现与用法

ClassPathXmlApplicationContext:
FileSystemXmlApplicationContext:
AnnotationConfigApplicationContext:
AnnotationConfigServletWebServerApplication:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBwToTrM-1662393502589)(${graph}/bec10309f6c068585fd7382f96ae8d80.png)]

@Slf4j
public class TestApplicationContext {
    @Test
    // 1.最为经典的容器,基于classpath 下 xml 格式的配置文件来创建
    public void testClassPathXmlApplicationContext() {
        // 如果xml中没有<context:annotation-config/> 
        // 则无法解析@Autowire这样的注解
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring_bean.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    @Test
    // 2.基于磁盘路径下 xml 格式的配置文件来创建
    public void testFileSystemXmlApplicationContext() {
        // 可以用绝对路径或者相对路径
        // FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\ideacode\\spring-source-study\\spring_02_02_applicationcontext_impl\\src\\main\\resources\\spring_bean.xml");
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src\\main\\resources\\spring_bean.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    @Test
    // 模拟一下ClassPathXmlApplicationContext和FileSystemXmlApplicationContext底层的一些操作
    public void testMockClassPathAndFileSystemXmlApplicationContext() {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        System.out.println("读取之前");
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println("读取之后");
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        // reader.loadBeanDefinitions("spring_bean.xml");
        // reader.loadBeanDefinitions(new ClassPathResource("spring_bean.xml"));
        reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\spring_bean.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Test
    // 3.较为经典的容器,基于java配置类来创建
    public void testAnnotationConfigApplicationContext() {
        // 会自动加上5个后处理器
        // org.springframework.context.annotation.internalConfigurationAnnotationProcessor
        // org.springframework.context.annotation.internalAutowiredAnnotationProcessor
        // org.springframework.context.annotation.internalCommonAnnotationProcessor
        // org.springframework.context.event.internalEventListenerProcessor
        // org.springframework.context.event.internalEventListenerFactory
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    @Test
    // 4.较为经典的容器,基于java配置类来创建,并且还可以用于web环境
    // 模拟了 springboot web项目内嵌Tomcat的工作原理
    public void testAnnotationConfigServletWebServerApplicationContext() throws IOException {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        // 防止程序终止
        System.in.read();
    }
}

@Configuration
class WebConfig {
    @Bean
    // 1. WebServer工厂
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    @Bean
    // 2. web项目必备的DispatcherServlet
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    // 3. 将DispatcherServlet注册到WebServer上
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }
	// 如果斜杆开头,默认就是他的访问路径	
    @Bean("/hello")
    public Controller controller1() {
        // 这里返回了一个实现Controller接口的类
        return (request, response) -> {
            response.getWriter().println("hello");
            return null;
        };
    }
}

// 单元测试的过程中如果要解析一些Spring注解,比如@Configuration的时候不要把相关类定义到写单元测试类的内部类,会读取不到
@Configuration
class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public Bean2 bean2(Bean1 bean1) {
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1);
        return bean2;
    }
}

class Bean1 {

}

class Bean2 {
    private Bean1 bean1;

    public Bean1 getBean1() {
        return bean1;
    }

    public void setBean1(Bean1 bean1) {
        this.bean1 = bean1;
    }
}

spring-config.xml

// 加入后处理器
<context:annotation-config/> 

Bean的生命周期

生命周期

package com.demo01;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class BeanLifeCycleApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(BeanLifeCycleApplication.class, args);

        context.close();
    }
}

LifeCycleBean:

package com.demo01;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
@Slf4j
public class LifeCycleBean {
    public LifeCycleBean() {
        log.info("构造");
    }

    @Autowired
    public void autowire(@Value("${JAVA_HOME}") String name) {
        log.info("依赖注入:{}", name);
    }

    @PostConstruct
    public void init() {
        log.info("初始化");
    }

    @PreDestroy
    public void destroy() {
        log.info("销毁");
    }
}

MyBeanPostProcessor:

package com.demo01;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    @Override
    // 实例化前(即调用构造方法前)执行的方法
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.info("<<<<<<<<<<< 实例化前执行");
        // 返回null保持原有对象不变,返回不为null,会替换掉原有对象
        return null;
    }

    @Override
    // 实例化后执行的方法
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.info("<<<<<<<<<<< 实例化后执行,这里如果返回 false 会跳过依赖注入阶段");
            // return false;
        }

        return true;
    }

    @Override
    // 依赖注入阶段执行的方法
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.info("<<<<<<<<<<< 依赖注入阶段执行,如@Autowired、@Value、@Resource");
        return pvs;
    }

    @Override
    // 销毁前执行的方法
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean"))
            log.info("<<<<<<<<<<<销毁之前执行");
    }

    @Override
    // 初始化之前执行的方法
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean"))
            log.info("<<<<<<<<<<< 初始化之前执行,这里返回的对象会替换掉原本的bean,如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    // 初始化之后执行的方法
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("lifeCycleBean"))
            log.info("<<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的bean,如 代理增强");
        return bean;
    }
}

结果:

com.demo01.MyBeanPostProcessor  : <<<<<<<<<<< 实例化前执行,
com.demo01.LifeCycleBean        : 构造
com.demo01.MyBeanPostProcessor  : <<<<<<<<<<< 实例化后执行,这里如果返回 false 会跳过依赖注入阶段

    
com.demo01.MyBeanPostProcessor  : <<<<<<<<<<< 依赖注入阶段执行,如@Autowired@Value@Resource
com.demo01.LifeCycleBean        : 依赖注入:D:\java\jdk1.8.0_291

    
com.demo01.MyBeanPostProcessor  : <<<<<<<<<<< 初始化之前执行,这里返回的对象会替换掉原本的bean,如 @PostConstruct@ConfigurationProperties
com.demo01.LifeCycleBean        : 初始化
com.demo01.MyBeanPostProcessor  : <<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的bean,如 代理增强

   
com.demo01.MyBeanPostProcessor  : <<<<<<<<<<<销毁之前执行
com.demo01.LifeCycleBean        : 销毁

模板方法

package com.demo01;

import java.util.ArrayList;
import java.util.List;

public class TestMethodTemplatePattern {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造:" + bean);
            System.out.println("依赖注入:" + bean);
            for (BeanPostProcessor processor : processors) {
                processor.inject(bean);
            }
            System.out.println("初始化:" + bean);
            return bean;
        }

        private List<BeanPostProcessor> processors = new ArrayList<>();

        public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
            processors.add(beanPostProcessor);
        }
    }

    interface BeanPostProcessor {
        void inject(Object bean);
    }
}

Bean后置处理器

后置处理器

​ 定义三个Bean,名称分别为Bean1,Bean2,Bean3,其中Bean1中依赖了Bean2和Bean3,Bean2通过@Autowired的注解注入,Bean3通过@Resource注解注入,再通过@Value注解注入一个Java的环境变量JAVA_HOME的值。最后定义两个方法init()和destroy(),分别加上@PostConstruct和@PreDestroy注解。

package com.demo02;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

@Slf4j
public class Bean1 {
    private Bean2 bean2;

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

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

    private Bean3 bean3;

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

    private String java_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 +
                ", java_home='" + java_home + '\'' +
                '}';
    }
}

public class Bean2 {
}

public class Bean3 {
}


// springboot 中的注解,自动读取配置文件
@ConfigurationProperties(prefix = "java")
@Slf4j
class Bean4 {
    // 读取java.home
    private String home;
    // 读取java.version
    private String version;
}

​ 这里用GenericApplicationContext 来探究一下@Autowired、@Value、@Resource、@PostConstruct、@PreDestroy以及springboot项目中的@ConfigurationProperties这些注解分别是由哪个后处理器来解析的。

​ 注:GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试,这里用DefaultListableBeanFactory也可以完成测试,只是会比使用GenericApplicationContext麻烦一些。

测试:

package com.demo02;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;

@Slf4j
public class TestBeanPostProcessor {
    @Test
    public void testBeanPostProcessor() throws Exception {
        // ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
        // 这里用DefaultListableBeanFactory也可以完成测试,只是会比使用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 注解的解析器
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 添加解析 @Autowired 和 @Value 注解的后处理器
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        // 添加解析 @Resource、@PostConstruct、@PreDestroy 注解的后处理器
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        // 添加解析 @ConfigurationProperties注解的后处理器
        // ConfigurationPropertiesBindingPostProcessor后处理器不能像上面几种后处理器那样用context直接注册上去
        // context.registerBean(ConfigurationPropertiesBindingPostProcessor.class);
        // 需要反着来注册一下
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());


        // ⬇️初始化容器
        context.refresh();
        System.out.println(context.getBean(Bean4.class));

        // ⬇️销毁容器
        context.close();
    }
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yhmqr6a3-1662393502590)(${graph}/image-20220905205110097.png)]

@Autowired处理器

本案例测试代码紧接着上面,这里对Bean1中加了@Autowired注解的属性注入Bean2、方法注入Bean3以及方法注入环境变量JAVA_HOME的过程进行分析。

@Autowired注解解析用到的后处理器是AutowiredAnnotationBeanPostProcessor

  • 这个后处理器就是通过调用postProcessProperties(PropertyValues pvs, Object bean, String beanName)完成注解的解析和注入的功能
  • 这个方法中又调用了一个私有的方法findAutowiringMetadata(beanName, bean.getClass(), pvs),其返回值InjectionMetadata中封装了被@Autowired注解修饰的属性和方法
  • 然后会调用InjectionMetadata.inject(bean1, “bean1”, null)进行依赖注入
    由于InjectionMetadata.inject(bean1, “bean1”, null)的源码调用链过长,摘出主要调用过程进行说明:
  • 成员变量注入,InjectionMetadata注入Bean3的过程:
    • InjectionMetadata会把Bean1中加了@Autowired注解的属性的BeanName先拿到,这里拿到的BeanName就是 bean3,然后再通过反射拿到这个属性,Field bean3Field = Bean1.class.getDeclaredField(“bean3”);
    • 将这个属性封装成一个DependencyDescriptor对象,再去调用Bean3 bean3Value = (Bean3) beanFactory.doResolveDependency(dd1, null, null, null);拿到bean3Value
      最后把值赋给这个属性bean3Field.set(bean1, bean3Value);
  • 方法参数注入,InjectionMetadata注入Bean2的过程:
    • InjectionMetadata会把Bean1中加了@Autowired注解的方法的MethodName先拿到,这里拿到的MethodName就是 setBean2,然后再通过反射拿到这个方法,Method setBean2 = Bean1.class.getDeclaredMethod(“setBean2”, Bean2.class);
    • 将这个属性封装成一个DependencyDescriptor对象,再去调用Bean2 bean2Value = (Bean2) beanFactory.doResolveDependency(dd2, “bean2”, null, null);拿到bean2Value
    • 最后调用方法setBean2.invoke(bean1, bean2Value),给方法参数赋值。
  • 方法参数注入,参数类型为String类型,且加上了@Value注解,InjectionMetadata注入环境变量JAVA_HOME的过程:
    • InjectionMetadata会把Bean1中加了@Autowired注解的方法的MethodName先拿到,这里拿到的MethodName就是 setJava_home,然后再通过反射拿到这个方法,Method setJava_home = Bean1.class.getDeclaredMethod(“setJava_home”, String.class);
    • 将这个属性封装成一个DependencyDescriptor对象,再去调用String java_home = (String) beanFactory.doResolveDependency(dd3, null, null, null);拿到java_home
    • 最后调用方法setJava_home.invoke(bean1, java_home);,给方法参数赋值。

测试用例:

@Slf4j
public class TestBeanPostProcessors {
    @Test
    public void testAutowiredAnnotationBeanPostProcessor() throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 这里为了省事就不使用 beanFactory.registerBeanDefinition()方法去添加类的描述信息了
        // 直接使用 beanFactory.registerSingleton可以直接将Bean的单例对象注入进去,
        // 后面调用beanFactory.getBean()方法的时候就不会去根据Bean的定义去创建Bean的实例了,
        // 也不会有懒加载和依赖注入的初始化过程了。
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // 设置@Autowired注解的解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 设置解析 @Value 注解中的 ${} 表达式的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired,这称之为InjectionMetadata
        // 创建后处理器
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        // 后处理器在解析@Autowired和@Value的时候需要用到其他Bean,
        // 而BeanFactory提供了需要的Bean,所以需要把BeanFactory传给这个后处理器
        processor.setBeanFactory(beanFactory);
        // 创建Bean1
        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        // 解析@Autowired和@Value注解,执行依赖注入
        // PropertyValues pvs: 给注解的属性注入给定的值,这里不需要手动给定,传null即可
        // processor.postProcessProperties(null, bean1, "bean1");
        
        
        // postProcessProperties()方法底层原理探究
        // 通过查看源码得知 postProcessProperties()方法中调用了一个私有的方法findAutowiringMetadata(beanName, bean.getClass(), pvs); 会返回一个InjectionMetadata的对象,然后会调用InjectionMetadata.inject(bean1, "bean1", null)进行依赖注入
        // 通过反射调用一下
        /*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);

        // 2. 调用 InjectionMetaData 来进行依赖注入,注入时按类型查找值
        metadata.inject(bean1, "bean1", null);
        System.out.println(bean1);*/
        
        // 3. 如何去Bean工厂里面按类型查找值
        // 由于InjectionMetadata.inject(bean1, "bean1", null)的源码调用链过长,摘出主要调用过程进行演示

        // 3.1 @Autowired加在成员变量上,InjectionMetatadata给Bean1注入Bean3的过程
        // 通过InjectionMetadata把Bean1加了@Autowired注解的属性的BeanName先拿到,这里假设拿到的BeanName就是 bean3
        // 通过BeanName反射获取到这个属性,
        Field bean3Field = Bean1.class.getDeclaredField("bean3");
        // 设置私有属性可以被访问
        bean3Field.setAccessible(true);
        // 将这个属性封装成一个DependencyDescriptor对象
        DependencyDescriptor dd1 = new DependencyDescriptor(bean3Field, false);
        // 再执行beanFactory的doResolveDependency
        Bean3 bean3Value = (Bean3) beanFactory.doResolveDependency(dd1, null, null, null);
        System.out.println(bean3Value);
        // 给Bean1的成员bean3赋值
        bean3Field.set(bean1, bean3Value);
        System.out.println(bean1);

        // 3.2 @Autowired加在方法上,InjectionMetatadata给Bean1注入Bean2的过程
        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        Bean2 bean2Value = (Bean2) beanFactory.doResolveDependency(dd2, "bean2", null, null);
        System.out.println(bean2Value);
        // 给Bean1的setBean2()方法的参数赋值
        setBean2.invoke(bean1, bean2Value);
        System.out.println(bean1);

        // 3.3 @Autowired加在方法上,方法参数为String类型,加了@Value,
        // InjectionMetadata给Bean1注入环境变量JAVA_HOME属性的值
        Method setJava_home = Bean1.class.getDeclaredMethod("setJava_home", String.class);
        DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setJava_home, 0), true);
        String java_home = (String) beanFactory.doResolveDependency(dd3, null, null, null);
        System.out.println(java_home);
        setJava_home.invoke(bean1, java_home);
        System.out.println(bean1);
    }
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCe3eYbj-1662393502590)(${graph}/08274455f402d5eb3309c3ba1670c5d8.png)]

BeanFactory后置处理器

测试使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDcROakJ-1662393502591)(${graph}/image-20220905234503378.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixMvg0CT-1662393502591)(${graph}/image-20220905234539224.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-acesPthg-1662393502592)(${graph}/image-20220905234632839.png)]

测试代码:

package com.demo03;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.io.IOException;

@Slf4j
public class TestBeanFactoryPostProcessors {
    @Test
    public void testBeanPostProcessors() throws IOException {
        // ⬇️GenericApplicationContext 是一个【干净】的容器,默认不会添加任何后处理器,方便做测试
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("config", Config.class);
        // 添加Bean工厂后处理器ConfigurationClassPostProcessor
        // 解析@ComponentScan、@Bean、@Import、@ImportResource注解
        context.registerBean(ConfigurationClassPostProcessor.class);

        // 添加Bean工厂后处理器MapperScannerConfigurer,解析@MapperScan注解
        context.registerBean(MapperScannerConfigurer.class, beanDefinition -> {
            // 指定扫描的包名
            beanDefinition.getPropertyValues().add("basePackage", "top.jacktgq.mapper");
        });

        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        // ⬇️销毁容器
        context.close();
    }
}

经过测试和运行结果的比对:

@ComponentScan 中 包含了@component

@component由其他处理器解析

  • @ComponentScan@Bean对应的Bean工厂后处理器是ConfigurationClassPostProcessor
  • @MapperScan对应的Bean工厂后处理器是MapperScannerConfigurer

模拟

Bean后置处理器与BeanFactory后置处理器对比

BeanFactory 后置处理器 : 添加更多的Bean定义信息

Bean后置处理器 :维护bean的生命周期

BeanFactory后置增强的几个实现类

@ComponentScan,@Bean,@Configuration
class org.springframework.context.annotation.ConfigurationClassPostProcessor
class org.springframework.context.event.EventListenerMethodProcessor

Bean后置增强的几个实现类

@Autowire 
class org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
@Resource
class org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值