mini-spring|定义标记类型Aware接口,实现感知容器对象

**前言:**如果我们想获得 Spring 框架提供的 BeanFactory、ApplicationContext、BeanClassLoader等这些能力做一些扩展框架的使用时该怎么操作呢。所以我们本章节希望在 Spring 框架中提供一种能感知容器操作的接口,如果谁实现了这样的一个接口,就可以获取接口入参中的各类能力。’

框架设计

如果说我希望拿到 Spring 框架中一些提供的资源,那么首先需要考虑以一个什么方式去获取,之后你定义出来的获取方式,在 Spring 框架中该怎么去承接,实现了这两项内容,就可以扩展出你需要的一些属于 Spring 框架本身的能力了。
我们需要定义一个标记性的接口,这个接口不需要有方法,它只起到标记作用就可以,而具体的功能由继承此接口的其他功能性接口定义具体方法,最终这个接口就可以通过 instanceof 进行判断和调用了。
在这里插入图片描述
1.定义接口 Aware,在 Spring 框架中它是一种感知标记性接口,具体的子类定义和实现能感知容器中的相关对象。也就是通过这个桥梁,向具体的实现类中提供容器服务
2.继承 Aware 的接口包括BeanFactoryAware、BeanClassLoaderAware、BeanNameAware和ApplicationContextAware,当然在 Spring 源码中还有一些其他关于注解的,不过目前我们还是用不到。
3.在具体的接口实现过程中,一部分(BeanFactoryAware、BeanClassLoaderAware、BeanNameAware)在 factory 文件夹下,另外 ApplicationContextAware 是在 context 下,这是因为不同的内容获取需要在不同的包下提供。在 AbstractApplicationContext 的具体实现中会用到向 beanFactory 添加 BeanPostProcessor 内容的 ApplicationContextAwareProcessor 操作,最后由 AbstractAutowireCapableBeanFactory 创建 createBean 时处理相应的调用操作。

工程结构与类图

src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── beans
│ │ ├── factory
│ │ │ ├── factory
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java(实体类)
初始化和销毁
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java(接口) 定义了 destroySingletons 销毁方法
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java(抽象类)
主要作用:
继承关系:继承AbstractBeanFactory
实现AutowireCapableBeanFactory接口
主要方法:
CreateBean():创建Bean 调用registerDisposableBeanIfNecessary
initializeBean():初始化Bean,调用PostProcessor Before 处理,执行初始化方法invokeInitMethods,执行 BeanPostProcessor After 处理
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
实现destroySingletons 销毁方法( AbstractBeanFactory.java的父类)
│ │ │ │ ├── DisposableBeanAdapter.java
描述:销毁方法适配器
继承关系: 实现DisposableBean接口
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java(接口)
│ │ │ ├── BeanClassLoaderAware.java(实现Aware接口)
│ │ │ ├── BeanFactory.java
│ │ │ ├──BeanFactoryAware.java(实现Aware接口)
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java(接口) 定义初始化方法
│ │ │ └── ListableBeanFactory.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java(抽象类)
继承关系:实现 ConfigurableApplicationContext接口 继承DefaultResourceLoader类
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java(实现BeanPostProcessor接口)
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├──ApplicationContextAware.java
│ │ └── ConfigurableApplicationContext.java(接口)
主要描述:虚拟机关闭钩子注册调用销毁,定义刷新容器,关闭应用上下文
继承关系:继承ApplicationContext
主要方法:
refresh():
registerShutdownHook():注册虚拟机钩子的方法
close():手动执行关闭虚拟机钩子的方法
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java(实体类)
作用:资源处理器
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ └── utils
│ └── ClassUtils.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── UserDao.java
│ └── UserService.java
└── ApiTest.java
在这里插入图片描述
ware 有四个继承的接口,其他这些接口的继承都是为了继承一个标记,有了标记的存在更方便类的操作和具体判断实现。
另外由于 ApplicationContext 并不是在 AbstractAutowireCapableBeanFactory 中 createBean 方法下的内容,所以需要像容器中注册 addBeanPostProcessor ,再由 createBean 统一调用 applyBeanPostProcessorsBeforeInitialization 时进行操作。

