前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Spring注解驱动开发】自定义组件如何注入Spring底层的组件?看了这篇我才真正理解了原理!!

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

作者头像
冰河
发布2020-10-29 10:35:17
5220
发布2020-10-29 10:35:17
举报
文章被收录于专栏:冰河技术

作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:

https://github.com/sunshinelyz/mykit-delay

PS: 欢迎各位Star源码,也可以pr你牛逼哄哄的代码。

写在前面

最近,很多小伙伴出去面试都被问到了Spring问题,关于Spring,细节点很多,面试官也非常喜欢问一些很细节的技术点。所以,在 Spring 专题中,我们尽量把Spring的每个技术细节说清楚,将透彻。 关注 冰河技术 微信公众号,回复 “ Spring注解 ” 关键字领取源码。 如果文章对你有所帮助,欢迎大家留言、点赞、在看和转发,大家的支持是我持续创作的动力!

概述

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

XxxAware接口概览

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

代码语言:javascript
复制
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接口的源码,如下所示。

代码语言:javascript
复制
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输出,如下所示。

代码语言:javascript
复制
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容器中的名称,如下所示。

代码语言:javascript
复制
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解析器。如下所示。

代码语言:javascript
复制
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容器中,如下所示。

代码语言:javascript
复制
@Component
public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

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

代码语言:javascript
复制
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类的销毁方法。。。。。

输出结果中有如下信息。

代码语言:javascript
复制
当前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接口,本质上都是后置处理器。

代码语言:javascript
复制
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()方法中就不用说了吧!

其实就是这么简单!

・ω・。)ノ♡

写留言

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 冰河技术 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 概述
  • XxxAware接口概览
  • XxxAware接口案例
  • XxxAware原理
  • ・ω・。)ノ♡
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档