zoukankan      html  css  js  c++  java
  • 【Spring注解驱动开发】自定义组件如何注入Spring底层的组件?看了这篇我才真正理解了原理!!

    写在前面

    最近,很多小伙伴出去面试都被问到了Spring问题,关于Spring,细节点很多,面试官也非常喜欢问一些很细节的技术点。所以,在 Spring 专题中,我们尽量把Spring的每个技术细节说清楚,将透彻。

    关注 冰河技术 微信公众号,回复 “ Spring注解 ” 关键字领取源码。

    如果文章对你有所帮助,欢迎大家留言、点赞、在看和转发,大家的支持是我持续创作的动力!

    概述

    自定义组件要想使用Spring容器底层的一些组件(比如:ApplicationContext、BeanFactory等),此时,只需要让自定义组件实现XxxAware接口即可。此时,Spring在创建对象的时候,会调用XxxAware接口定义的方法,注入相关的组件。

    XxxAware接口概览

    其实,我们之前使用过XxxAware接口,例如,我们之前创建的Employee类,就实现了ApplicationContextAware接口,Employee类的源码如下所示。

    package io.mykit.spring.plugins.register.bean;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试ApplicationContextAware
     */
    @Component
    public class Employee implements ApplicationContextAware {
        private ApplicationContext applicationContext;
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    从Employee类的源码可以看出,实现ApplicationContextAware接口的话,需要实现setApplicationContext()方法。在IOC容器启动并创建Employee对象时,Spring会调用setApplicationContext()方法,并且会将ApplicationContext对象传入到setApplicationContext()方法中,我们只需要在Employee类中定义一个ApplicationContext类型的成员变量来接收setApplicationContext()方法的参数,就可以使用ApplicationContext对象了。

    其实,在Spring中,类似于ApplicationContextAware接口的设计有很多,本质上,Spring中类似XxxAware接口都继承了Aware接口,我们来看下Aware接口的源码,如下所示。

    package org.springframework.beans.factory;
    /**
     * @author Chris Beams
     * @author Juergen Hoeller
     * @since 3.1
     */
    public interface Aware {
    
    }
    

    可以看到,Aware接口是Spring 3.1版本中引入的接口,在Aware接口中,并未定义任何方法。

    接下来,我们看看都有哪些接口继承了Aware接口,如下所示。

    XxxAware接口案例

    接下来,我们就挑选几个常用的XxxAware接口来进行简单的说明。

    ApplicationContextAware接口使用的比较多,我们先来说说这个接口,通过ApplicationContextAware接口我们可以获取到IOC容器。

    首先,我们创建一个Blue类,并实现ApplicationContextAware接口,在实现的setApplicationContext()中将ApplicationContext输出,如下所示。

    package io.mykit.spring.plugins.register.bean;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试ApplicationContextAware接口
     */
    public class Blue implements ApplicationContextAware {
        private ApplicationContext applicationContext
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("传入的ioc:" + applicationContext);
            this.applicationContext = applicationContext;
        }
    }
    

    我们也可以为Blue类同时实现几个XxxAware接口,例如,使Blue类再实现一个BeanNameAware接口,我们可以通过BeanNameAware接口获取到当前bean在Spring容器中的名称,如下所示。

    package io.mykit.spring.plugins.register.bean;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试ApplicationContextAware接口
     */
    public class Blue implements ApplicationContextAware, BeanNameAware {
        private ApplicationContext applicationContext
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("传入的ioc:" + applicationContext);
            this.applicationContext = applicationContext;
        }
    
        @Override
        public void setBeanName(String name) {
            System.out.println("当前bean的名字");
        }
    }
    

    接下来,我们再实现一个EmbeddedValueResolverAware接口,我们通过EmbeddedValueResolverAware接口能够获取到StringValue解析器。如下所示。

    package io.mykit.spring.plugins.register.bean;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.EmbeddedValueResolverAware;
    import org.springframework.util.StringValueResolver;
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试ApplicationContextAware接口
     */
    public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
        private ApplicationContext applicationContext;
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("传入的ioc:" + applicationContext);
            this.applicationContext = applicationContext;
        }
    
        @Override
        public void setBeanName(String name) {
            System.out.println("当前bean的名字");
        }
    
        @Override
        public void setEmbeddedValueResolver(StringValueResolver resolver) {
            String resolveStringValue = resolver.resolveStringValue("你好${os.name} 年龄:#{20*18}");
            System.out.println("解析后的字符串为:" + resolveStringValue);
        }
    }
    

    接下来,我们需要在Blue类上标注@Component注解将Blue类添加到IOC容器中,如下所示。

    @Component
    public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    

    运行AutowiredTest类的testAutowired02()方法,输出的结果信息如下所示。

    postProcessBeforeInitialization...autowiredConfig=>io.mykit.spring.plugins.register.config.AutowiredConfig$$EnhancerBySpringCGLIB$$d3c83622@1c93084c
    postProcessAfterInitialization...autowiredConfig=>io.mykit.spring.plugins.register.config.AutowiredConfig$$EnhancerBySpringCGLIB$$d3c83622@1c93084c
    postProcessBeforeInitialization...personDao=>PersonDao{remark='1'}
    postProcessAfterInitialization...personDao=>PersonDao{remark='1'}
    postProcessBeforeInitialization...personDao2=>PersonDao{remark='2'}
    postProcessAfterInitialization...personDao2=>PersonDao{remark='2'}
    postProcessBeforeInitialization...personService=>PersonService{personDao=PersonDao{remark='2'}}
    postProcessAfterInitialization...personService=>PersonService{personDao=PersonDao{remark='2'}}
    postProcessBeforeInitialization...personController=>io.mykit.spring.plugins.register.controller.PersonController@48ae9b55
    postProcessAfterInitialization...personController=>io.mykit.spring.plugins.register.controller.PersonController@48ae9b55
    执行了Animal类的无参数构造方法
    postProcessBeforeInitialization...animal=>io.mykit.spring.plugins.register.bean.Animal@c267ef4
    执行了Animal类的初始化方法。。。。。
    postProcessAfterInitialization...animal=>io.mykit.spring.plugins.register.bean.Animal@c267ef4
    当前bean的名字:blue
    解析后的字符串为:你好Windows 10 年龄:360
    传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@5ecddf8f, started on Wed Aug 19 00:10:13 CST 2020
    postProcessBeforeInitialization...blue=>io.mykit.spring.plugins.register.bean.Blue@55182842
    postProcessAfterInitialization...blue=>io.mykit.spring.plugins.register.bean.Blue@55182842
    Cat类的构造方法...
    postProcessBeforeInitialization...cat=>io.mykit.spring.plugins.register.bean.Cat@76505305
    Cat的postConstruct()方法...
    postProcessAfterInitialization...cat=>io.mykit.spring.plugins.register.bean.Cat@76505305
    调用了Dog的有参构造方法
    postProcessBeforeInitialization...dog=>Dog{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
    postProcessAfterInitialization...dog=>Dog{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
    postProcessBeforeInitialization...employee=>io.mykit.spring.plugins.register.bean.Employee@74235045
    postProcessAfterInitialization...employee=>io.mykit.spring.plugins.register.bean.Employee@74235045
    postProcessBeforeInitialization...fish=>Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
    postProcessAfterInitialization...fish=>Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
    Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
    Cat的preDestroy()方法...
    执行了Animal类的销毁方法。。。。。
    

    输出结果中有如下信息。

    当前bean的名字:blue
    解析后的字符串为:你好Windows 10 年龄:360
    传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@5ecddf8f, started on Wed Aug 19 00:10:13 CST 2020
    

    说明正确的输出了结果信息。

    XxxAware原理

    XxxAware的底层原理是由XxxAwareProcessor类实现的, 例如,我们这里以ApplicationContextAware接口为例,ApplicationContextAware接口的底层原理就是由ApplicationContextAwareProcessor类实现的。从ApplicationContextAwareProcessor类的源码可以看出,其实现了BeanPostProcessor接口,本质上都是后置处理器。

    class ApplicationContextAwareProcessor implements BeanPostProcessor
    

    接下来,我们就以分析ApplicationContextAware接口的原理为例,看看Spring是怎么将ApplicationContext对象注入到Blue类中的。

    我们在Blue类的setApplicationContext()方法上打一个断点,如下所示。

    接下来,我们以debug的方式来运行AutowiredTest类的testAutowired02()方法,

    这里,我们可以看到,实际上ApplicationContext对象已经注入到Blue类中的setApplicationContext()方法中了。我们在IDEA的方法调用栈中选择postProcessBeforeInitialization()方法,如下所示。

    我们双击IDEA中的postProcessBeforeInitialization()方法的调用栈,会在IDEA中自动定位到postProcessBeforeInitialization()方法中,如下所示。

    其实,postProcessBeforeInitialization()方法所在的类就是ApplicationContextAwareProcessor。postProcessBeforeInitialization()方法的逻辑比较简单。

    我们来看下在postProcessBeforeInitialization()方法中调用的invokeAwareInterfaces()方法,如下所示。

    看到这里,大家是不是有种豁然开朗的感觉!Blue类实现了ApplicationContextAware接口后,Spring为啥会将ApplicationContext对象自动注入到setApplicationContext()方法中就不用说了吧!

    其实就是这么简单!

    重磅福利

    关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深入浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!!

    好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一起学习,一起进步!!

    写在最后

    如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫!

  • 相关阅读:
    [LeetCode] Exclusive Time of Functions 函数的独家时间
    [LeetCode] Design Log Storage System 设计日志存储系统
    [LeetCode] Find the Derangement of An Array 找数组的错排
    [LeetCode] 632. Smallest Range Covering Elements from K Lists 覆盖K个列表元素的最小区间
    [LeetCode] 633. Sum of Square Numbers 平方数之和
    [LeetCode] Design Excel Sum Formula 设计Excel表格求和公式
    [LeetCode] Delete Operation for Two Strings 两个字符串的删除操作
    [LeetCode] Kill Process 结束进程
    [LeetCode] Course Schedule III 课程清单之三
    [LeetCode] K Inverse Pairs Array K个翻转对数组
  • 原文地址:https://www.cnblogs.com/binghe001/p/13527001.html
Copyright © 2011-2022 走看看