欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

Spring中的ingoreDependencyInterface方法简析

时间:2023-08-01

本文仅代表个人理解,如有不正确的欢迎交流。

方法的作用

首先,从方法名理解,就是忽略指定接口的依赖。

其次,根据阅读源码的理解是:当需要忽略指定接口类型的属性的依赖注入时,可调用该接口,然后自定义该属性的依赖注入逻辑,不想使用Spring提供的依赖注入方式。

举个例子:

首先,有一个MyInterface接口,其中有一个set方法:

public interface MyInterface{ // setter方法必须存在 void setMyBean(MyBean myBean);}

然后有一个类MyBean02实现了该接口,并有一个MyBean类型的属性:

public class MyBean02 implement MyInterface{ private MyBean myBean; @Override public void setMyBean(MyBean myBean){ this.myBean = myBean; }}

当在构建MyBean02的Bean实例时,不希望使用Spring容器的自动依赖注入(如:by-name、by-type),而想自定义实现对其MyBean属性的注入,此时为了让自己的注入方式生效,而不让Spring提供的方式生效,就可以调用 ignoreDependencyInterface(MyInterface.class) 方法,这样当Spring对MyBean02进行初始化时,就会忽略MyBean类型的属性的注入。

源码简析

首先,查看方法定义,该方法由 ConfigurableListableBeanFactory 接口提供,该接口继承自ConfigurableBeanFactory,可用来配置BeanFactory,指的就是配置Spring容器,让其启动过程中初始化Bean时忽略指定接口类型的Bean的自动注入,方法定义具体如下:

void ignoreDependencyInterface(Class<?> ifc);

注释的含义大概是这样的:忽略指定接口的自动依赖注入,典型的使用场景是:在spring 容器实现依赖注入时,不想用容器提供的自动注入方式,而希望通过其他方式注入,如:通过BeanFactoryAware注入BeanFactory,通过ApplicationContextAware注入ApplicationContext。默认情况下,只有BeanFactoryAware接口被忽略。如果希望更多接口类型被忽略,那就针对每种接口类型调用该方法。

       有了接口定义,还不是很清除其价值,那就看看在Spring内部是如何使用的,这里主要列举Spring自身使用了该接口的地方(这里使用的Spring版本是5.2.12):

其实,观察一下使用方式,就会发现,使用该方法的地方传入的Class均是 Aware 接口的子接口,Aware接口的作用,简单来说,就是一个用于提示的标记,当Spring容器初始化时,会判断Bean的类型是否为xxxAware类型,进而执行一些操作,有兴趣的可以自己研究一下。

        再观察调用,Spring自身主要的调用就在 AbstractApplicationContext 类内部的 prepareBeanFactory(Xxx) 方法:

// Configure the bean factory with context callbacks.beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);

这里忽略了几个xxxAware类型的依赖,这里手动忽略了,那就有地方执行依赖的注入,而依赖的注入就在:

// Configure the bean factory with context callbacks.beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

这段代码里,也就是上面的代码的前两行,这里是为spring容器增加了一个BeanPostProcessor,BeanPostProcessor提供了扩展功能,可以在Spring容器初始化Bean的阶段,在初始化前、后分别执行其扩展,便于开发人员自定义扩展。

       这里插入一个小知识:Bean的构建分为两个阶段,Bean的实例化、Bean的初始化,实例化就是创建出Bean的引用,初始化就是为对象设置其属性值,比如:

// 实例化 Person person = new Person(); // 初始化 person.setName("xxx"); person.setAge(12);

 构建出person对象就是实例化,后续设置属性就是初始化。

        在 ApplicationContextAwareProcessor 中的 postProcessBeforeInitialization 方法内有一个private方法 invokeAwareInterfaces, 该方法内部在初始化对应xxxAware的属性,如:

private void invokeAwareInterfaces(Object bean) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}

可知,在Spring容器初始化过程中,针对初始化的Bean会判断其是否是xxxAware类型,然后决定是否进行属性的设置,当是xxxAware类型时,回调当前bean对应的setXxx方法,这也是xxxAware接口都对应一个setXxx方法的原因。如:

public interface EnvironmentAware extends Aware {void setEnvironment(Environment environment);}

目前在Spring内部的使用中,针对ignoreDependencyInterface方法的使用,基本都是针对xxxAware接口的。

       知道了该方法主要的调用方式,及针对的对象,那再看一下在真实调用时,底层执行的逻辑:我这里阅读的是 AbstractAutowireCapableBeanFactory  中的实现逻辑,其实现是这样的:

private final Set> ignoredDependencyInterfaces = new HashSet<>(); public void ignoreDependencyInterface(Class<?> ifc) {this.ignoredDependencyInterfaces.add(ifc);}

