spring中自动注入field的继承问题

git: github.com/fw103699437…html

先说问题

隔离在家,闲的蛋疼,正好有空梳理一下项目里的代码,就是所谓的重构罗,可是呢,我很显然没有相关的经验和知识,就是想把一些java里的继承之类的、设计模式之类的给用上。
可是因为spring的存在,有些东西没法很方便的弄到spring中来,不少时候须要验证。
例如一个接口的多个实现,有一些重复代码,确定是要提出来,我将他提到一个抽象类里。
那么问题来了,抽象类没法实例化,那么也就没法放入到spring容器中,可是抽象类中又要注入一个Bean。
按照个人理解java

  • 放入Spring容器中的bean的属性(field)能够@Autowired,这个是没有争议的。
  • 没有放入Spring容器中的类,天然没法@Autowired,由于spring对这个类视而不见了。
  • 那么,没有被显式放入spring(即加上@Component等)的父类的field,能不能被@Autowired呢?

面向百度编程

百度获得结果以下:git

  • 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还有这么神奇的功效?github

本身实践

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

git: github.com/fw103699437…

@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寻找须要被注入的属性post

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)ui

而后根据findAutowiredAnnotation,找到了field上的注解@Autowired,肯定要注入父类中的Daothis

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


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

一点副产物

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

BeanPostProcessor真的是一应俱全

相关文章
相关标签/搜索