前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >不一样的视角来学习Spring源码之容器与Bean---上

不一样的视角来学习Spring源码之容器与Bean---上

作者头像
大忽悠爱学习
发布2022-05-10 16:10:14
4310
发布2022-05-10 16:10:14
举报
文章被收录于专栏:c++与qt学习

1) 容器接口

  • BeanFactory 接口,典型功能有:
代码语言:txt
复制
- getBeanApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:
代码语言:txt
复制
- 国际化
- 通配符方式获取一组 Resource 资源
- 整合 Environment 环境(能通过它获取各种来源的配置信息)
- 事件发布与监听,实现组件之间的解耦

BeanFactory 与 ApplicationContext 的区别

  1. 到底什么是 BeanFactory
代码语言:txt
复制
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactoryBeanFactory 能干点啥
代码语言:txt
复制
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
- 下面例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean

BeanFactory只是一个接口,我们需要的是它的实现类,BeanFactory默认实现类为DefaultListableBeanFactory

下面我们尝试反射调用该成员变量:

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ConfigurableApplicationContext context = SpringApplication.run(OneMain.class, args);

        //默认单例bean的注册中心
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //获取在beanFactory实例上singletonObjects属性集合
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        //对Bean名称进行过滤,不然会有很多的单例bean
        map.entrySet().stream().filter(e->e.getKey().startsWith("dhy")).forEach(e->{
            System.out.println(e.getKey()+" = "+e.getValue());
        });
    }
}

3.ApplicationContext 比 BeanFactory 多点啥

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

国际化

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

messages.properties(空)

messages_en.properties

代码语言:javascript
复制
hi=Hello

messages_zh.properties

代码语言:javascript
复制
hi=你好

注意

  • ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
  • 使用 SpringBoot 时,国际化文件名固定为 messages
  • 空的 messages.properties 也必须存在
代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        GenericApplicationContext context=new GenericApplicationContext();
         //自定义一个bean注册到容器中
        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));
    }
}

资源获取

详细参考

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        GenericApplicationContext context=new GenericApplicationContext();
        //寻找多个资源,底层使用ResourcePatternResolver
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (int i = 0; i < resources.length; i++) {
            System.out.println(resources[i]);
        }
        //查找单个资源--底层使用DefaultResourceLoader
        Resource resource = context.getResource("classpath:messages.properties");
        System.out.println(resource);
    }
}

环境

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        GenericApplicationContext context=new GenericApplicationContext();
        String java_home = context.getEnvironment().getProperty("java_home");
        System.out.println(java_home);
    }
}

事件

自定义一个事件发布对象:

代码语言:javascript
复制
public class UEvent extends ApplicationEvent {
    public UEvent(Object source) {
        super(source);
    }
}

发布事件:

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(OneMain.class, args);
        //我们只要获取到了context对象,就可以在应用程序执行期间发布事件
        applicationContext.publishEvent(new UEvent("你好,我是自定义事件"));
    }
}

监听器:

代码语言:javascript
复制
@Component
public class UListener {
    @EventListener
    public void listener(UEvent event)
    {
        System.out.println(event.getSource());
    }
}

详细参考


2) 容器实现

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

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

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


DefaultListableBeanFactory

使用演示:

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();
       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);
        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
    }

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

BeanFactory自己为什么不能解析bean里面的@Bean和 @Configuration注解呢?

为什么只有config被注册到了容器中,config里面的@Bean注解并没有被解析,显然BeanFactory并没有解析这些注解的能力,那么这些能力是who提供的呢?

代码语言:javascript
复制
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();
       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->{beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        });

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
    }

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

Spring的BeanFactory原始功能并没有那么丰富,但是可以通过后置处理器来增强BeanFactory的功能


BeanFactory自己为什么不能从bean定义中扫描到@Autowired注解,并进行自动依赖注入呢?

上面添加的bean工厂的后置处理器是用来解析一些例如@Configuration,@Bean等注解

我们还需要让Bean的后置处理器开始工作,Bean的后置处理器一般工作在Bean的生命周期过程中

代码语言:javascript
复制
public class Bean2 {
    @Autowired
    private Bean1 bean1;

    public Bean2() {
        System.out.println("Bean2构造中...");
    }

