Spring5底层源码分析第一篇(容器与Bean)

Spring5底层源码分析

1. 容器与 bean

1) 容器接口

  • BeanFactory 接口,典型功能有:

    • getBean
  • ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:

    • 国际化 MessageSource
    • 通配符方式获取一组 Resource 资源 ResourcePatternResolver
    • 整合 Environment 环境(能通过它获取各种来源的配置信息)EnvironmentCapable
    • 事件发布与监听,实现组件之间的解耦 ApplicationEventPublisher

可以看到,都是 BeanFactory 提供的基本功能,ApplicationContext 中的扩展功能都没有用到。

BeanFactory 与 ApplicationContext 的区别

image-20220730190853244

代码参考

**
 * @description: BeanFactoryApplicationContext 的区别
 * @author: ~Teng~
 * @date: 2022/7/30 21:57
 */
@SpringBootApplication
public class A01 {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
        /*
            1. 到底什么是 BeanFactory
                - 它是 ApplicationContext 的父接口
                - 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能
         */
        System.out.println(context);

        /*
            2. BeanFactory 能干点啥
                - 表面上只有 getBean
                - 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
         */
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        map.entrySet().stream().forEach(e -> System.out.println(e.getKey() + "====>" + e.getValue()));

        /*
            3. ApplicationContext 比 BeanFactory 多点啥 4点
         */
        // 3.1 国际化
        System.out.println(context.getMessage("hi", null, Locale.CHINA));
        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
        // 3.2 通配符方式获取资源
        Resource[] resources = context.getResources("classpath:application.properties");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
        System.out.println("====================================");
        resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
        System.out.println("======================================");
        // 3.3 整合Environment环境
        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));

        // 3.4 事件发布与监听
        context.publishEvent(new UserRegisteredEvent(context));
        System.out.println("======================================");
        // 实现解耦
        context.getBean(Component1.class).register();
        /*
            4. 学到了什么
                a. BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能
                b. 又新学一种代码之间解耦途径
         */
    }
}
总结💡

通过这个示例结合 debug 查看 ApplicationContext 对象的内部结构,学到:

  1. 到底什么是 BeanFactory
    • 它是 ApplicationContext 的父接口
    • 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
  2. BeanFactory 能干点啥
    • 表面上只有 getBean
    • 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
    • 例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
  3. ApplicationContext 比 BeanFactory 多点啥
    • ApplicationContext 组合并扩展了 BeanFactory 的功能
    • 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
    • 新学一种代码之间解耦途径,事件解耦

注意

  • 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED,这是因为这些版本的 jdk 默认不允许跨 module 反射
  • 事件发布还可以异步,这个视频中没有展示,请自行查阅 @EnableAsync,@Async 的用法
国际化
public class TestMessageSource {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("messageSource", MessageSource.class, () -> {
            ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
            ms.setDefaultEncoding("utf-8");
            ms.setBasename("messages");
            return ms;
        });

        context.refresh();

        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.CHINESE));
        System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
    }
}

国际化文件均在 src/resources 目录下

messages.properties(空)

messages_en.properties

hi=Hello

messages_ja.properties

hi=こんにちは

messages_zh.properties

hi=你好

注意

  • ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
  • 使用 SpringBoot 时,国际化文件名固定为 messages
  • 空的 messages.properties 也必须存在

2) 容器实现

Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

  • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现
  • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContextSpring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContextSpring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来

DefaultListableBeanFactory

部分代码参考

/**
 * @description: 实现BeanFactory
 * @author: ~Teng~
 * @date: 2022/7/30 22:24
 */
