手写Spring8(Aware感知容器变化)

news2024/9/8 3:56:28

文章目录

  • 目标
  • 设计思想
  • 项目结构
  • 一、实现
    • 1、定义标记接口
    • 2、容器感知类
      • 2.1、BeanFactoryAware
      • 2.2、BeanFactoryAware
      • 2.3、BeanNameAware
      • 2.4、ApplicationContextAware
    • 3、包装处理器(ApplicationContextAwareProcessor)
    • 4、注册 BeanPostProcessor
    • 5、感知调用操作
  • 二、测试
    • 1、事先准备
    • 2、配置文件
    • 3、单元测试
  • 总结


目标

本章目标实现获得Spring 框架提供的 BeanFactory、ApplicationContext、BeanClassLoader、ApplicationContextAware等这些能力.,并且还知道他们都是什么时候触发的,在以后想排查类的实例化顺序也可以有一个清晰的思路了。

在 Spring 框架中提供一种能感知容器操作的接口,如果谁实现了这样的一个接口,就可以获取接口入参中的各类能力


设计思想

如果说我希望拿到 Spring 框架中一些提供的资源,那么首先需要考虑以一个什么方式去获取,之后你定义出来的获取方式,在 Spring 框架中该怎么去承接,实现了这两项内容,就可以扩展出你需要的一些属于 Spring 框架本身的能力了。

在关于 Bean 对象实例化阶段我们操作过一些额外定义、属性、初始化和销毁的操作,其实我们如果像获取 Spring 一些如 BeanFactory、ApplicationContext 时,也可以通过此类方式进行实现

那么我们需要定义一个标记性的接口,这个接口不需要有方法,它只起到标记作用就可以,而具体的功能由继承此接口的其他功能性接口定义具体方法,最终这个接口就可以通过 instanceof 进行判断和调用了。整体设计结构如下图
在这里插入图片描述


项目结构

在这里插入图片描述

在这里插入图片描述


一、实现

1、定义标记接口

/**
 * 标记类接口,实现该接口可以被Spring容器感知
 */
public interface Aware {

}

在 Spring 中有特别多类似这样的标记接口的设计方式,它们的存在就像是一种标签一样,可以方便统一摘取出属于此类接口的实现类,通常会有 instanceof 一起判断使用。


2、容器感知类

2.1、BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

实现此接口,就能感知到所属的 BeanFactory


2.2、BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}


实现此接口,既能感知到所属的 ClassLoader


2.3、BeanNameAware

public interface BeanNameAware extends Aware{

    void setBeanName(String name);

}

实现此接口,既能感知到所属的 BeanName


2.4、ApplicationContextAware

public interface ApplicationContextAware extends Aware{

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

实现此接口,既能感知到所属的 ApplicationContext

3、包装处理器(ApplicationContextAwareProcessor)

public class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ApplicationContext applicationContext;

    public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(applicationContext);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

由于 ApplicationContext 的获取并不能直接在创建 Bean 时候就可以拿到,所以需要在 refresh 操作时,把 ApplicationContext 写入到一个包装的 BeanPostProcessor 中去,再由 AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization 方法调用


4、注册 BeanPostProcessor

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException {
        // 1. 创建 BeanFactory,并加载 BeanDefinition
        refreshBeanFactory();

        // 2. 获取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        // 4. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
        invokeBeanFactoryPostProcessors(beanFactory);

        // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
        registerBeanPostProcessors(beanFactory);

        // 6. 提前实例化单例Bean对象
        beanFactory.preInstantiateSingletons();
    }
    
 	// ...   
}    

refresh() 方法就是整个 Spring 容器的操作过程,与上一章节对比,本次新增加了关于 addBeanPostProcessor 的操作。

添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext。


5、感知调用操作

因为这个类代码太多,这里只展示展示本章相关类的代码,

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // 给 Bean 填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
            // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // 注册实现了 DisposableBean 接口的 Bean 对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        addSingleton(beanName, bean);
        return bean;
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {

        // invokeAwareMethods
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }

        // 1. 执行 BeanPostProcessor Before 处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 执行 Bean 对象的初始化方法
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        // 2. 执行 BeanPostProcessor After 处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }



    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

}

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {

    /**
     * ClassLoader to resolve bean class names with, if necessary
     */
    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

    //...

    public ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }
}

1、在AbstractAutowireCapableBeanFactory 的initializeBean (初始化方法)中添加判断,通过判断 bean instanceof Aware,调用了三个接口方法,BeanFactoryAware.setBeanFactory(this)、BeanClassLoaderAware.setBeanClassLoader(getBeanClassLoader())、BeanNameAware.setBeanName(beanName),这样就能通知到已经实现了此接口的类。

2、在AbstractBeanFactory抽象类中添加beanClassLoader和提供get方法,

3、另外我们还向 BeanPostProcessor 中添加了 ApplicationContextAwareProcessor,此时在这个方法中也会被调用到具体的类实现,得到一个 ApplicationContex 属性


二、测试

1、事先准备

