全面深度讲解Spring5底层原理01:容器与Bean

全面深度讲解Spring5底层原理

本文章基于B站UP主"黑马程序员"视频教程《全面深度讲解spring5底层原理》进行整理记录,仅用于个人学习/交流使用。

参考:https://www.bilibili.com/video/BV1P44y1N7QG

时间:2022年4月

作者:耿鬼不会笑

文章目录

第一章:容器与Bean

1.BeanFactory和ApplicationContext

image-20220324093121807

Q:BeanFactory和ApplicationContext的关系

BeanFactory是ApplicationContext的父接口,它才是Spring的核心容器

ApplicationContext间接继承了BeanFactory,通过"组合"的方式(BeanFactory是ApplicationContext的一个成员变量,ApplicationContext的部分功能是直接调用BeanFactory的功能,例如getBean),对BeanFactory的功能提供了一些扩展

Q:BeanFactory的功能

1.BeanFactory中提供的方法

image-20220324093650018

2.BenaFactory的实现类

实际上,控制反转,基本的依赖注入,直至Bean的生命周期的各种功能,都由BeanFactory的实现类提供。

Q:BenaFactory的实现类之DefaultListableBeanFactory

DefaultListableBeanFactory管理所有对象(尤其是管理单例对象),是BeanFactory的一个实现

image-20220324094100404

DefaultSingletonBeanFactory管理所有的单例对象

Map的key是单例对象的名字,value是单例对象

image-20220324094157339

演示:获取所有以component开头的单例对象

        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().filter(e -> e.getKey().startsWith("component"))
                .forEach(e -> {
                    System.out.println(e.getKey() + "=" + e.getValue());
                });
Q:ApplicationContext对于BeanFactory的扩展

image-20220324094838355

MessageSource演示:处理国际化资源(context.getMessage)

image-20220324095403153

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 也必须存在
ResourcePatternResolver演示:根据通配符获取一个或多个资源

image-20220324100108098

ApplicationEventPublisher演示:发布事件对象(基本使用)

image-20220405091644257

ApplicationEventPublisher演示:场景应用:注册后发送短信,主要作用:实现组件之间的解耦

image-20220324101233176

EnvironmentCapable演示:获取配置信息(系统环境变量 和 配置文件中的信息)

image-20220324100155813

Q:BeanFactory和ApplianceContext的总结

到底什么是 BeanFactory

  • 它是 ApplicationContext 的父接口
  • 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory

BeanFactory 能干点啥

  • 表面上只有 getBean
  • 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
  • 例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean

ApplicationContext 比 BeanFactory 多点啥

  • ApplicationContext 组合并扩展了 BeanFactory 的功能
  • 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
  • 新学一种代码之间解耦途径,事件解耦

建议练习:完成用户注册与发送短信之间的解耦,用事件方式、和 AOP 方式分别实现

注意

  • 如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED,这是因为这些版本的 jdk 默认不允许跨 module 反射
  • 事件发布还可以异步,这个视频中没有展示,请自行查阅 @EnableAsync,@Async 的用法

2.BeanFactory的实现

Q:如何自己定义一个bean

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
		// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //(beanName,xxx)
        beanFactory.registerBeanDefinition("config", beanDefinition);

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

输出结果为config,只有自己定义的bean,如果我们使用@Configuration、@Bean等注解,使用注解标注的bean也不会被扫描

总结:原始的BeanFactory没有解析注解的能力

Q:如何扫描使用注解声明的bean

1、给 BeanFactory 添加一些常用的后处理器

Tips:后置处理器有多种,这里指的是针对bean工厂的后置处理器,后面还会讲针对依赖注入的后置处理器

2、执行BeanFactory后置处理器

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
		// BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //(beanName,xxx)
        beanFactory.registerBeanDefinition("config", beanDefinition);

        // 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展)
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // BeanFactory 后处理器主要功能,补充了一些 bean 定义
        // beanFactory.getBeansOfType 根据类型获取多个bean
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
        });

        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 {
        private static final Logger log = LoggerFactory.getLogger(Bean1.class);
        @Autowired
        private Bean2 bean2;
    }

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