public class BeanFactoryTest {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class, scope, 初始化, 销毁)
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        // 向bean工厂注入 但此时bean1 和 bean2 未被注入 需要向bean工厂中添加一些后置处理器
        beanFactory.registerBeanDefinition("config", beanDefinition);
        // 给 BeanFactory 添加一些常用的后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        // BeanFactory 后处理器主要功能,补充了一些 bean 定义 此时才注入了 bean1 和 bean2
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().
                forEach(beanFactoryPostProcessor -> beanFactoryPostProcessor.postProcessBeanFactory(beanFactory));

        // Bean 后处理器,针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                // 进行排序
                // .sorted(beanFactory.getDependencyComparator())
                .forEach(beanPostProcessor -> {
                    System.out.println("======>" + beanPostProcessor);
                    beanFactory.addBeanPostProcessor(beanPostProcessor);
                });

        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String definitionName : beanDefinitionNames) {
            System.out.println(definitionName);
        }

        // 提前准备好所有单例
        beanFactory.preInstantiateSingletons();
        System.out.println("===========================================");
        // 默认是用到时才创建
        Bean2 bean2 = beanFactory.getBean(Bean1.class).getBean2();
        System.out.println(bean2);

        /*
            学到了什么:
            a. beanFactory 不会做的事
                   1. 不会主动调用 BeanFactory 后处理器
                   2. 不会主动添加 Bean 后处理器
                   3. 不会主动初始化单例
                   4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
            b. bean 后处理器会有排序的逻辑
         */
    }
}
总结💡
  • beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象
    • 我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中
    • bean definition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
  • beanFactory 需要手动调用 beanFactory 后处理器对它做增强
    • 例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition
  • beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强
    • 例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的
    • bean 后处理的添加顺序会对解析结果有影响,见视频中同时加 @Autowired,@Resource 的例子
  • beanFactory 需要手动调用方法来初始化单例
  • beanFactory 需要额外设置才能解析 ${} 与 #{}
常见 ApplicationContext 实现

部分代码参考

/**
 * @description: 常见 ApplicationContext 实现
 * @author: ~Teng~
 * @date: 2022/7/30 22:00
 */
public class A02 {
    public static void main(String[] args) {
        /**
         * 4种
         * 1. 基于xml配置文件创建
         * 2. 根据磁盘路径创建
         * 3. 基于java配置类进行实现
         * 4. 基于java配置类进行实现 用于web环境
         */
        // testAnnotationConfigApplicationContext();
        testAnnotationConfigServletWebServerApplicationContext();

        /*
            学到了什么
                a. 常见的 ApplicationContext 容器实现
                b. 内嵌容器、DispatcherServlet 的创建方法、作用
         */
    }

    /**
     * 较为经典的容器, 基于 java 配置类来创建
     */
    private static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
    }

    /**
     * 基于java配置类进行实现 用于web环境
     */
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebApplicationContext context
                = new AnnotationConfigServletWebApplicationContext(WebConfig.class);
        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}
/**
 * @description: Web配置 前3个是必须的
 * @author: ~Teng~
 * @date: 2022/7/30 23:23
 */
@Configuration
public class WebConfig {

    /**
     * 配置tomcat容器
     */
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    /**
     * 配置前置处理器
     */
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    /**
     * 将二者关联
     */
    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    @Bean("/hello")
    public Controller controller() {
        return (request, response) -> {
            response.getWriter().println("hello");
            return null;
        };
    }
}
总结💡
  1. 常见的 ApplicationContext 容器实现
  2. 内嵌容器、DispatcherServlet 的创建方法、作用

3) ※ Bean 的生命周期 ※

一个受 Spring 管理的 bean,生命周期主要阶段有

  1. 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
  2. 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
  3. 初始化:回调各种 Aware 接口,调用对象的各种初始化方法(@PostConstruct)
  4. 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
    • prototype 对象也能够销毁,不过需要容器这边主动调用

一些资料会提到,生命周期中还有一类 bean 后处理器:BeanPostProcessor,会在 bean 的创建和初始化的前后,以及依赖注入阶段销毁前提供一些扩展逻辑。

bean生命周期

代码参考

@Component
@Slf4j
public class LifeCycleBean {

    public LifeCycleBean() {
        log.info("构造方法");
    }

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

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

    @PreDestroy
    public void destroy() {
        log.info("销毁");
    }
}
**
 * @description: bean后处理器
 * @author: ~Teng~
 * @date: 2022/7/30 23:46
 */
@Component
@Slf4j
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.info("<<<<<< 构造方法之前执行, 这里返回的对象会替换掉原本的 bean");
        }
        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 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;
    }

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

结果:

<<<<<< 构造方法之前执行, 这里返回的对象会替换掉原本的 bean
构造方法
<<<<<< 构造方法之后执行, 这里如果返回 false 会跳过依赖注入阶段
<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource
依赖注入:D:\java_config\Java\jdk-17.0.4
<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties
初始化
<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强
<<<<<< 销毁之前执行, 如 @PreDestroy
销毁
创建
依赖注入
初始化
可用
销毁