定义标记接口

Aware

public interface Aware {
}

标记接口的存在就像是一种标签一样,可以方便统一摘取出属于此类接口的实现类,通常会有 instanceof 一起判断使用。

容器感知类

BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

实现此接口,可以感知到所属的 BeanFactory
BeanClassLoaderAware

public interface BeanClassLoaderAware extends Aware{

    void setBeanClassLoader(ClassLoader classLoader);

}

实现此接口,可以感知到所属的 ClassLoader
BeanNameAware

public interface BeanNameAware extends Aware {

    void setBeanName(String name);

}

ApplicationContextAware

public interface ApplicationContextAware extends Aware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

实现此接口可以感知到所属的 ApplicationContext

包装处理器

ApplicationContextAwareProcessor

/**
 * 使用包装的BeanPostProcessor获取ApplicationContext
 */
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 方法调用。
什么是refresh操作

注册 BeanPostProcessor

AbstractApplicationContext(重点)

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。

感知调用操作

AbstractAutowireCapableBeanFactory

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

}

首先在 initializeBean 中,通过判断 bean instanceof Aware,调用了三个接口方法,BeanFactoryAware.setBeanFactory(this)、BeanClassLoaderAware.setBeanClassLoader(getBeanClassLoader())、BeanNameAware.setBeanName(beanName)
我们还向 BeanPostProcessor 中添加了 ApplicationContextAwareProcessor,此时在这个方法中也会被调用到具体的类实现,得到一个 ApplicationContex 属性

测试

UserService

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
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧城市与数字孪生:共创未来城市新篇章

一、引言 随着科技的飞速发展,智慧城市与数字孪生已成为现代城市建设的核心议题。智慧城市注重利用先进的信息通信技术,提升城市治理水平,改善市民生活品质。而数字孪生则通过建立物理城市与数字模型之间的连接,为城市管理、规划…

docker容器技术(2)

docker容器数据卷 什么是数据卷? 在Docker中,数据卷(Data Volumes)是一种特殊的目录,可以在容器和主机之间共享数据。它允许容器内的文件持久存在,并且可以被多个容器共享和访问。 数据卷的主要作用如下&am…

jdk21本地执行flink出现不兼容问题

环境说明:换电脑尝尝鲜,jdk,flink都是最新的,千辛万苦把之前的项目编译通过,跑一下之前的flink项目发现启动失败,啥都不说了上异常 Exception in thread "main" java.lang.IllegalAccessError: …

贝叶斯核机器回归拓展R包:bkmrhat

1.摘要 bkmrhat包是用于扩展bkmr包的贝叶斯核机器回归(Bayesian Kernel Machine Regression, BKMR)分析工具,支持多链推断和诊断。该包利用future, rstan, 和coda包的功能,提供了在贝叶斯半参数广义线性模型下进行identity链接和 …

【kubernetes】二进制部署k8s集群之cni网络插件flannel和calico工作原理

k8s集群的三种接口 k8s集群有三大接口: CRI:容器进行时接口,连接容器引擎--docker、containerd、cri-o、podman CNI:容器网络接口,用于连接网络插件如:flannel、calico、cilium CSI:容器存储…

SPI技术实现对比Java SPI、Spring SPI、Dubbo SPI

概念 SPI机制,全称为Service Provider Interface,是一种服务提供发现机制。 SPI的核心思想是面向接口编程,它允许程序员定义接口,并由第三方实现这些接口。在运行时,SPI机制能够发现并加载所有可用的实现&#xff0c…

文献速递:深度学习--深度学习方法用于帕金森病的脑电图诊断

文献速递:深度学习–深度学习方法用于帕金森病的脑电图诊断 01 文献速递介绍 人类大脑在出生时含有最多的神经细胞,也称为神经元。这些神经细胞无法像我们身体的其他细胞那样自我修复。随着年龄的增长,神经元逐渐死亡,因此变得…

docker小知识:linux环境安装docker

安装必要软件包,执行如下命令 yum install -y yum-utils device-mapper-persistent-data lvm2目的是确保在安装 Docker 之前,系统已经安装了必要的软件包和服务,以支持 Docker 的正常运行。设置yum源,添加Docker官方的CentOS存储…