    public Bean1 getBean1() {
        return bean1;
    }
}
代码语言:javascript
复制
public class Bean1 {
    public Bean1() {
        System.out.println("Bean1构造中...");
    }
}
代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();

       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->{beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        });

        //添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

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

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

使用到bean才会去创建,这显然表明BeanFactory是懒加载的


BeanFactory设置预先实例化所有的Bean

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();

       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->{beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        });

        //添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
        
        //预先实例化所有的bean
        beanFactory.preInstantiateSingletons();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

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

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

小结

  • 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 后处理的添加顺序会对解析结果有影响

后置处理器都实现了该接口,确保可以保证不同后置处理的执行顺序

order越小,优先级越高

  • beanFactory 需要手动调用方法来初始化单例
  • beanFactory 需要额外设置才能解析 ${} 与 #{}

常见 ApplicationContext 实现

从类路径下加载配置文件:

代码语言:javascript
复制
        ClassPathXmlApplicationContext xmlApplicationContext=new ClassPathXmlApplicationContext("application.xml");

从磁盘路径加载配置文件:

代码语言:javascript
复制
        FileSystemXmlApplicationContext fileSystemXmlApplicationContext=new FileSystemXmlApplicationContext("D:application.xml");

使用DefaultListableBeanFactory作为bean的注册中心,XmlBeanDefinitionReader从配置文件读取配置信息,注册到DefaultListableBeanFactory中

代码语言:javascript
复制
        DefaultListableBeanFactory listableBeanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(listableBeanFactory);
       // reader.loadBeanDefinitions(new ClassPathResource("application.xml"));
          reader.loadBeanDefinitions(new FileSystemResource("D:application.xml"));

基于注解方式的: 通过@Configuration标注的配置类替代原来的配置文件

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(Config.class);
        String[] definitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        System.out.println("已经注册的bean定义有: ");
        for (int i = 0; i < definitionNames.length; i++) {
            System.out.println(definitionNames[i]);
        }
    }

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

因为默认注册了这些后置处理器,才可以去解析这些注解


xml配置文件中注册上面的后置处理器

代码语言:javascript
复制
    <context:annotation-config/>

这个标签的作用就是往容器中注入上面那些后置处理器


用于web环境的容器:

代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        AnnotationConfigServletWebApplicationContext context=new AnnotationConfigServletWebApplicationContext(WebConfig.class);
    }
    
    @Configuration
    static class WebConfig{
        @Bean
        public ServletWebServerFactory webServerFactory(){
            //tomcat环境下的web服务器工厂
            return new TomcatServletWebServerFactory();
        }
        @Bean
        public DispatcherServlet dispatcherServlet(){
            //负责请求派发的servlet
            return new DispatcherServlet();
        }
        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(){
            //注册上的servlet---拦截所有路径
            return new DispatcherServletRegistrationBean(dispatcherServlet(),"/");
        }

        /**
         * 把bean的名称作为访问路径
         */
        @Bean("/hello")
        public Controller controller(){
            //DispatcherServlet拦截到请求后,将请求转发给对应的controller
            return new Controller() {
                @Override
                public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
                    httpServletResponse.getWriter().println("大忽悠");
                    return null;
                }
            };
        }
    }
}

大家可以顺便联系一下springboot中内嵌tomcat是如何启动的


3) Bean 的生命周期

先看下面这段代码运行结果:

代码语言:javascript
复制
@Component("lifeCycleBean")
public class TestBean {
    public TestBean() {
        System.out.println("testBean的构造方法被调用");
    }

    @Autowired
    public void autowired(@Value("${JAVA_HOME}") String home)
    {
        System.out.println("依赖注入得到的JAVA_HOME结果为: "+home);
    }

    @PostConstruct
    public void init(){
        System.out.println("初始化方法");
    }

    @PreDestroy
    public void destory()
    {
        System.out.println("销毁");
    }
}
代码语言:javascript
复制
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(OneMain.class, args);
        applicationContext.close();
    }

}

ApplicationContext会在容器启动的时候自动初始化所有的单例Bean

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

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

一些资料会提到,生命周期中还有一类 bean 后处理器:BeanPostProcessor,会在 bean 的初始化的前后,提供一些扩展逻辑。但这种说法是不完整的,见下面的演示1