输出结果:
config
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
bean1
bean2

补充:当只添加了后置处理器,并没有启动后置处理器时

image-20220405102121999

Q:能否取出bean1、bean2正常使用

结论:不能,bean2为null

原因:@Autowired对于BeanFactory而言也是扩展功能,需要使用【bean的后置处理器来解析@Autowired、@Value注解】

image-20220405102801499

Q:如何解决上面出现的问题

解决方案:添加bean的后置处理器

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
        // BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //(beanName,xxx)
        beanFactory.registerBeanDefinition("config", beanDefinition);


        // 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展)
        // 把后置处理器加到bean工厂
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // BeanFactory 后处理器主要功能,补充了一些 bean 定义
        // beanFactory.getBeansOfType 根据类型获取多个bean
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
        });

        // Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
        // 建立后置处理器和bean工厂的联系
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

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

        System.out.println(beanFactory.getBean(Bean1.class).getBean2());
    }

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

    static class Bean1 {
        private static final Logger log = LoggerFactory.getLogger(Bean1.class);
        public Bean1() {
            log.debug("构造 Bean1()");
        }
        @Autowired
        private Bean2 bean2;
        public Bean2 getBean2() {
            return bean2;
        }
    }

    static class Bean2 {
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);
        public Bean2() {
            log.debug("构造 Bean2()");
        }
    }
}
Q:单例对象的创建时机

默认情况下(延迟创建):延时实例化、初始化,即当用到bean1、bean2时才会去创建

预先创建:预先处理(创建)所用的单例对象,beanFactory.preInstantiateSingletons();

image-20220405110552451

Q:总结:bean工厂后置处理器 & bean后置处理器

bean工厂后置处理器:补充了一些 bean 定义

1.org.springframework.context.annotation.internalConfigurationAnnotationProcessor

  • 解析@Configuration、@Bean注解

bean的后置处理器:针对每个bean的生命周期(创建、依赖注入、初始化)提供扩展功能

1.org.springframework.context.annotation.internalAutowiredAnnotationProcessor

  • 解析@Autowired、@Value

2.org.springframework.context.annotation.internalCommonAnnotationProcessor

  • 解析@Resource注解(JavaEE注解,非Spring注解)
Q:bean 后处理器会有排序的逻辑

当同时使用@Autowired、@Resource时,哪个会生效

AnnotationConfigUtils中含有多个bean注解解析器

image-20220405153321925

@Autowired、@Resource注解会先解析哪一个?

image-20220405154411521

自定义解析顺序(使用比较器)

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // bean 的定义(class(类型), scope(单例or多例), 初始化方法, 销毁方法)
        // BeanDefinitionBuilder..xxx.xxx.xxx.getBeanDefinition()
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //(beanName,xxx)
        beanFactory.registerBeanDefinition("config", beanDefinition);

        // 【重点】给 BeanFactory 添加一些常用的后处理器(对BeanFactory的扩展)
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        // BeanFactory 后处理器主要功能,补充了一些 bean 定义
        // beanFactory.getBeansOfType 根据类型获取多个bean
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器
        });

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

        beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                .sorted(beanFactory.getDependencyComparator())
                .forEach(beanPostProcessor -> {
            System.out.println(">>>>" + beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

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

        System.out.println(beanFactory.getBean(Bean1.class).getInter());
//        打印注解排序,实现order接口实现,数字小的排在前面
//        System.out.println("Common:" + (Ordered.LOWEST_PRECEDENCE - 3));
//        System.out.println("Autowired:" + (Ordered.LOWEST_PRECEDENCE - 2));
    }

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

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

        @Bean
        public Bean3 bean3() {
            return new Bean3();
        }

        @Bean
        public Bean4 bean4() {
            return new Bean4();
        }
    }

    interface Inter {}

    static class Bean3 implements Inter {}

    static class Bean4 implements Inter {}

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

        public Bean1() {
            log.debug("构造 Bean1()");
        }

        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2() {
            return bean2;
        }

        @Autowired
        @Resource(name = "bean4")
        private Inter bean3;
        //当这里写的是 @Autowired private Inter inter; 时,会报错
        //原因:@Autowired是根据类型匹配,bean3、bean4都是inter

        public Inter getInter() {
            return bean3;
        }
    }

    static class Bean2 {
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);
        public Bean2() {
            log.debug("构造 Bean2()");
        }
    }
}
Q:BeanFactory总结