public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    public void initDataMethod(){
        System.out.println("UserDao xml的实现, 执行:init-method");
        hashMap.put("10001", "ljc");
        hashMap.put("10002", "yaya");
        hashMap.put("10003", "zz");
    }

    public void destroyDataMethod(){
        System.out.println("UserDao xml的实现, 执行:destroy-method");
        hashMap.clear();
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }

}
public class UserService implements BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware {

    private ApplicationContext applicationContext;
    private BeanFactory beanFactory;

    private String uId;
    private String company;
    private String location;
    private UserDao userDao;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("Bean Name is:" + name);
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("ClassLoader:" + classLoader);
    }

    // ...get/set
}

UserDao 本次并没有什么改变,还是提供了关于初始化的方法,并在 Spring.xml 中提供 init-method、destroy-method 配置信息。

UserService 新增加,BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware,四个感知的实现类,并在类中实现相应的接口方法


2、配置文件

基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="userDao" class="springframework.test.bean.UserDao" init-method="initDataMethod" destroy-method="destroyDataMethod"/>

    <bean id="userService" class="springframework.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="腾讯"/>
        <property name="location" value="深圳"/>
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>

本章节中并没有额外新增加配置信息,与上一章节内容相同。


3、单元测试

@Test
public void test_xml() {
    // 1.初始化 BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    applicationContext.registerShutdownHook();      

    // 2. 获取Bean对象调用方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);
    System.out.println("ApplicationContextAware:"+userService.getApplicationContext());
    System.out.println("BeanFactoryAware:"+userService.getBeanFactory());

测试方法中主要是添加了一写关于新增 Aware 实现的调用,其他不需要调用的也打印了相应的日志信息,可以在测试结果中看到。

UserDao xml的实现, 执行:init-method
ClassLoadersun.misc.Launcher$AppClassLoader@14dad5dc
Bean Name is:userService
测试结果:ljc, 公司:腾讯, 地点深圳
ApplicationContextAwarecn.ljc.springframework.context.support.ClassPathXmlApplicationContext@153f5a29
BeanFactoryAwarecn.ljc.springframework.beans.factory.support.DefaultListableBeanFactory@7f560810
UserDao xml的实现, 执行:destroy-method

Process finished with exit code 0

从测试结果可以看到,本章节新增加的感知接口对应的具体实现(BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware),已经可以如期输出结果了。


总结

目前关于 Spring 框架的实现中,某些功能点已经越来趋向于完整,尤其是 Bean 对象的生命周期,已经有了很多的体现,结构如下图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/100012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(附源码)springboo计算机专业大学生就业指南网 毕业设计 061355

计算机专业大学生就业指南网 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对学生就业管理等…

Premiere Pro 快捷键大全(2023版)

说明&#xff1a;为避免篇幅过大&#xff0c;本文快捷键是基于 Windows 系统 Pr 2023 版本的。Mac系统下的快捷键可按以下方式进行对应&#xff1a;Ctrl→Command&#xff0c;Alt→Option。有不能对应的&#xff0c;本文会给出说明。◆ ◆ ◆媒体相关新建序列&#xff1a;Ctrl …

创建Series()对象--pandas

1. 函数功能 产生带有标签(索引)的一维数组&#xff0c;Series对象中的数据可以是任意类型&#xff08;整型、字符串、浮点型、python对象等&#xff09; 2. 函数语法 pandas.Series(dataNone, indexNone, dtypeNone, nameNone, copyFalse, fastpathFalse)3. 函数参数与示例…

猿如意初体验!赞一个。

目录 功能一&#xff1a;chatGPT 功能二、对 “效率工具”的试体验&#xff01; 功能三&#xff1a;教程文档 最后总结 猿如意传送门猿如意下载地址&#xff1a;猿如意-程序员的如意兵器,工具代码,一搜就有 猿如意使用了几次了&#xff0c;今天来想分享一下我对于猿如意的…

工程复现 -- SiamMOT

工程复现 – SiamMOT 先赞后看&#xff0c;养成好习惯&#xff0c;感谢您的理解与支持&#xff01; 参考&#xff1a; 1. siam-mot源码地址 2. SiamMOT 论文地址 3. SiamMOT 论文解析 简单介绍 SiamMOT&#xff08;SiamMOT: Siamese Multi-Object Tracking&#xff09;是…

微服务框架 SpringCloud微服务架构 服务异步通讯 53 MQ 集群 53.5 仲裁队列【SpringAMQP】

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 服务异步通讯 文章目录微服务框架服务异步通讯53 MQ 集群53.5 仲裁队列【SpringAMQP】53.5.1 仲裁队列53 MQ 集群 53.5 仲裁队列【SpringAM…

Jvm学习笔记

&#x1f3b6; 文章简介&#xff1a;Jvm学习笔记 &#x1f4a1; 创作目的&#xff1a;Jvm学习笔记 ☀️ 今日天气&#xff1a;天气很好 &#x1f4dd; 每日一言&#xff1a;乾坤琉璃色&#xff0c;碧宇凝清光。 文章目录类文件结构详解一 概述二 Class 文件结构总结2.1 魔数&am…

工程机械部件的大尺寸空间精度检测仪器

工程机械作为工程建设中的重要工具&#xff0c;施工过程中长期承受各种外力&#xff0c;因此对各结构件质量要求很高&#xff0c;检测十分严格&#xff0c;通常要求的公差在0.1mm&#xff5e;0.2mm以内。工程机械部件外形尺寸及重量通常较大&#xff0c;常规仪器无法有效满足检…

【Linux权限管理】

The secret of getting ahead is getting started. 目录 1 Linux权限的概念 2 文件访问者的分类&#xff08;人&#xff09; 3 文件类型和访问权限&#xff08;事物属性&#xff09; chmod chown: chgrp: 4 文件掩码 umask 5 目录的权限 6 粘滞位 7 权限的总结 1 Linu…

【瑕疵检测】瓶盖瑕疵检测【含Matlab源码 730期】

⛄一、简介 文中将图像预处理与边缘检测相结合对瓶盖瑕疵进行检测, 先使用直方图规定化的方法对图像做出修正与增强, 再利用中值滤波的方法消除图像孤立的噪声点;Canny算子快速分辨出瓶盖瑕疵, 再利用采用Otsu阈值方法求取自适应阈值自动选择并提取瑕疵。 1 图像预处理 在特征…

Mac 使用 brew 安装 mysql

最近需要用到 MySQL 来开发项目&#xff0c;所以在 Mac 配置了下 MySQL 的环境。 1、使用 brew install mysql 安装 MySQL 安装完毕后会有以下提示信息&#xff0c;告诉我们初始安装好后 root 账户没有密码&#xff0c;只需要输入 mysql -uroot就能连接运行MySQL。 那咱们输入 …

模板引擎:ftl文件生成word

wshanshi&#xff1a;总结记录…便于回顾… 一、什么是FreeMarker FreeMarker 是一个用 Java 语言编写的模板引擎&#xff0c;基于模板来生成文本输出。 FreeMarker的原理&#xff1a;模板数据模型输出&#xff0c;模板只负责数据在页面中的表现&#xff0c;不涉及任何的逻辑代…

设计模式之组合模式

Composite design pattern 组合模式的概念、组合模式的结构、组合模式的优缺点、组合模式的使用场景、组合模式的实现示例、组合模式的源码分析 1、组合模式的概念 组合模式&#xff0c;即部分整体模式&#xff0c;是用于把一组相似的对象当做一个单一个的对象。组合模式依据树…

C#语言实例源码系列-身份证验证

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

【车载开发系列】UDS诊断---数据传输($0x36)

【车载开发系列】UDS诊断—数据传输&#xff08;$0x36&#xff09; UDS诊断---数据传输&#xff08;$0x36&#xff09;【车载开发系列】UDS诊断---数据传输&#xff08;$0x36&#xff09;一.概念定义二.报文格式1&#xff09;请求报文2&#xff09;肯定响应3&#xff09;否定响…

FL Studio21最新中文公测版下载及新功能介绍

FL Studio水果21现已推出&#xff0c;提供更快、更精确的音频编辑&#xff0c;coco玛奇朵升级后的DAW为用户提供了更多的内容发现和改进的界面。 Image-Line发布了FL Studio 21&#xff0c;称其可以实现更快、更精确的音频编辑&#xff0c;以及对整个DAW的更多控制。 期待已久…

【毕业设计_课程设计】基于SSM的实验室管理系统(源码+论文)

文章目录0 项目说明1 研究目的2 研究方法3 项目使用4 研究结论5 论文目录6 项目源码0 项目说明 基于SSM的实验室管理系统 提示&#xff1a;适合用于课程设计或毕业设计&#xff0c;工作量达标&#xff0c;源码开放 1 研究目的 基于B/S模式的实验室管理系统&#xff0c;它所覆…

Linux命令行笔记-00 综述

文章目录1 Linux命令行简介1.1 Linux命令行的分类1.1.1 根据系统中作用来分类1.1.2 根据对象来分类2 Linux命令行解释器2.1 命令行解释器shell2.1.1 核心程序2.1.2 公用程序shell2.1.3 用户的默认shell2.1.4 shell如何工作2.2 Shell发展历史2.3 shell版本的差异2.3.1 C shell2.…

云原生----什么是云原生

【原文链接】云原生----什么是云原生 文章目录1. 云原生的概念2. 云原生的四要素3. 云原生的关键目标什么是云原生&#xff1f;这里将从云原生的概念、云原生的四要素、云原生关键目标等方面介绍。1. 云原生的概念 云原生应用时面向云而设计的应用&#xff0c;在使用云原生技术…

Metasploit Framework和msf框架

Metasploit Framework ● MSF默认集成与kali linux之中 ● 使用postgresql数据库存储数据 ○ 早期版本需要先启动数据库再启动msf MSF架构 Rex ● 基本功能库&#xff0c;用于完成日常基本任务&#xff0c;无需人工手动编码实现 ● 处理socket连接访问、协议应答&#xff08…