如何实现一个简易版的 Spring - 如何实现 @Autowired 注解
前言
本文是 如何实现一个简易版的 Spring
系列第四篇,在 上篇 介绍了 @Component
注解的实现,这篇再来看看在使用 Spring
框架开发中常用的 @Autowired
注入要如何实现,大家用过 Spring
都知道,该注解可以用在 字段
、 构造函数
以及 setter 方法
上,限于篇幅原因我们主要讨论用在字段的方式实现,其它的使用方式大体思路是相同的,不同的只是解析和注入方式有所区别,话不多说,下面进入我们今天的正题— 如何实现一个简易版的 Spring - 如何实现 @Autowired 注解
。
实现步骤拆分
实现步骤总的来说分为三大步:
Spring
细心的朋友可以发现,其实前面几篇文章的实现也是套路,其中最为关键也是比较困难的点就是如何抽象出数据结构。这里我们要做的是当某个 Bean
上的字段有 @Autowired
注解时,从容器中获取该类型的 Bean
然后调用该字段对应的 setter
方法设置到对象的属性中。下面就跟着这个思路去实现 @Autowired
注解。
数据结构抽象
要想根据字段的类型注入在容器中对应的实例,首先需要提供这个从一个类型获取对应 Bean
实例的能力,这需要 BeanFactory
接口提供一个这样的能力,等等,像这样容器内部使用的接口直接定义在 BeanFactory
好吗?像这种内部的操作应该尽量做到对使用者透明,所以这里新加一个接口 AutowireCapableBeanFactory
继承自 BeanFactory
,这样在内部就可以直接使用新接口接口。需要注意的是新接口的方法参数并不能直接使用 Class
类型去容器中查找对应的 Bean
,为了后期的灵活扩展(比如:是否必须依赖等),需要使用一个类来描述这种依赖,命名为 DependencyDescriptor
,其部分源码如下所示:
/** * @author mghio * @since 2021-03-07 */ public class DependencyDescriptor { private Field field; private boolean required; public DependencyDescriptor(Field field, boolean required) { Assert.notNull(field, "Field must not be null"); this.field = field; this.required = required; } public Class<?> getDependencyType() { if (this.field != null) { return field.getType(); } throw new RuntimeException("only support field dependency"); } public boolean isRequired() { return this.required; } }
接口 AutowireCapableBeanFactory
声明如下:
/** * @author mghio * @since 2021-03-07 */ public interface AutowireCapableBeanFactory extends BeanFactory { Object resolveDependency(DependencyDescriptor descriptor); }
查找解析依赖的功能我们抽象完成了,下面来看看核心步骤如何抽象封装注入的过程,抽象总结后不难发现,注入可以分为两大部分: 注入的目标对象
和 需要被注入的元素列表
,这些对于注入来说是一些 元数据
,命名为 InjectionMetadata
,其包含两个字段,一个是注入的目标对象,另一个是被注入的元素列表,还有一个重要的方法将元素列表注入到方法参数传入的目标对象中去。
每个注入元素都要提供一个注入到指定目标对象的能力,所以抽取出公共抽象父类 InjectionElement
,使用上文的 AutowireCapableBeanFactory
接口解析出当前字段类型对应 Bean
,然后注入到指定的目标对象中。抽象父类 InjectinElement
的主要代码如下:
/** * @author mghio * @since 2021-03-07 */ public abstract class InjectionElement { protected Member member; protected AutowireCapableBeanFactory factory; public InjectionElement(Member member, AutowireCapableBeanFactory factory) { this.member = member; this.factory = factory; } abstract void inject(Object target); }
注入元数据类 InjectionMetadata
的主要代码如下:
/** * @author mghio * @since 2021-03-07 */ public class InjectionMetadata { private final Class<?> targetClass; private List<InjectionElement> injectionElements; public InjectionMetadata(Class<?> targetClass, List<InjectedElement> injectionElements) { this.targetClass = targetClass; this.injectionElements = injectionElements; } public void inject(Object target) { if (injectionElements == null || injectionElements.isEmpty()) { return; } for (InjectionElement element : injectionElements) { element.inject(target); } } ... }
把一个 Class
转换为 InjectionMetadata
的部分实现我们留到下文实现部分介绍,抽象后总的流程就是把一个 Class
转换为 InjectionMedata
,然后调用 InjectionMedata
提供的 inject(Object)
方法来完成注入(依赖 AutowireCapableBeanFactory
接口提供的 resolveDependency(DependencyDescriptor)
能力),下面是抽象后的字段注入部分的相关类图关系如下:
解析构造出定义的数据结构
在上文我们还没实现将一个类转换为 InjectionMetadata
的操作,也就是需要实现这样的一个方法 InjectionMetadata buildAutowiringMetadata(Class<?> clz)
,实现过程也比较简单,扫描类中声明的属性找到有 @Autowried
注解解析构造出 InjectinMetadata
实例,核心实现代码如下:
/** * @author mghio * @since 2021-03-07 */ public class AutowiredAnnotationProcessor { private final String requiredParameterName = "required"; private boolean requiredParameterValue = true; private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(); public AutowiredAnnotationProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); } public InjectionMetadata buildAutowiringMetadata(Class<?> clz) { LinkedList<InjectionElement> elements = new LinkedList<>(); Class<?> targetClass = clz; do { LinkedList<InjectionElement> currElements = new LinkedList<>(); for (Field field : targetClass.getDeclaredFields()) { Annotation ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { continue; } boolean required = determineRequiredStatus(ann); elements.add(new AutowiredFieldElement(field, required, beanFactory)); } } elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clz, elements); } protected boolean determineRequiredStatus(Annotation ann) { try { Method method = ReflectionUtils.findMethod(ann.annotationType(), this.requiredParameterName); if (method == null) { return true; } return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, ann)); } catch (Exception e) { return true; } } private Annotation findAutowiredAnnotation(AccessibleObject ao) { for (Class<? extends Annotation> annotationType : this.autowiredAnnotationTypes) { Annotation ann = AnnotationUtils.getAnnotation(ao, annotationType); if (ann != null) { return ann; } } return null; } ... }
上面在做数据结构抽象时定义好了注入元素的抽象父类 InjectionElement
,这里需要定义一个子类表示字段注入类型,命名为 AutowiredFieldElement
,依赖 AutowireCapableBeanFactory
接口的能力解析出字段所属类型的 Bean
,然后调用属性的 setter
方法完成注入,在基于我们上面定义好的数据结构后实现比较简单,主要代码如下:
/** * @author mghio * @since 2021-03-07 */ public class AutowiredFieldElement extends InjectionElement { private final boolean required; public AutowiredFieldElement(Field field, boolean required, AutowireCapableBeanFactory factory) { super(field, factory); this.required = required; } public Field getField() { return (Field) this.member; } @Override void inject(Object target) { Field field = this.getField(); try { DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required); Object value = factory.resolveDependency(descriptor); if (value != null) { ReflectionUtils.makeAccessible(field); field.set(target, value); } } catch (Throwable e) { throw new BeanCreationException("Could not autowire field:" + field, e); } } }
注入到 Spring 中
接下来面临的问题是:要在什么时候调用上面这些类和方法呢?在这里我们回顾一下 Spring
中 Bean
的生命周期,其中几个钩子入口如下图所示:
通过生命周期开放的钩子方法可以看出我们需要在 InstantiationAwareBeanPostProcessor
接口的 postProcessPropertyValues
方法中实现 Autowired
注入,将前面的 AutowiredAnnotationProcessor
类实现该接口然后在 postProcessPropertyValues
方法处理注入即可。这部分的整体类图如下所示:
AutowiredAnnotationProcessor
处理器实现的 postProcessPropertyValues()
方法如下:
/** * @author mghio * @since 2021-03-07 */ public class AutowiredAnnotationProcessor implements InstantiationAwareBeanProcessor { ... @Override public void postProcessPropertyValues(Object bean, String beanName) throws BeansException { InjectionMetadata metadata = this.buildAutowiringMetadata(bean.getClass()); try { metadata.inject(bean); } catch (Throwable e) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed"); } } }
然后只需要在抽象父类 AbstractApplicationContext
构造函数注册那些我们定义的 processor
,然后在 Bean
注入的时候( DefaultBeanFactory.populateBean()
)调用 processor
的 postProcessPropertyValues
方法完成属性注入,抽象类 AbstractApplicationContext
改动部分的代码如下:
/** * @author mghio * @since 2021-03-07 */ public abstract class AbstractApplicationContext implements ApplicationContext { ... public AbstractApplicationContext(String configFilePath) { ... registerBeanPostProcessor(beanFactory); } protected void registerBeanPostProcessor(ConfigurableBeanFactory beanFactory) { AutowiredAnnotationProcessor postProcessor = new AutowiredAnnotationProcessor(); postProcessor.setBeanFactory(beanFactory); beanFactory.addBeanPostProcessor(postProcessor); } ... }
BeanFactory
接口的默认实现类 DefaultBeanFactory
注入 Bean
属性的方法 populateBean(BeanDefinition, Object)
改动如下:
/** * @author mghio * @since 2021-03-07 */ public class DefaultBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory, BeanDefinitionRegistry { ... private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>(); private void populateBean(BeanDefinition bd, Object bean) { for (BeanPostProcessor postProcessor : this.getBeanPostProcessors()) { if (postProcessor instanceof InstantiationAwareBeanProcessor) { ((InstantiationAwareBeanProcessor) postProcessor).postProcessPropertyValues(bean, bd.getId()); } } ... } ... }
总的来说整个使用 processor
的过程分为两步,首先在 AbstractApplicationContext
构造方法中注册我们自定义的 processor
,然后再 DefaultBeanFactory
中调用其 postProcessPropertyValues
方法进行注入,至此使用在类字段上的 @Autowired
注解实现完成。
总结
本文简要介绍了实现 Spring
的 @Autowired
注解(使用在类字段上的方式),其中比较麻烦的步骤是数据结构抽象部分,需要考虑到后期的扩展性和内部操作对使用者尽量透明,限于篇幅,只列出了部分核心实现代码,完整代码已上传至 GitHub ,感兴趣的朋友可以查看完整代码。
- API 文档简洁之美,只需三步开启
- Kotlin 和 Jetpack 视频合集 | MAD Skills
- 实战 Java 16 值类型 Record - 1. Record 的默认方法使用以及基于预编译生成相关字节码的底层实现
- 优先使用 KTX 库 | MAD Skills
- 实操搭建企业级Harbor v1.10.6
- 推荐一个开源文件管理系统,简单好用
- 一文搞懂MySQL体系架构!!
- 使用Documentfragment优化DOM操作
- 深入理解浏览器缓存机制
- 百度搜索与推荐引擎的云原生改造 | Geek大咖说第一期
- 聊聊LiteOS事件模块的结构体、初始化及常用操作
- 从零搭建自己的社区系统,这个开源项目值得拥有
- 茫茫内存,我该如何用 windbg 找到你 ?
- Java中对象的生与灭- 核心篇
- 使用JavaScript学习设计模式
- 一文带你剖析LiteOS互斥锁Mutex源代码
- 小胖问我:MySQL 事务与 MVCC 原理?
- 免费正版 IntelliJ IDEA license 详细指南
- 纯函数是什么?怎么合理运用纯函数?
- 聊聊dddsample-core的Specification