a.beanFactory 不会做的事,这些事情都需要自己来完成

  1. 不会主动调用 BeanFactory 后处理器

  2. 不会主动添加 Bean 后处理器

  3. 不会主动初始化单例

  4. 不会解析beanFactory 还不会解析 ${ } 与 #{ } //$占位符,Spring中的EL表达式

那么BeanFactory不会做的话,AppliactionContext就会自己做

b.bean 后处理器会有排序的逻辑

3.AppliactionContext的常见实现和用法

ClassPathXmlApplicationContext

从类路径下读取xml配置文件,并根据xml配置文件的内容来创建Spring中的AppliactionContext容器

    // 较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
    private static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("a02.xml");

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

        System.out.println(context.getBean(Bean2.class).getBean1());
    }
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
    <bean id="bean1" class="com.itheima.a02.A02.Bean1"/>

    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.itheima.a02.A02.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <property name="bean1" ref="bean1"/>
    </bean>
    
    <!--添加bean后置处理器的标签->
    <context:annotation-config/>
</beans>
FileSystemXmlApplicationContext

从磁盘路径下读取xml配置文件,并根据xml配置文件的内容来创建Spring中的AppliactionContext容器

这里写绝对路径、相对路径都可以

    // 基于磁盘路径下 xml 格式的配置文件来创建
    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext(
                        "src\\main\\resources\\a02.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }
Q:XmlApplicationContext实现机理
    public static void main(String[] args) {
        
        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(new FileSystemResource("src\\main\\resources\\a02.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

创建DefaultListableBeanFactory对象,作为bean工厂

创建XmlBeanDefinitionReader,读取bean,读取xml标签,转化为bean定义,放到 defaultListableBeanFactory 里面

AnnotationConfigApplicationContext

基于注解(配置类)的实现方式

其中配置类也是一个bean

还会自动为你添加相关的bean后置处理器

    // 较为经典的容器, 基于 java 配置类来创建
    private static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);

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

        System.out.println(context.getBean(Bean2.class).getBean1());
    }

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

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

支持Java配置类,以及Servlet服务器(web环境)的创建方式

    // 较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }
	//Web环境需要更多的配置信息/组件
    @Configuration
    static class WebConfig {
        @Bean //产生内嵌的Tomcat服务器
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }
        @Bean //核心,DispatcherServlet,请求入口点,【前控制器】
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
        @Bean //把 DispatcherServlet 注册到 Tomcat服务器 中
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        @Bean("/hello") //控制器,实现Controller接口
        public Controller controller1() {
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
    }

4.Bean的生命周期

执行顺序:构造方法、依赖注入的方法、bean的初始化方法、bean的销毁方法

同时,在执行的各个阶段,可以使用 后置处理器 进行增强

@SpringBootApplication
public class A03 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);
        context.close();
    }
}
@Component
public class LifeCycleBean {
    private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);

    public LifeCycleBean() {
        log.debug("构造");
    }

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

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

    @PreDestroy
    public void destroy() {
        log.debug("销毁");
    }
}
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

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

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

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        return null;
    }

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

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

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

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

image-20220405163642986

5.模板方法模式

提高代码的扩展能力

固定不变的形成方法的主干、经常变化的抽象成接口

