spring 无法获取父类属性_spring中自动注入field的继承问题

博客探讨了在Spring框架中,父类字段是否能够通过@Autowired进行自动注入的问题。通过实例和源码分析,得出结论:即使父类未被添加到Spring容器中,其protected级别的字段也能被子类自动注入。建议在需要时将父类字段改为protected以确保子类能访问。同时,文章提醒读者复习private权限的含义,并分享了debug技巧。
摘要由CSDN通过智能技术生成

推荐阅读:

我是如何被算法虐到自闭的?字节跳动抖音Java一面二面面经分享

Java程序员4面字节最终斩获offer,只有努力复习,方能战胜寒冬

斩获阿里P7Offer,这一份Java面试文档,送给年后换工作的程序员


先说问题

隔离在家,闲的蛋疼,正好有空梳理一下项目里的代码,就是所谓的重构罗,但是呢,我很显然没有相关的经验和知识,就是想把一些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还有这么神奇的功效?

自己实践

我们写一个简单的例子好了,如图所示:

bbf0d54b7d0e73347d1148ff48abba07.png
 git: github.com/fw103699437…
@RestControllerpublic 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 elements = new ArrayList<>();Class> targetClass = clazz;do {final List currElements = new ArrayList<>();            //这里找到需要注入的fieldReflectionUtils.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

@Nullableprivate 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就够用。因此其实还是有那么一丝误导,没有说的太透彻。)

dc469632a016de8e60f407c8697dfff2.png


百度得到的结果,他们为什么都改成了protected?**其实只是为了严谨,protected是保证子类能获取到父类field的最小权限而已。**结果让我这种菜鸟花了眼,哈哈哈哈。 2:好好的复习了一下private:
当一个子类被实例化的时候,默认会先调用父类的构造方法对父类进行初始化,即在内存中创建一个父类对象,然后再父类对象的外部放上子类独有的属性,两者合起来成为一个子类的对象。 所以:子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访问到的。即只是拥有,但是无法使用。 3:IDEA debug的Drop Frame真香

一点副产物

5c964d63cbc11dd8493583fc9266d98d.png

其中MergedBeanDefinitionPostProcessor的接口postProcessMergedBeanDefinition的实现,负责找到需要注入的属性 InstantiationAwareBeanPostProcessor的接口postProcessProperties负责真正注入。

BeanPostProcessor真的是包罗万象


作者:Wayne_Kdl
链接:https://juejin.im/post/5e3adbe0e51d4526d43f229c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值