前言
本文是 如何实现一个简易版的 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,感兴趣的敌人能够查看残缺代码。