public class TestMethodTemplate {

    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();
    }

    // 模板方法  Template Method Pattern
    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 processor) {
            processors.add(processor);
        }
    }
    
    static interface BeanPostProcessor {
        public void inject(Object bean); // 对依赖注入阶段的扩展
    }
}

6.常见的Bean后置处理器

常见的Bean后置处理器1和2

Bean后处理器的作用:为Bean生命周期的各个阶段提供扩展

AutowiredAnnotationBeanPostProcessor.class 
解析:@Autowired @Value
执行时机:依赖注入阶段

单独使用时不能解析 “值注入” 的内容
	@Autowired
	public void setHome(@Value("${JAVA_HOME}") String home) {
需要换一个实现,即使用
	context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
	context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
CommonAnnotationBeanPostProcessor.class 
解析:@Resource @PostConstruct(初始化) @PreDestroy(销毁)
执行时机:@Resource(依赖注入) 、@PostConstruct(初始化前,虽然叫初始化后) @PreDestroy(销毁前)

程序源码:

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.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value

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

        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

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

        System.out.println(context.getBean(Bean1.class));

        // 销毁容器
        context.close();
    }
}
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 {
}
常见的Bean后置处理器3

SpringBoot中的后置处理器

ConfigurationPropertiesBindingPostProcessor
解析:@ConfigurationProperties(prefix = "xxx")
执行时机:初始化前

SpringBoot中的属性绑定功能:根据前缀以及属性名称匹配配置文件中的信息进行绑定

/*  [@ConfigurationProperties,prefix = "java",属性name、version,可以匹配到: ]
    java.home=
    java.version=
 */
@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 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());

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

        System.out.println(context.getBean(Bean1.class));

        // 销毁容器
        context.close();

        /*
            学到了什么
                a. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能
                b. 这些扩展功能由 bean 后处理器来完成
         */
    }
}

7.AutowiredAnnotationBeanPostProcessor

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", new Bean2()); // new Bean2()会认为这个是一个成熟的bean,不会再走 创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        processor.postProcessProperties(null, bean1, "bean1"); //【重点】 2.执行依赖注入,解析@Autowired @Value
        System.out.println(bean1);
    }
}

processor.postProcessProperties 执行过程

image-20220406103735947

不直接使用 processor.postProcessProperties ,自己模拟执行过程

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", new Bean2()); // new Bean2()会认为这个是一个成熟的bean,不会再走 创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
        //【新添加的内容:${} 的解析器】
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); 

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

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

        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);
    }
}

metadata里面有什么?

image-20220406104147866

如何按类型查找值

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", new Bean2()); // new Bean2()会认为这个是一个成熟的bean,不会再走 创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        // 3. 如何按类型查找值
        // 查找属性
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
        //根据成员变量的类型去找要注入谁
        Object o = beanFactory.doResolveDependency(dd1, null, null, null);
        System.out.println(o);

        //查找方法
        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dd2 =
                new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        //根据方法参数的类型(Bean2 bean2)到容器里面去找,看看有没有一个Bean2类型的bean
        Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
        System.out.println(o1);

        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);

    }
}

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

    @Autowired
    private Bean3 bean3;

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

总结:metadata.inject() 做了哪些事情?

1.找到标注了@Autowired的成员变量、方法信息

2.封装成一个DependencyDescriptor

3.调用 beanFactory.doResolveDependency()方法

4.传入参数,根据属性类型、方法参数类型进一步查找

8.BeanFactory后置处理器

BeanFactory后置处理器的作用:为BeanFactory提供扩展

常用的BeanFactory后置处理器

ConfigurationClassPostProcessor

  • 解析@ComponentScan注解、扫描一些组件,注册到bean工厂里面
  • 解析@Bean注解,把@Bena标注的方法,作为BeanDefinition,注册到bean工厂
  • 解析@Import注解
  • 解析@ImportResource注解

