spring中自动注入field的继承问题
先说问题
隔离在家,闲的蛋疼,正好有空梳理一下项目里的代码,就是所谓的重构罗,但是呢,我很显然没有相关的经验和知识,就是想把一些java里的继承之类的、设计模式之类的给用上。 但是由于spring的存在,有些东西无法很方便的弄到spring中来,很多时候需要验证。 例如一个接口的多个实现,有一些重复代码,肯定是要提出来,我将他提到一个抽象类里。 那么问题来了,抽象类无法实例化,那么也就无法放入到spring容器中,但是抽象类中又要注入一个Bean。 按照我的理解
- 放入Spring容器中的bean的属性(field)可以@Autowired,这个是没有争议的。
- 没有放入Spring容器中的类,自然无法@Autowired,因为spring对这个类视而不见了。
- 那么,没有被显式放入spring(即加上@Component等)的父类的field,能不能被@Autowired呢?
面向百度编程
百度得到结果如下:
- www.cnblogs.com/zhjh256/p/9… 文章结论:只要父类要注入的属性是protected保护级别即可 (文中父类属性原先为default)
- www.cnblogs.com/walson/p/38… 文章结论:super.set方法 / 父类属性由private为protected,利用子类的自动注入方法(加上自动注入注解)来设置子类中的属性
- www.iteye.com/blog/arthur… 文章结论:在抽象类中把属性声明为protected并使用注解方式注入属性
说实话,有点get不到点,protected还有这么神奇的功效?
自己实践
我们写一个简单的例子好了,如图所示:

@RestController
public class Controller {
@Qualifier("impl1")
// @Qualifier("impl2")
@Autowired
Service service;
public void save(){
service.save();
}
}
经过实验可以发现,用@Qualifier指定不同实现时,子类的bean中会注入Dao,不会有问题。
断点进源码
getBean(beanName=controller) 然后注入属性 populateBean(beanName=controller) 为Controller注入beanName=imp1的bean
getBean(beanName=imp1) 利用AutowiredAnnotationBeanPostProcessor为imp1寻找需要被注入的属性
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
//这里找到需要注入的field
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
//注意这一句
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
其中while (targetClass != null && targetClass != Object.class)这一句,先是在 ServiceImp1中需要要注入的field,没有找到,然后在父类AbstractService中寻找,利用getDeclaredFields(clazz)找到了field(也就是dao)
然后根据findAutowiredAnnotation,找到了field上的注解@Autowired,确定要注入父类中的Dao
@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
具体注入过程就不分析了。
结论
子类交给spring容器,父类不交给spring容器,父类中的field也可以完成自动注入
总结
1:百度的结果中所说的protected,其实是由private(或者default)改过来的,目的只是让子类能够访问到而已。(第一篇文章中从default改为protected,其实子类也在同一个包下时default就够用。因此其实还是有那么一丝误导,没有说的太透彻。)

一点副产物

其中MergedBeanDefinitionPostProcessor的接口postProcessMergedBeanDefinition的实现,负责找到需要注入的属性 InstantiationAwareBeanPostProcessor的接口postProcessProperties负责真正注入。
BeanPostProcessor真的是包罗万象
转载自:https://juejin.cn/post/6844904057463635976