创建前后的增强

  • postProcessBeforeInstantiation
    • 这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程
  • postProcessAfterInstantiation
    • 这里如果返回 false 会跳过依赖注入阶段

依赖注入前的增强

  • postProcessProperties
    • 如 @Autowired、@Value、@Resource

初始化前后的增强

  • postProcessBeforeInitialization
    • 这里返回的对象会替换掉原本的 bean
    • 如 @PostConstruct、@ConfigurationProperties
  • postProcessAfterInitialization
    • 这里返回的对象会替换掉原本的 bean
    • 如代理增强

销毁之前的增强

  • postProcessBeforeDestruction
    • 如 @PreDestroy
总结💡
  1. Spring bean 生命周期各个阶段
  2. 模板设计模式, 指大流程已经固定好了, 通过接口回调(bean 后处理器)在一些关键点前后提供扩展
模板方法设计模式

关键代码

/**
 * @description: 设计模式---模板方法
 * @author: ~Teng~
 * @date: 2022/7/31 0:03
 */
public class MethodTemplateTest {
    public static void main(String[] args) {
        MyBeanFactory myBeanFactory = new MyBeanFactory();
        myBeanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        myBeanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        myBeanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            // 目的:在保持原有代码不变的情况下,增加新的功能
            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 后处理器排序
总结💡
  1. 实现了 PriorityOrdered 接口的优先级最高
  2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
  3. 其它的排在最后

4) Bean 后处理器

后处理器作用

代码参考

/**
 * @description: bean 后处理器的作用
 * @author: ~Teng~
 * @date: 2022/7/31 10:53
 */
public class A04 {
    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);

        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
        context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory()); // @ConfigurationProperties

        // 初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

        System.out.println(context.getBean(Bean4.class));
        // 销毁容器
        context.close();
    }
}
总结💡
  1. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成
  2. 每个后处理器各自增强什么功能
    • AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @Value
    • CommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroy
    • ConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties
  3. 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等
@Autowired bean 后处理器运行分析

代码参考

/**
 * @description: AutowiredAnnotationBeanPostProcessor 运行分析
 * @author: ~Teng~
 * @date: 2022/7/31 11:33
 */
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 解析
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMeta
        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);

        // 利用反射进行执行
        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);
        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. 如何按类型查找值 InjectionMetadata的inject内部方法大致过程
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(bean3, false);
        Object o = beanFactory.doResolveDependency(dependencyDescriptor, null, null, null);
        System.out.println(o);

        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dependencyDescriptor1 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
        Object o1 = beanFactory.doResolveDependency(dependencyDescriptor1, null, null, null);
        System.out.println(o1);

        Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
        DependencyDescriptor dependencyDescriptor2 = new DependencyDescriptor(new MethodParameter(setHome, 0), false);
        Object o2 = beanFactory.doResolveDependency(dependencyDescriptor2, null, null, null);
        System.out.println(o2);
    }
}
总结💡
  1. AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionMetadata
  2. InjectionMetadata 可以完成依赖注入
  3. InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
  4. 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找

5) BeanFactory 后处理器

BeanFactory 后处理器的作用

代码参考

/**
 * @description: BeanFactory 后处理器的作用
 * @author: ~Teng~
 * @date: 2022/7/31 12:00
 */
public class A05 {
    public static void main(String[] args) throws IOException {
        // GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("config", Config.class);
        // bean工厂后处理器 解析 @ComponentScan @Bean @Import @ImportResource
        // context.registerBean(ConfigurationClassPostProcessor.class);
        // 解析 @MapperScanner
        // context.registerBean(MapperScannerConfigurer.class, bd -> bd.getPropertyValues().add("basePackage", "com.lt.spring5.a05.mapper"));

        // 解析 @ComponentScan
        //context.registerBean(ComponentScanPostProcessor.class);
        // 解析 @Bean
        context.registerBean(AtBeanPostProcessor.class);
        // 解析 Mapper 接口
        context.registerBean(MapperPostProcessor.class);

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

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

        context.close();
        /*
            学到了什么
                a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
                b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义
         */
    }
}
  • ConfigurationClassPostProcessor 可以解析
    • @ComponentScan
    • @Bean
    • @Import
    • @ImportResource
  • MapperScannerConfigurer 可以解析
    • Mapper 接口