MapperScannerConfigurer

  • 根Mybatis整合时经常用到的BeanFactory后置处理器
  • 解析@MapperScan,在SSM项目中常用的注解
  • 扫描Mapper接口,作为BeanDefinition,注册到bean工厂
  • 很少用了,如果用SpringBoot整合,一般间接配置
/*
    BeanFactory 后处理器的作用
 */
public class A05 {
    private static final Logger log = LoggerFactory.getLogger(A05.class);

    public static void main(String[] args) throws IOException {

        // GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
        context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
            bd.getPropertyValues().add("basePackage", "com.itheima.a05.mapper");
        });

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

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

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

@Configuration
@ComponentScan("com.itheima.a05.component")
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }
}
@Mapper
public interface Mapper1 {
}
模拟BeanFactory后处理器解析@ComponentScan
public class A05 {
    private static final Logger log = LoggerFactory.getLogger(A05.class);

    public static void main(String[] args) throws IOException {

        // GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScan

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

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

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

        // 销毁容器
        context.close();
    }
}
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override // context.refresh
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            //【1】使用AnnotationUtils看看Config.class上是不是有注解@ComponentScan
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                    System.out.println(p);
                    // 【2】把获取的 包名 转化为 类路径名
                    // com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    System.out.println(path);
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    //【3】getResources获取类路径下的所有资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        // System.out.println(resource);
                        MetadataReader reader = factory.getMetadataReader(resource);
                        // System.out.println("类名:" + reader.getClassMetadata().getClassName());
                        //【4】查看是不是加了对应的注解
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        // System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                        // System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                        if (annotationMetadata.hasAnnotation(Component.class.getName())
                            || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            //【5】如果直接或间接加了注解,创建BeanDefinition
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            String name = generator.generateBeanName(bd, beanFactory);
                            //【6】加入到bean工厂里面,(bean的名字,bean)
                            beanFactory.registerBeanDefinition(name, bd);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
模拟BeanFactory后处理器解析@Bean
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            //【1】
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            //【2】获取类路径下的 数据元信息 MetadataReader
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a05/Config.class"));
            //【3】寻找标注了@Bean注解的方法集合
            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();
                //【4】定义了一下配置类“config”的工厂方法
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                //【5】定义自动装配模式
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length() > 0) {
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                //【6】注册bean到工厂
                beanFactory.registerBeanDefinition(method.getMethodName(), bd);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
模拟BeanFactory后处理器解析@ComponentScan

Spring不能直接管理 Mapper接口,只能管理对象

需要使用工厂对象,MapperFactoryBean


基本使用方式,缺点:不能批量添加

@Configuration
@ComponentScan("com.itheima.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;
    }

    @Bean
    public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

    @Bean
    public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }
}

改进:

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            //【】扫描路径下有哪些资源
            Resource[] resources = resolver.getResources("classpath:com/itheima/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()) {
                    //生成的是MapperBeanDefinition
                    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, beanFactory);
                    //注册
                    beanFactory.registerBeanDefinition(name, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

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

    }
}

9.Aware接口

Aware接口提供了一种【内置】的注入手段,可以注入BeanFactory, ApplicationContext

InitializingBean接口提供了一种【内置】的初始化手段

内置的注入和初始化不受扩展功能的影响,总会被执行,因此Spring框架内部的类常用它们


Q:Aware 接口有什么作用?

Aware 接口用于注入一些与容器相关信息, 例如

  • a.BeanNameAware 注入 bean 的名字

  • b.BeanFactoryAware 注入 BeanFactory 容器

  • c.ApplicationContextAware 注入 ApplicationContext 容器

  • d.EmbeddedValueResolverAware 解析 ${}

/*
    Aware 接口及 InitializingBean 接口
 */
public class A06 {
    private static final Logger log = LoggerFactory.getLogger(A06.class);