这里调用 ignoreDependencyInterface 方法,就是尝试向Set集合中增加元素。一个属性只有在使用的时候才有价值,所以查看该集合在哪里调用了: 

在直接使用该属性的地方,有一处更值得研究,就是上面图片红框中的第三处(另外两处属于添加操作), AbstractAutowireCapableBeanFactory 类的 isExcludedFromDependencyCheck 方法内部使用的:

protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||this.ignoredDependencyTypes.contains(pd.getPropertyType()) ||AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));}

首先:该方法的作用是什么?                                                                                                       

        该方法的作用是判断当前属性是否要从依赖检查中排除,若排除了,后续在Bean的初始化过程中就不会去操作该属性,不会对其进行依赖注入等操作,若不排除,则需要对该属性进行依赖注入。这里看一下AutowireUtils 中定义的方法 isSetterDefinedInterface 方法: 。

public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set> interfaces) {Method setter = pd.getWriteMethod();if (setter != null) {Class<?> targetClass = setter.getDeclaringClass();for (Class<?> ifc : interfaces) {if (ifc.isAssignableFrom(targetClass) && ClassUtils.hasMethod(ifc, setter)) {return true;}}}return false;}

该方法针对bean属性的setter方法是否定义在要忽略的接口中,且setter方法所在的类是否是忽略接口类型(要忽略的接口集合中的任意一个接口满足该条件即可)。这其实要求:定义的Bean要实现忽略的接口,同时还要有对应的setter方法,该忽略作用才会生效。若定义的忽略接口中没有setter方法是不能生效的。

其次,该方法是在哪里调用的?

调用的话,我这里是两处(因为我这里只引入了基础的依赖,如果有引入其他项目依赖,还可能有其他调用)。

第一处:AbstractAutowireCapableBeanFactory 中的 unsatisfiedNonSimpleProperties方法:

protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {Set result = new TreeSet<>();PropertyValues pvs = mbd.getPropertyValues();PropertyDescriptor[] pds = bw.getPropertyDescriptors();for (PropertyDescriptor pd : pds) {if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&!BeanUtils.isSimpleProperty(pd.getPropertyType())) {result.add(pd.getName());}}return StringUtils.toStringArray(result);}

 该方法的作用是:去除无需后续依赖注入的Bean的属性,把要进行后续Spring执行依赖注入的属性保留下来,就是一步过滤的作用,筛选一下属性。该方法的调用是在:autowireByName、autowireByType 两个方法内部调用,也就是属性的依赖注入过程(Bean的初始化过程),在初始化过程中,如:

protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);for (String propertyName : propertyNames) {if (containsBean(propertyName)) {// 省略具体逻辑}}}

会发现,针对哪些属性要进行依赖注入,操作的就是 unsatisfiedNonSimpleProperties 方法返回的结果,也就是经过筛选后的属性才会进行属性依赖注入。

第二处:AbstractAutowireCapableBeanFactory的 filterPropertyDescriptorsForDependencyCheck 方法内:

protected PropertyDescriptor[] filterPropertyDescriptorsForDependencyCheck(BeanWrapper bw, boolean cache) {PropertyDescriptor[] filtered = this.filteredPropertyDescriptorsCache.get(bw.getWrappedClass());if (filtered == null) {filtered = filterPropertyDescriptorsForDependencyCheck(bw);if (cache) {PropertyDescriptor[] existing =this.filteredPropertyDescriptorsCache.putIfAbsent(bw.getWrappedClass(), filtered);if (existing != null) {filtered = existing;}}}return filtered;}

该方法的作用是过滤出需要进行依赖检查的PropertyDescriptor集合,用于:1、若存在InstantiationAwareBeanPostProcessor类型的后置处理器,针对哪些属性调用postProcessPropertyValues 方法,这些属性是由上述的过滤方法过滤得到;2、后续检查需要设置值的属性是否已经初始化;作用也是用于筛选需要处理的属性,个人感觉这部分使用是扩展和辅助检查,更重要的是“第一处”中的使用。

注意:

(1)在忽略属性的判断中,同时满足两个条件才会忽略:有setter方法、且当前初始化的Bean是忽略的接口类型;

(2)针对忽略属性的Spring依赖注入方式,是在by-name或者by-type时才会生效的。

总结:

        综上,ignoreDependencyInterface方法的作用是忽略指定接口类型的属性的自动注入,而通过其他方式实现注入(如:Spring中的xxxAware接口,通过判断Bean的类型回调setXxx方法实现属性的注入)。个人感觉:一方面提供了一种针对依赖属性注入的扩展机制,一方面像xxxAware这种方式,明确实现的接口,代码的可读性很好,很清晰,便于管理。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。