总结💡
  1. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
  2. 这些扩展功能由不同的 BeanFactory 后处理器来完成,其实主要就是补充了一些 bean 定义
模拟解析 @ComponentScan

代码参考

/**
 * @description: 工厂后置处理器 解析@ComponentScan注解的 模拟实现
 * @author: ~Teng~
 * @date: 2022/7/31 12:04
 */
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    /**
     * context.refresh后执行
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        try {
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                    System.out.println(p);
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    System.out.println(path);
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        MetadataReader reader = factory.getMetadataReader(resource);
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        if (annotationMetadata.hasAnnotation(Component.class.getName())
                                || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            String name = generator.generateBeanName(bd, beanDefinitionRegistry);
                            beanDefinitionRegistry.registerBeanDefinition(name, bd);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
总结💡
  1. Spring 操作元数据的工具类 CachingMetadataReaderFactory
  2. 通过注解元数据(AnnotationMetadata)获取直接或间接标注的注解信息
  3. 通过类元数据(ClassMetadata)获取类名,AnnotationBeanNameGenerator 生成 bean 名
  4. 解析元数据是基于 ASM 技术
模拟解析 @Bean

代码参考

/**
 * @description: 工厂后置处理器 解析@Bean注解的 模拟实现
 * @author: ~Teng~
 * @date: 2022/7/31 12:05
 */
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        try {
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/lt/spring5/a05/Config.class"));
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : methods) {
                System.out.println(method);
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();

                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length() > 0) {
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
                beanDefinitionRegistry.registerBeanDefinition(method.getMethodName(), beanDefinition);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}
总结💡
  1. 进一步熟悉注解元数据(AnnotationMetadata)获取方法上注解信息
模拟解析 Mapper 接口

代码参考

/**
 * @description: 解析 @Mapper 后置处理器
 * @author: ~Teng~
 * @date: 2022/7/31 12:04
 */
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:com/lt/spring5/a05/mapper/**/*.class");
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            for (Resource resource : resources) {
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                if (classMetadata.isInterface()) {
                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                    String name = generator.generateBeanName(bd2, beanDefinitionRegistry);
                    beanDefinitionRegistry.registerBeanDefinition(name, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}
总结💡
  1. Mapper 接口被 Spring 管理的本质:实际是被作为 MapperFactoryBean 注册到容器中
  2. Spring 的诡异做法,根据接口生成的 BeanDefinition 仅为根据接口名生成 bean 名

6) Aware 接口

Aware 接口及 InitializingBean 接口

代码参考

/**
 * @description: Aware 接口及 InitializingBean 接口
 * @author: ~Teng~
 * @date: 2022/7/31 15:15
 */
@Slf4j
public class A06 {
    public static void main(String[] args) {
        /*
            1. Aware 接口用于注入一些与容器相关信息, 例如
                a. BeanNameAware 注入 bean 的名字
                b. BeanFactoryAware 注入 BeanFactory 容器
                c. ApplicationContextAware 注入 ApplicationContext 容器
                d. EmbeddedValueResolverAware ${}
         */
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myBean", MyBean.class);

        context.refresh();
        context.close();
         /*
            2. 有同学说: b、c、d 的功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢
            简单地说:
                a. @Autowired 的解析需要用到 bean 后处理器, 属于扩展功能
                b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别
            某些情况下, 扩展功能会失效, 而内置功能不会失效

            例1: 你会发现用 Aware 注入 ApplicationContext 成功, 而 @Autowired 注入 ApplicationContext 失败
         */

        /*
            学到了什么
                a. Aware 接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory, ApplicationContext
                b. InitializingBean 接口提供了一种【内置】的初始化手段
                c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们
         */
    }
}
@Slf4j
public class MyBean implements BeanNameAware, InitializingBean, ApplicationContextAware {
    @Override
    public void setBeanName(String name) {
        log.debug("当前bean " + this + " 名字叫:" + name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.debug("当前bean " + this + " 容器是:" + applicationContext);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("当前bean " + this + " 初始化");
    }

    @Autowired
    public void aaa(ApplicationContext applicationContext) {
        log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);
    }

    @PostConstruct
    public void init() {
        log.debug("当前bean " + this + " 使用@PostConstruct 初始化");
    }
}
总结💡
  1. Aware 接口提供了一种【内置】 的注入手段,例如
    • BeanNameAware 注入 bean 的名字
    • BeanFactoryAware 注入 BeanFactory 容器
    • ApplicationContextAware 注入 ApplicationContext 容器
    • EmbeddedValueResolverAware 注入 ${} 解析器
  2. InitializingBean 接口提供了一种【内置】的初始化手段
  3. 对比
    • 内置的注入和初始化不受扩展功能的影响,总会被执行
    • 而扩展功能受某些情况影响可能会失效
    • 因此 Spring 框架内部的类常用内置注入和初始化
配置类 @Autowired 失效分析

Java 配置类不包含 BeanFactoryPostProcessor 的情况

ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor 3. 创建和初始化 3.1 依赖注入扩展(如 @Value 和 @Autowired) 3.2 初始化扩展(如 @PostConstruct) 3.3 执行 Aware 及 InitializingBean 3.4 创建成功 ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类

Java 配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效

ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 3. 创建和初始化 3.1 执行 Aware 及 InitializingBean 3.2 创建成功 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类

对应代码

@Configuration
public class MyConfig1 {

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

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.debug("注入 ApplicationContext");
    }

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

    @Bean //  ⬅️ 注释或添加 beanFactory 后处理器对应上方两种情况
    public BeanFactoryPostProcessor processor1() {
        return beanFactory -> {
            log.debug("执行 processor1");
        };
    }

}