    public static void main(String[] args) {

        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myBean", MyBean.class);
    
        context.refresh();
        context.close();
    }
}
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {

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

    //From:BeanNameAware
    @Override
    public void setBeanName(String name) {
        log.debug("当前bean " + this + " 名字叫:" + name);
    }

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

    //From:InitializingBean
    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("当前bean " + this + " 初始化");
    }
}
Q:部分功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢

有同学说: b、c、d 的功能用 @Autowired 就能实现啊, 为啥还要用 Aware 接口呢

简单地说:

  • a.@Autowired 的解析需要用到 bean 后处理器, 属于扩展功能

  • b.而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别某些情况下, 扩展功能会失效, 而内置功能不会失效

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

例2: Java 配置类在添加了 bean 工厂后处理器后,
你会发现用传统接口方式的注入和初始化仍然成功, 而 @Autowired 和 @PostConstruct 的注入和初始化失败
*/
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {

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

    //From:BeanNameAware
    @Override
    public void setBeanName(String name) {
        log.debug("当前bean " + this + " 名字叫:" + name);
    }

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

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

    //【不生效,需要加AutowiredAnnotationBeanPostProcessor后置处理器】
    @Autowired
    public void aaa(ApplicationContext applicationContext) {
        log.debug("当前bean " + this + " 使用@Autowired 容器是:" + applicationContext);
    }

    //【不生效,需要加@后置处理器】
    @PostConstruct
    public void init() {
        log.debug("当前bean " + this + " 使用@PostConstruct 初始化");
    }
}
public class A06 {
    private static final Logger log = LoggerFactory.getLogger(A06.class);

    public static void main(String[] args) {

        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myBean", MyBean.class);
        //添加后置处理器
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        context.registerBean(ConfigurationClassPostProcessor.class);

        context.refresh(); 
        context.close();
    }
}

Q:Aware接口的执行顺序

1.先执行扩展

2.再执行aware接口方式的依赖注入

3.再执行接口方式的初始化

10.@Autowired失效分析

正常代码

@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后失效,@Autowired、@PostConstruct失效

@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");
        };
    }

}

context.refresh()

1.到容器里找到所有的beanFactory的后置处理器

2.添加bean的后置处理器(bean的扩展功能)

3.初始化所有的单例

image-20220407085031458

image-20220407085002119

解决方法:

使用内置接口

@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {

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

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

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

    @Bean //  beanFactory 后处理器
    public BeanFactoryPostProcessor processor2() {
        return beanFactory -> {
            log.debug("执行 processor2");
        };
    }
}
public class A06 {
    private static final Logger log = LoggerFactory.getLogger(A06.class);

    public static void main(String[] args) {
        
        GenericApplicationContext context = new GenericApplicationContext();
//        context.registerBean("myConfig1", MyConfig1.class);
        context.registerBean("myConfig2", MyConfig2.class);
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        context.registerBean(ConfigurationClassPostProcessor.class);

        context.refresh(); // 1. beanFactory 后处理器,  2. 添加 bean 后处理器, 3. 初始化单例
        context.close();

        /*
            学到了什么
                a. Aware 接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory, ApplicationContext
                b. InitializingBean 接口提供了一种【内置】的初始化手段
                c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们
         */
    }
}

11.Spring初始化和销毁

Q:Spring提供的三种初始化bean的手段

第一种:实现 InitializingBean 接口,重写 afterPropertiesSet() 方法 【再执行】

第二种:使用 @PostConstruct 注解 【首先执行】

第三种:使用 @Bean(initMethod = “init3”) 注解 【最后执行】

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

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

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

    public void init3() {
        log.debug("初始化3");
    }
}
@SpringBootApplication
public class A07_1 {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A07_1.class, args);
        context.close();
    }

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

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

12.Scope

Q:Scope类型有哪些
作用域描述
singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
prototype每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
application限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。
Q:演示 request, session, application 作用域
/*
    singleton, prototype, request, session, application

    jdk >= 9 如果反射调用 jdk 中方法,会IllegalAccessException
    jdk <= 8 不会有问题

    演示 request, session, application 作用域
    打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
    如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
 */
