news 2026/4/16 15:22:51

手写简易Spring(八)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手写简易Spring(八)

参考小傅哥的教程:第09章:Aware感知容器对象 | 小傅哥 bugstack 虫洞栈

本期的目标是实现Aware功能,Aware是一个顶层接口,其拥有众多子类,如BeanClassLoaderAware、BeanNameAware、BeanFactoryAware、ApplicationContextAware接口,实现不同的Aware接口可以使Bean获得不一样的能力。

举个例子,假设我们的Bean实现了BeanFactoryAware接口,那意味着我们的Bean拥有获取Spring中的BeanFactory类对象的权力。

因此,Aware接口是赋予Bean感知容器中的相关对象,具体感知什么对象取决于实现了哪个Aware子类接口

实现

Aware

首先是顶层接口Aware,其只是一个标记性接口

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

接下来是各个子接口,这些子接口有一个共同特点,都提供了相应的set方法

BeanFactoryAware

/** * 实现此接口,即能感知到所属的 BeanFactory */ public interface BeanFactoryAware extends Aware { void setBeanFactory(BeanFactory beanFactory) throws BeansException; }

BeanClassLoaderAware

/** * 实现此接口,既能感知到所属的 ClassLoader */ public interface BeanClassLoaderAware extends Aware { void setBeanClassLoader(ClassLoader classLoader); }

BeanNameAware

/** * 实现此接口,即能感知到所属的 BeanName */ public interface BeanNameAware extends Aware { void setBeanName(String name); }

ApplicationContextAware

/** * 实现此接口,即能感知到所属的 ApplicationContext */ public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }

包装处理器

根据之前的学习,我们知道,管理Bean的流程是先通过XML获取对应的配置文件,然后在ApplicationContext中执行refresh操作,得到beanFactory,最后要回归到BeanFacotry中执行创建对象、依赖注入和各种前置、后置操作。

在BeanFacotry中,我们很容易能获取到相关的BeanName,BeanFactory,BeanClassLoader对象,但是无法获取到ApplicationContext对象,因为我们是先在ApplicationContext中执行了一些操作,再转到BeanFactory中执行剩余的操作。

为了解决在BeanFactory中无法获取ApplicationContext对象的问题,我们考虑在ApplicationContext中执行操作时,把ApplicationContext封装成一个BeanPostProcessor对象,直接把封装后的对象加入到BeanFactory中即可。

因此,就有了下面这个类