注意

解决方法:

  • 用内置依赖注入和初始化取代扩展依赖注入和初始化
  • 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建

7) 初始化与销毁

初始化与销毁顺序

代码参考

@Slf4j
public class Bean1 implements InitializingBean {
    @PostConstruct
    public void init1() {
        log.info("初始化1");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("初始化2");
    }

    public void init3() {
        log.info("初始化3");
    }
}
@Slf4j
public class Bean2 implements DisposableBean {
    @PreDestroy
    public void destroy1() {
        log.info("销毁1");
    }

    @Override
    public void destroy() throws Exception {
        log.info("销毁2");
    }

    public void destroy3() {
        log.info("销毁3");
    }
}
@SpringBootApplication
public class A07_1 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A07_1.class, args);
        context.close();
        /*
            学到了什么
                a. Spring 提供了多种初始化和销毁手段
                b. Spring 的面试有多么地卷
         */
    }

    @Bean(initMethod = "init3")
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean(destroyMethod = "destroy3")
    public Bean2 bean2() {
        return new Bean2();
    }
}

结果

2022-08-01 00:32:16.621  INFO 25184 --- [  restartedMain] com.lt.spring5.a07.Bean1                 : 初始化1
2022-08-01 00:32:16.621  INFO 25184 --- [  restartedMain] com.lt.spring5.a07.Bean1                 : 初始化2
2022-08-01 00:32:16.621  INFO 25184 --- [  restartedMain] com.lt.spring5.a07.Bean1                 : 初始化3
...
2022-08-01 00:32:17.018  INFO 25184 --- [  restartedMain] com.lt.spring5.a07.Bean2                 : 销毁1
2022-08-01 00:32:17.018  INFO 25184 --- [  restartedMain] com.lt.spring5.a07.Bean2                 : 销毁2
2022-08-01 00:32:17.018  INFO 25184 --- [  restartedMain] com.lt.spring5.a07.Bean2                 : 销毁3
总结💡

Spring 提供了多种初始化手段,除了课堂上讲的 @PostConstruct@Bean(initMethod) 之外,还可以实现 InitializingBean 接口来进行初始化,如果同一个 bean 用了以上手段声明了 3 个初始化方法,那么它们的执行顺序是

  1. @PostConstruct 标注的初始化方法
  2. InitializingBean 接口的初始化方法
  3. @Bean(initMethod) 指定的初始化方法

与初始化类似,Spring 也提供了多种销毁手段,执行顺序为

  1. @PreDestroy 标注的销毁方法
  2. DisposableBean 接口的销毁方法
  3. @Bean(destroyMethod) 指定的销毁方法

8) Scope

在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope

  • singleton,容器启动时创建(未设置延迟),容器关闭时销毁
  • prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
  • request,每次请求用到此 bean 时创建,请求结束时销毁
  • session,每个会话用到此 bean 时创建,会话结束时销毁
  • application,web 容器用到此 bean 时创建,容器停止时销毁