@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 销毁机制疑似实现有误
         */
    }
}
@Scope("application")
@Component
public class BeanForApplication {
    private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);

    @PreDestroy
    public void destroy() {
        log.debug("destroy");
    }
}
@Scope("request")
@Component
public class BeanForRequest {
    private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);

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

}
@Scope("session")
@Component
public class BeanForSession {
    private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);

    @PreDestroy
    public void destroy() {
        log.debug("destroy");
    }
}
@RestController
public class MyController {

    @Lazy
    @Autowired
    private BeanForRequest beanForRequest;

    @Lazy
    @Autowired
    private BeanForSession beanForSession;

    @Lazy
    @Autowired
    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;
    }

}

image-20220407091200318

Q:Scope创建和销毁时机

单例:

  • Spring容器创建时会把所有的单例创建出来

  • Spring容器关闭时

多例:

  • 每次使用时会创建

  • 不会由Spring容器进行管理,我们可以自己调用销毁方法

Request:

  • 请求来的时候创建Request域的请求对象放入Web的Request域中

  • 请求结束的时候销毁(调用销毁的方法)

Session:

  • 会话时,会创建Session对象,放入Session域中

  • 会话结束时 或 Session超时(一段时间内没有向服务器发送请求),默认30min,在配置文件中配置 server.servlet.session.timeout=10s,这并不是严格的时间,因为一次session检查就可能超过10s,这里等了一分钟

Application:

  • 当首次使用ServletContext的时候,创建并放入Appliaction域中

  • 预测在关闭服务器时调用销毁方法,但是SpringBoot好像没有正确实现?

image-20220407091845770

Q:Scope失效问题

在单例的bean中,如果要注入其他作用域的bean的时候,会失效

常见的解决方法是加 @Lazy注解


以单例注入多例为例

有一个单例对象 E

@Component
public class E {

    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}

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

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

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

测试

/*
    如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED
 */
@ComponentScan("com.itheima.a08.sub")
public class A08_1 {

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

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(A08_1.class);

        E e = context.getBean(E.class);

        log.debug("{}", e.getF1().getClass());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());
        log.debug("{}", e.getF1());
    }
}

输出

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

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


问题出现的原因

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

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

    @Lazy
    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}

注意

@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 对象的类型是代理类型


解决方法2:@Scope

在注入的目标上使用 @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
@Component
public class E {

    @Autowired
    private F2 f2;
   
    public F2 getF2() {
        return f2;
    }
}
log.debug("{}", e.getF2().getClass());
log.debug("{}", e.getF2());
log.debug("{}", e.getF2());
log.debug("{}", e.getF2());
解决方法3:ObjectFactory

对象工厂,声明工厂类型的成员变量,到时候注入的不是F3,而是注入F3的对象工厂

先拿到工厂,再从工厂里拿F3,f3.getObject()

@Component
public class E {

    @Autowired
    private ObjectFactory<F3> f3;

    public F3 getF3() {
        return f3.getObject();
    }
}
@Scope("prototype")
@Component
public class F3 {
}
log.debug("{}", e.getF3());
log.debug("{}", e.getF3());
解决方法4:ApplicationContext

注入Spring容器ApplicationContext

直接调用getBean方法获取任意对象

@Component
public class E {
    
    @Autowired
    private ApplicationContext context;
    
    public F4 getF4() {
        return context.getBean(F4.class);
    }
}
@Scope("prototype")
@Component
public class F4 {
}
log.debug("{}", e.getF4());
log.debug("{}", e.getF4());
强行总结

解决方法虽然不同,但理念上殊途同归:都是推迟其它scope bean 的获取

  • @Lazy
  • @Scope(value = “prototype”, proxyMode = ScopedProxyMode.TARGET_CLASS)
  • ObjectFactory
  • ApplicationContext
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值