bean 生命周期

ApplicationContext会自动识别容器中的BeanPostProcessor

代码语言:javascript
复制
@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");
        //返回null,表示不进行替换
        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;
    }
}

小结

创建前后的增强

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

依赖注入前的增强

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

初始化前后的增强

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

销毁之前的增强

  • postProcessBeforeDestruction
    • 如 @PreDestroy

收获💡

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

模板方法设计模式

看问题:

代码语言:javascript
复制
public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("初始化 " + bean);
            return bean;
        }
    }
}

我们通过伪代码简单模拟了一下构造,依赖注入和初始化的流程,这个流程是不变的,因此可以看做是一个模板过程

但是,如果我们在依赖注入过程中,一开始只支持了@Autowired注解的解析,现在又要增加对 @Resource注解的解析,我们可以写成下面这样子:

代码语言:javascript
复制
public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("对Autowired注解进行解析...");
            System.out.println("对Resource注解进行解析...");
            System.out.println("初始化 " + bean);
            return bean;
        }
    }
}

那现在我们又要增加对Inject注解的解析,该怎么办呢? 难不成写成下面这样子?

代码语言:javascript
复制
public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("对Autowired注解进行解析...");
            System.out.println("对Resource注解进行解析...");
            System.out.println("对Inject注解进行解析...");
            System.out.println("初始化 " + bean);
            return bean;
        }
    }
}

这不是意味着后面如果我们想要增强功能,就需要改动getBean方法来提供对新功能的支持?

这显然是非常错误的做法,那么有没有什么方法可以很好的改进这个错误做法呢? ----模板方法模式

代码语言:javascript
复制
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.addBeanPostProcessor(bean -> System.out.println("解析 @Inject"));
        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); // 对依赖注入阶段的扩展
    }
}

无论功能如何增强,我们都可以通过增加后置处理器来巧妙解决这个问题,是不是很方便?

模板方法就是静中有动,即整个流程中有些操作是固定不变的,但是有些操作是不确定的,我们可以用抽象方法来或者接口中的抽象方法来提供,参考上面的代码。


常见的Bean的后置处理器

代码语言:javascript
复制
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 + '\'' +
               '}';
    }
}
代码语言:javascript
复制
public class Bean2 {
}
代码语言:javascript
复制
public class Bean3 {
}
代码语言:javascript
复制
@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 + '\'' +
               '}';
    }
}

这里属性绑定,会从配置文件和环境变量两个地方找,因为这里java.home和java.version在环境变量中都存在,因此我们不需要在配置文件中配置任何内容,这里就可以绑定

代码语言:javascript
复制
public class A04 {
    public static void main(String[] args) {
        // ⬇️GenericApplicationContext 是一个【干净】的容器
        //不会像AnnotationConfigApplicationContext一样自动给我们添加一些后置处理器
        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);

        //配合autowired和value注解解析值注入类型,例如string类型的值注入
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

        // @Autowired @Value
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

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

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

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

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

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

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

注意该解析器不是用来解析{}大括号的,而是解析这种值注入类型的,否则会把String当做bean对象看待,如上所示: 这河里吗?

当然该解析器不只这个功能,目前通过我们的测试,只发现了这个功能,后续我们会专门探讨一下该解析器


收获💡
  1. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成
  2. 每个后处理器各自增强什么功能
    • AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @Value
    • CommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroy
    • ConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties
  3. 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等

AutowiredAnnotationBeanPostProcessor后置处理器分析—负责解析autowired注解和value注解

代码语言:javascript
复制
// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value---值解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        //与Bean工厂关联----需要到容器找寻找一些Bean进行依赖注入
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //此时后置处理器没有工作
        System.out.println(bean1);

        //执行依赖注入@Autowired @Value
        //第一个参数是手动指定值,而不通过依赖注入,这里我们不需要手动指定,而是通过去容器中找,进行依赖注入
        //postProcessProperties方法会在bean生命周期中的依赖注入阶段被执行
        processor.postProcessProperties(null,bean1,"bean1");
        System.out.println(bean1);

    }
}

看一下postProcessProperties方法的源码:

代码语言:javascript
复制
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value---值解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        //与Bean工厂关联----需要到容器找寻找一些Bean进行依赖注入
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //此时后置处理器没有工作
        System.out.println(bean1);

        //执行依赖注入@Autowired @Value
        //第一个参数是手动指定值,而不通过依赖注入,这里我们不需要手动指定,而是通过去容器中找,进行依赖注入
      // processor.postProcessProperties(null,bean1,"bean1");
//        System.out.println(bean1);

        //反射调用findAutowiringMetadata方法,来获取返回值封装的InjectionMetadata信息
        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class,
                Class.class, PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);
        // 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
        InjectionMetadata injectionMetadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
        System.out.println(injectionMetadata);
    }

由于InjectionMetadata 没有重写toString方法,因此我们通过断点查看


调用InjectionMetadata的inject方法,完成依赖注入

代码语言:javascript
复制
// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value---值解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        //与Bean工厂关联----需要到容器找寻找一些Bean进行依赖注入
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //此时后置处理器没有工作
        System.out.println(bean1);

        //执行依赖注入@Autowired @Value
        //第一个参数是手动指定值,而不通过依赖注入,这里我们不需要手动指定,而是通过去容器中找,进行依赖注入
      // processor.postProcessProperties(null,bean1,"bean1");
//        System.out.println(bean1);

        //反射调用findAutowiringMetadata方法,来获取返回值封装的InjectionMetadata信息
        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class,
                Class.class, PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);
        // 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
        InjectionMetadata injectionMetadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);

        //调用InjectionMetadata的inject方法,完成依赖注入
        injectionMetadata.inject(bean1,"bean1",null);
        System.out.println(bean1);
    }
}

injectionMetadata中的inject方法的源码分析:

代码语言:javascript
复制
	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			for (InjectedElement element : elementsToIterate) {
			//调用InjectedElement 的inject方法---这里实际上应该是AutowiredFieldElement
				element.inject(target, beanName, pvs);
			}
		}
	}

AutowiredFieldElement 是AutowiredAnnotationBeanPostProcessor的一个内部类,它里面的inject方法

代码语言:javascript
复制
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {

//inject方法源码:
@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
				try {
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Unexpected removal of target bean for cached argument -> re-resolve
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}

resolveFieldValue方法中都会用到一个叫做DependencyDescriptor的依赖注入描述符号

成员变量依赖bean查找:

代码语言:javascript
复制
        //3.按照类型查找
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        //封装依赖关系---->需要bean3类型的bean,并且required=false
        DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(bean3, false);
       //目标类型的bean---通过依赖关系,去容器中找到该类型的bean
        Object targetBean = beanFactory.doResolveDependency(dependencyDescriptor, null, null,null);
        System.out.println(targetBean);

方法参数依赖查找:

这里是按照方法参数为单位去容器中寻找对应的依赖关系

代码语言:javascript
复制
        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        //封装方法参数描述符
        DependencyDescriptor dd2 =
                new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        //解析,获取依赖的bean
        Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
        System.out.println(o1);

如果不像容器中放入bean2类型的bean对象:

代码语言:javascript
复制
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
       // beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());

值注入

代码语言:javascript
复制
        Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
        DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), false);
        Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
        System.out.println(o2);

这里是去环境变量中寻找JAVA_HOME的值


收获💡
  1. AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionMetadata
  2. InjectionMetadata 可以完成依赖注入
  3. InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
  4. 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找

未完待续…

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring源码学习一之容器与Bean---上
  • 1) 容器接口
    • BeanFactory 与 ApplicationContext 的区别
      • 国际化
      • 资源获取
      • 环境
      • 事件
  • 2) 容器实现
    • DefaultListableBeanFactory
      • BeanFactory自己为什么不能解析bean里面的@Bean和 @Configuration注解呢?
      • BeanFactory自己为什么不能从bean定义中扫描到@Autowired注解,并进行自动依赖注入呢?
      • BeanFactory设置预先实例化所有的Bean
      • 小结
    • 常见 ApplicationContext 实现
    • 3) Bean 的生命周期
      • bean 生命周期
        • 小结
        • 收获💡
      • 模板方法设计模式
        • 常见的Bean的后置处理器
          • AutowiredAnnotationBeanPostProcessor后置处理器分析—负责解析autowired注解和value注解
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档