有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃

但要注意,如果在 singleton 注入其它 scope 都会有问题,解决方法有

  • @Lazy
  • @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
  • ObjectFactory
  • ApplicationContext.getBean
request, session, application 作用域

代码参考

@SpringBootApplication
public class A08 {
    public static void main(String[] args) {
        SpringApplication.run(A08.class, args);
        /*
            学到了什么
                a. 有几种 scope
                b. 在 singleton 中使用其它几种 scope 的方法
                c. 其它 scope 的销毁
                    1. 可以将通过 server.servlet.session.timeout=10s 观察 session bean 的销毁
                    2. ServletContextScope 销毁机制疑似实现有误
         */
    }
}
@Component
@Slf4j
@Scope("request")
public class BeanForRequest {
    @PreDestroy
    public void destroy() {
        log.info("destroy");
    }
}
@Component
@Scope("session")
@Slf4j
public class BeanForSession {
    @PreDestroy
    public void destroy() {
        log.info("destroy");
    }
}
@Slf4j
@Component
@Scope("application")
public class BeanForApplication {
    @PreDestroy
    public void destroy() {
        log.info("destroy");
    }
}
@RestController
public class MyController {

    @Autowired
    @Lazy
    private BeanForRequest beanForRequest;

    @Autowired
    @Lazy
    private BeanForSession beanForSession;

    @Autowired
    @Lazy
    private BeanForApplication beanForApplication;

    @GetMapping(value = "/test", produces = "text/html")
    public String test(HttpServletRequest request, HttpSession session) {
        ServletContext sc = request.getServletContext();
        String sb = "<ul>" +
                "<li>" + "request scope:" + beanForRequest + "</li>" +
                "<li>" + "session scope:" + beanForSession + "</li>" +
                "<li>" + "application scope:" + beanForApplication + "</li>" +
                "</ul>";
        return sb;
    }
}

打开不同的浏览器, 刷新 http://localhost:8081/test 即可查看效果

Edge浏览器

image-20220731232023988

Chrome浏览器

image-20220731232055411

对于不同的请求,其中的request scope不同;对于不同的会话,其中二点session scope不同,但是application是相同的。

总结💡
  1. 有5种 scope
  2. 在 singleton 中使用其它几种 scope 的方法
  3. 其它 scope 的销毁时机
    • 可以将通过 server.servlet.session.timeout=30s 观察 session bean 的销毁
    • ServletContextScope 销毁机制疑似实现有误
singleton 注入其它 scope 失效

以单例注入多例为例

有一个单例对象 E

@Component
public class E {
    private static final Logger log = LoggerFactory.getLogger(E.class);

    private F f;

    public E() {
        log.info("E()");
    }

    @Autowired
    public void setF(F f) {
        this.f = f;
        log.info("setF(F f) {}", f.getClass());
    }

    public F getF() {
        return f;
    }
}

要注入的对象 F 期望是多例

@Component
@Scope("prototype")
public class F {
    private static final Logger log = LoggerFactory.getLogger(F.class);

    public F() {
        log.info("F()");
    }
}

测试

E e = context.getBean(E.class);
F f1 = e.getF();
F f2 = e.getF();
System.out.println(f1);
System.out.println(f2);

输出

com.itheima.demo.cycle.F@6622fc65
com.itheima.demo.cycle.F@6622fc65

发现它们是同一个对象,而不是期望的多例对象

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F

e 创建
e set 注入 f
f 创建

解决

  • 仍然使用 @Lazy 生成代理
  • 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的 f 对象
使用f方法
使用f方法
使用f方法
e 创建
e set 注入 f代理
f 创建
f 创建
f 创建
@Component
public class E {

    @Autowired
    @Lazy
    public void setF(F f) {
        this.f = f;
        log.info("setF(F f) {}", f.getClass());
    }

    // ...
}

注意

  • @Lazy 加在也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了
  • @Autowired 加在 set 方法的目的类似

输出

E: setF(F f) class com.itheima.demo.cycle.F$$EnhancerBySpringCGLIB$$8b54f2bc
F: F()
com.itheima.demo.cycle.F@3a6f2de3
F: F()
com.itheima.demo.cycle.F@56303b57

从输出日志可以看到调用 setF 方法时,f 对象的类型是代理类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ambition0823

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值