/** * 包装处理器 * 负责在 Bean 初始化之前,把 Spring 的 ApplicationContext 注入到实现了 * ApplicationContextAware 接口的 Bean 中,使这些 Bean 能够感知并访问容器本身。 */ public class ApplicationContextAwareProcessor implements BeanPostProcessor { // 要注入的 Spring 应用上下文 private final ApplicationContext applicationContext; public ApplicationContextAwareProcessor(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } /** * 在 Bean 初始化(init-method、@PostConstruct 等)之前调用。 * 如果当前 Bean 实现了 ApplicationContextAware 接口,就把容器注入进去。 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 如果bean 实现了 ApplicationContextAware 接口 if (bean instanceof ApplicationContextAware) { // 注入 ApplicationContext,让 Bean 可以访问容器资源 ((ApplicationContextAware) bean).setApplicationContext(applicationContext); } return bean; } /** * 在 Bean 初始化之后调用,这里不做任何处理,直接返回原 Bean。 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }

最后由AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization方法调用即可。

注册BeanPostProcessor

@Override public void refresh() throws BeansException { // 1. 子类负责创建/刷新 BeanFactory 并加载 BeanDefinition(模板方法) refreshBeanFactory(); // 2. 拿到已经完成定义的 BeanFactory(模板方法) ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext // 由于创建Bean的时候无法获取到对应的ApplicationContext, 因此这里先把ApplicationContext做一个包装, 直接在这里获取 // 其余的Aware接口, 包括BeanFactory BeanName 都可以在创建Bean那块获取 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 4. 先执行 BeanFactoryPostProcessor,允许动态修改/补充 BeanDefinition invokeBeanFactoryPostProcessors(beanFactory); // 5. 再把所有 BeanPostProcessor 提前实例化并注册到工厂,后续创建的 Bean 才能被拦截 registerBeanPostProcessors(beanFactory); // 6. 一次性提前实例化所有单例 Bean(lazy-init=false 的) beanFactory.preInstantiateSingletons(); }

注意,这一块就是前文提到的有一些操作先在ApplicationContext中执行,在这个环境下执行的时候,我直接把当前ApplicationContext对象封装为BeanPostProcessor,交给BeanFactory。

感知调用操作

private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) { // 先检查Aware接口 // 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(wrappedBean, beanName); return wrappedBean; }

这一块,在初始化阶段会检查当前Bean是否实现了相应的Aware接口,如果有,就把相关的对象给到Bean,这个在后面的例子中会更好理解。

而对于ApplicationContext对象,则会在执行BeanPostProcessor的前置操作时执行,因为ApplicationContext的封装对象重写了这个方法:

/** * 在 Bean 初始化(init-method、@PostConstruct 等)之前调用。 * 如果当前 Bean 实现了 ApplicationContextAware 接口,就把容器注入进去。 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 如果bean 实现了 ApplicationContextAware 接口 if (bean instanceof ApplicationContextAware) { // 注入 ApplicationContext,让 Bean 可以访问容器资源 ((ApplicationContextAware) bean).setApplicationContext(applicationContext); } return bean; }

测试

定义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); } public String queryUserInfo() { return userDao.queryUserName(uId) + "," + company + "," + location; } // 其余 get set 方法 }

这里的处理方式是,首先实现了各个Aware接口,同时在Bean中加入了相关字段,用来存储获取到的对象。之后,重写接口中的相关set方法,获取对象,将对象地址给到Bean中的相关字段。这也就赋予了Bean获取Spring中相关容器的能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 12:18:39

Kotaemon品牌定位陈述撰写:核心价值提炼

Kotaemon品牌定位陈述撰写:核心价值提炼 在企业智能化转型的浪潮中,智能客服、虚拟助手等AI对话系统已不再是锦上添花的“技术玩具”,而是支撑客户服务效率与用户体验的核心基础设施。然而,许多企业在落地AI应用时却发现&#xff…

作者头像 李华
网站建设 2026/4/16 12:23:55

如何利用Kotaemon进行知识库覆盖率分析?

如何利用Kotaemon进行知识库覆盖率分析? 在企业智能客服系统日益普及的今天,一个常见却棘手的问题浮出水面:为什么用户问“发票怎么开?”时,AI能对答如流,但换成“电子票据申请流程”就支支吾吾&#xff1f…

作者头像 李华
网站建设 2026/4/16 12:27:37

Kotaemon智能体框架在金融风控场景的应用探索

Kotaemon智能体框架在金融风控场景的应用探索 在今天的金融机构里,一个客户经理可能每天要面对上百个类似这样的问题:“我这笔转账会不会被风控拦截?”“最近逾期了几笔账单,还能申请贷款吗?”“和黑名单上的公司有过…

作者头像 李华
网站建设 2026/4/15 16:08:16

【完整源码+数据集+部署教程】肽质量指纹图谱提取区域检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]

一、背景意义 在生物医学研究和药物开发领域,肽作为生物活性分子的关键组成部分,受到越来越多的关注。肽质量指纹图谱的提取与分析,能够为新药的发现、疾病的诊断及生物标志物的筛选提供重要的信息。然而,传统的肽质量指纹图谱分析…

作者头像 李华
网站建设 2026/4/16 14:02:36

逼自己练完这 64 页!你的 AI Agent 开发水平直接起飞(建议收藏)

在AI浪潮席卷全球的今天,AI Agent(人工智能代理)已不再是科幻概念,而是成为构建下一代智能应用的核心。从自动化工作流到个性化虚拟助手,从复杂决策系统到创意生成工具,掌握AI Agent开发能力,无…

作者头像 李华
网站建设 2026/4/16 6:20:30

腾讯云RAG实践指南:从文档解析到多模态,大模型落地全链路拆解

RAG(检索增强生成)技术被视为解决大模型幻觉和知识局限的关键路径,但其落地实践在文档处理、检索和理解等环节面临诸多挑战。 腾讯云最新发布的《腾讯云工具指南第八期:RAG技术应用与实践》,系统性拆解了RAG应用中的四…

作者头像 李华