Open CASCADE学习|GC_MakeArcOfCircle构造圆弧

目录 1、通过圆及圆的两个参数创建圆弧,参数为弧度角 2、通过圆及圆上的一点、圆的1个参数创建圆弧,参数为弧度角,Sense决定方向 3、通过圆及圆上的两个点创建圆弧,Sense决定方向 4、通过三点创建圆弧,最后一点应安…

Mysql 常用数据类型

数值型(整数)的基本使用 如何定义一个无符号的整数 数值型(bit)的使用 数值型(小数)的基本使用 字符串的基本使用 字符串使用细节 日期类型的基本使用

用html编写的小广告板

用html编写的小广告板 相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</tit…

【练习——打印每一位数】

打印一个数的每一位 举个例子&#xff1a;我们现在要求打印出123的每一位数字。我们需要去想123%10等于3&#xff0c;就可以把3单独打印出来了&#xff0c;然后再将123/10可以得到12&#xff0c;将12%10就可以打印出2&#xff0c;而我们最后想打印出1&#xff0c;只需要1%10就…

国内大型语言模型(LLM)的研发及突破性应用

随着人工智能技术的迅猛发展&#xff0c;大型语言模型&#xff08;LLM&#xff09;在国内外科技领域成为了热点话题。这些模型因其在文本生成、理解和处理方面的卓越能力&#xff0c;被广泛应用于各种行业和场景中。 在中国&#xff0c;一批人工智能公司在LLM的研发与应用方面…

科技云报道:黑马Groq单挑英伟达,AI芯片要变天?

科技云报道原创。 近一周来&#xff0c;大模型领域重磅产品接连推出&#xff1a;OpenAI发布“文字生视频”大模型Sora&#xff1b;Meta发布视频预测大模型 V-JEPA&#xff1b;谷歌发布大模型 Gemini 1.5 Pro&#xff0c;更毫无预兆地发布了开源模型Gemma… 难怪网友们感叹&am…

数据结构之栈的链表实现

数据结构之栈的链表实现 代码&#xff1a; #include<stdio.h> #include<stdbool.h> #include<stdlib.h> //链表节点定义 typedef struct node {int value;struct node* next; }Node; //入栈操作 bool push(Node** head, int val) {if (*head NULL){*head …

如何学习Arduino单片机

&#xff08;本文为简单介绍&#xff0c;内容源于网络&#xff09; 学习Arduino相关的网址和开源社区&#xff1a; Arduino官方文档: Arduino - HomeArduino Forum: Arduino ForumArduino Playground: Arduino Playground - HomePageGitHub: GitHub: Let’s build from here …

第十三天-mysql交互

目录 1.安装MySQL connector 方式1&#xff1a;直接安装 方式2&#xff1a;下载 2.创建链接 3.游标Cursor 4.事务控制 5. 数据库连接池 1. 使用 6.循环执行SQL语句 不了解mysql的可以先了解mysql基础 1.安装MySQL connector 1. MySQL connector 是MySQL官方驱动模块…

接口测试 —— Jmeter读取数据库数据作测试参数

1、添加Jdbc Request 2、添加ForEach控制器(右键线程组->逻辑控制器->ForEach控制器) ①输入变量的前缀&#xff1a;mobilephone&#xff1b; 从jdbc request设置的变量得知&#xff0c;我们要取的值为mobilephone_1、mobilephone_2、mobilephone_3......所以这里输入m…

备战蓝桥杯---DFS基础刷题

话不多说&#xff0c;直接看题&#xff1a; 1.注意搜索顺序枚举方式 首先&#xff0c;看到数据范围&#xff0c;我们就不可以直接每一轮3次的暴力。 我们可以发现a^2的大部分情况>2a以及a1,并且&#xff0c;我们发现其实1的操作是没有必要的&#xff08;因为2a以经包括了&…

Spring-Cloud-Gateway集成Sentinel限流

1&#xff09;gateway添加sentinel相关依赖 <spring-cloud.version>2021.0.1</spring-cloud.version> <spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version><dependencies><!--gateway--><dependency><gro…