在阅读Spring容器扩展部分源码的过程中,我了解到BeanFactory接口中有个方法叫ignoreDependencyInterface。从官方文档的“字面”来看,其作用指定自动装配(autowiring)的时候忽略的接口。还有一个很相似的方法叫ignoreDependencyType,同样其官方字面意思是指自动装配(autowiring)的时候忽略的类。
究竟这两个方法是不是我们的理解相同呢?真的可以让指定的接口和类在自动装配的时候被忽略?有没有注意不到的坑?

  1. /**
  2. * Ignore the given dependency interface for autowiring.
  3. * <p>This will typically be used by application contexts to register
  4. * dependencies that are resolved in other ways, like BeanFactory through
  5. * BeanFactoryAware or ApplicationContext through ApplicationContextAware.
  6. * <p>By default, only the BeanFactoryAware interface is ignored.
  7. * For further types to ignore, invoke this method for each type.
  8. * @param ifc the dependency interface to ignore
  9. * @see org.springframework.beans.factory.BeanFactoryAware
  10. * @see org.springframework.context.ApplicationContextAware
  11. */
  12. void ignoreDependencyInterface(Class<?> ifc);
  13. /**
  14. * Ignore the given dependency type for autowiring:
  15. * for example, String. Default is none.
  16. * @param type the dependency type to ignore
  17. */
  18. void ignoreDependencyType(Class<?> type);

先上结论:

  1. 自动装配时忽略指定接口或类的依赖注入,使用ignoreDependencyType已经足够
  2. ignoreDependencyInterface的真正意思是在自动装配时忽略指定接口的实现类中,对外的依赖。

具体详述见下文的逐步踩坑。

“自动装配(autowiring)”的坑

首先我们来试试水。我们试试能不能忽略一些我们常用的类。
假设有一个类,叫ListHolder,就只有一个用@Autowired注解的ArrayList对象。

  1. public class ListHolder {
  2. @Autowired
  3. private ArrayList<String> list;
  4. public ArrayList<String> getList() {
  5. return list;
  6. }
  7. public void setList(ArrayList<String> list) {
  8. this.list = list;
  9. }
  10. }

xml配置文件配置了ArrayList bean和ListHolder bean:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <bean id="list" class="java.util.ArrayList">
  10. <constructor-arg>
  11. <list>
  12. <value>foo</value>
  13. <value>bar</value>
  14. </list>
  15. </constructor-arg>
  16. </bean>
  17. <bean id="listHolder" class="com.huxuecong.ignoreDependency.ListHolder"/>
  18. <bean class="com.huxuecong.autowire.IgnoreAutowiringProcessor"/>
  19. <context:annotation-config></context:annotation-config>
  20. </beans>

我们定义一个BeanFactoryPostProcessor接口实现类,在接口实现类中调用ignoreDependencyType方法:

  1. public class IgnoreAutowiringProcessor implements BeanFactoryPostProcessor {
  2. @Override
  3. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  4. beanFactory.ignoreDependencyType(ArrayList.class);
  5. }
  6. }

运行结果:
[foo, bar]

没有起作用。即使调用了ignoreDependencyType方法,ArrayList还是被注入了。说明ignoreDependencyType方法完全不起到该有的作用。
由于我对Spring的了解尚浅,我很怀疑自动装配是不是真的和我了解的相同,使用@Autowired注解就可以了呢?

经过一番中英文搜索之后,我终于发现有博客专门讲ignoreDependencyType和ignoreDependencyInterface方法。
在博客中提到了我之前没用过的自动装配方式:在beans标签中使用default-autowire属性来注入依赖。
于是我这次按照网友的说法,对我的例子进行改造:
(1)ListHolder中的list对象不使用@Autowired注解

  1. public class ListHolder {
  2. private ArrayList<String> list;
  3. public ArrayList<String> getList() {
  4. return list;
  5. }
  6. public void setList(ArrayList<String> list) {
  7. this.list = list;
  8. }
  9. }

(2)xml配置文件中去掉context:annotation-config/标签,并且在beans标签添加default-autowire的属性,其值为“byType”,意思是按照对象的类型进行装配。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd"
  9. default-autowire="byType">
  10. <bean id="list" class="java.util.ArrayList">
  11. <constructor-arg>
  12. <list>
  13. <value>foo</value>
  14. <value>bar</value>
  15. </list>
  16. </constructor-arg>
  17. </bean>
  18. <bean id="listHolder" class="com.huxuecong.ignoreDependency.ListHolder"/>
  19. <bean class="com.huxuecong.autowire.IgnoreAutowiringProcessor"/>
  20. </beans>

这次运行之后,结果就符合期待了:
null

经过这次踩坑,发现英语中的autowiring特定指的是通过beans标签default-autowire属性来依赖注入的方式,而不是指使用@Autowired注解进行的依赖注入。区别在于,使用default-autowire会自动给所有的类都会从容器中查找匹配的依赖并注入,而使用@Autowired注解只会给这些注解的对象从容器查找依赖并注入。

自动装配和@Autowired注解的装配不是同一回事。

但从这次例子来看,ignoreDependencyType方法和我们期待的完全一致,可以在自动装配的时候忽略ArrayList类的对象。

ignoreDependencyInterface的坑

在愉快地使用ignoreDependencyType方法后,立即如法炮制ignoreDependencyInterface方法,如无意外,应该效果一样,使得某个接口的实现类被忽略。
于是在上面的例子上进行改造:
(1)ListHolder类中的ArrayList类型改为List类型:

  1. public class ListHolder {
  2. private List<String> list;
  3. public List<String> getList() {
  4. return list;
  5. }
  6. public void setList(List<String> list) {
  7. this.list = list;
  8. }
  9. }

(2)ignoreDependencyType改为使用ignoreDependencyInterface方法:

  1. public class IgnoreAutowiringProcessor implements BeanFactoryPostProcessor {
  2. @Override
  3. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  4. beanFactory.ignoreDependencyInterface(List.class);
  5. }
  6. }

运行结果:
[foo, bar]

完全不起作用,ignoreDependencyInterface不按照我们的想法工作。曾经一度怀疑Spring是否有bug,但是从Spring的流行性和网上搜索结果来看,这种可能微乎其微,更多是对该方法的理解不对。
我突发奇想,能不能也把接口List.class传给ignoreDependencyType,而不是使用ArrayList.class呢?
于是又在上面的代码基础上,改为:

  1. public class IgnoreAutowiringProcessor implements BeanFactoryPostProcessor {
  2. @Override
  3. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  4. beanFactory.ignoreDependencyType(List.class);
  5. }
  6. }

这次的运行结果:null
从这次的结果来看,如果真的要忽略某个接口的实现类,大不必使用ignoreDependencyInterface方法,ignoreDependencyType方法已经居家必用了。

迷之一样的ignoreDependencyInterface方法到底是用来干嘛的呢?

ignoreDependencyInterface的真正奥义

谷歌苦寻无果,无论中英文都没有发现比较详细的阐述,也说明该方法甚少被使用。只能自行在源码中寻找答案。

经过若干个小时在咖啡厅的不断调试,终于发现最终的奥义,我们只看最终关键的代码。
调用ignoreDependencyInterface方法后,被忽略的接口会存储在BeanFactory的名为ignoredDependencyInterfaces的Set集合中,而调用ignoreDependencyType则存储在ignoredDependencyTypes的Set集合中:

  1. public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
  2. implements AutowireCapableBeanFactory {
  3. private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
  4. private final Set<Class<?>> ignoredDependencyTypes = new HashSet<>();
  5. public void ignoreDependencyType(Class<?> type) {
  6. this.ignoredDependencyTypes.add(type);
  7. }
  8. public void ignoreDependencyInterface(Class<?> ifc) {
  9. this.ignoredDependencyInterfaces.add(ifc);
  10. }
  11. ...
  12. }

ignoredDependencyInterfaces集合在同类中被使用仅在一处——isExcludedFromDependencyCheck方法中:

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

isExcludedFromDependencyCheck方法的意思是判断给定的bean属性在依赖检测中要被排除,假如该方法返回true,也就是在依赖检测中这个bean的属性要被排除,在自动装配时就会被忽略。

通过这个方法的源码也就明白,实际上我们说的在自动装配时忽略某个类或者接口的实现,使用ignoreDependencyType方法已经足够了,因为在isExcludedFromDependencyCheck方法中使用ignoredDependencyTypes集合是否包含属性的类型来判断。
因此在我们例子中,ListHolder对象中的list属性是List接口的实现,而我们又把List.class参数传给ignoreDependencyType方法,自然就会在自动装配时被忽略。

而ignoredDependencyInterface的真正作用还得看AutowireUtils类的isSetterDefinedInInterface方法。

  1. public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
  2. //获取bean中某个属性对象在bean类中的setter方法
  3. Method setter = pd.getWriteMethod();
  4. if (setter != null) {
  5. // 获取bean的类型
  6. Class<?> targetClass = setter.getDeclaringClass();
  7. for (Class<?> ifc : interfaces) {
  8. if (ifc.isAssignableFrom(targetClass) && // bean类型是否接口的实现类
  9. ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) { // 接口是否有入参和bean类型完全相同的setter方法
  10. return true;
  11. }
  12. }
  13. }
  14. return false;
  15. }

这个方法的意思就是判断这一堆接口中有没有某个接口是拥有该bean属性的setter方法的。在我的例子中就是判断List接口有没有list属性类型的setter方法,也就是有无自己本身类型的setter方法。List接口的方法当中当然没有setList(List list)的方法啊,因此也不会生效。

所以ignoredDependencyInterface方法并不是让我们在自动装配时直接忽略实现了该接口的依赖。
这个方法的真正意思是忽略该接口的实现类中和接口setter方法入参类型相同的依赖。
举个例子。首先定义一个要被忽略的接口。

  1. public interface IgnoreInterface {
  2. void setList(List<String> list);
  3. void setSet(Set<String> set);
  4. }

然后需要实现该接口,在实现类中注意要有setter方法入参相同类型的域对象,在例子中就是List和Set。

  1. public class IgnoreInterfaceImpl implements IgnoreInterface {
  2. private List<String> list;
  3. private Set<String> set;
  4. @Override
  5. public void setList(List<String> list) {
  6. this.list = list;
  7. }
  8. @Override
  9. public void setSet(Set<String> set) {
  10. this.set = set;
  11. }
  12. public List<String> getList() {
  13. return list;
  14. }
  15. public Set<String> getSet() {
  16. return set;
  17. }
  18. }

定义xml配置文件:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd"
  9. default-autowire="byType">
  10. <bean id="list" class="java.util.ArrayList">
  11. <constructor-arg>
  12. <list>
  13. <value>foo</value>
  14. <value>bar</value>
  15. </list>
  16. </constructor-arg>
  17. </bean>
  18. <bean id="set" class="java.util.HashSet">
  19. <constructor-arg>
  20. <list>
  21. <value>foo</value>
  22. <value>bar</value>
  23. </list>
  24. </constructor-arg>
  25. </bean>
  26. <bean id="ii" class="com.huxuecong.ignoreDependency.IgnoreInterfaceImpl"/>
  27. <bean class="com.huxuecong.autowire.IgnoreAutowiringProcessor"/>
  28. </beans>

最后调用ignoreDependencyInterface:

  1. beanFactory.ignoreDependencyInterface(IgnoreInterface.class);

运行结果:
null
null
而如果不调用ignoreDependencyInterface,则是:
[foo, bar]
[bar, foo]

忽略接口生效。但其意思和我们最初理解的存在一定的差距。我们最初理解是在自动装配时忽略该接口的实现,实际上是在自动装配时忽略该接口实现类中和setter方法入参相同的类型,也就是忽略该接口实现类中存在依赖外部的bean属性注入。

典型应用就是BeanFactoryAware和ApplicationContextAware接口。
首先看该两个接口的源码:

  1. public interface BeanFactoryAware extends Aware {
  2. void setBeanFactory(BeanFactory beanFactory) throws BeansException;
  3. }
  4. public interface ApplicationContextAware extends Aware {
  5. void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
  6. }

在Spring源码中在不同的地方忽略了该两个接口:

  1. beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
  2. ignoreDependencyInterface(BeanFactoryAware.class);

使得我们的BeanFactoryAware接口实现类在自动装配时不能被注入BeanFactory对象的依赖:

  1. public class MyBeanFactoryAware implements BeanFactoryAware {
  2. private BeanFactory beanFactory; // 自动装配时忽略注入
  3. @Override
  4. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  5. this.beanFactory = beanFactory;
  6. }
  7. public BeanFactory getBeanFactory() {
  8. return beanFactory;
  9. }
  10. }

ApplicationContextAware接口实现类中的ApplicationContext对象的依赖同理:

  1. public class MyApplicationContextAware implements ApplicationContextAware {
  2. private ApplicationContext applicationContext; // 自动装配时被忽略注入
  3. @Override
  4. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  5. this.applicationContext = applicationContext;
  6. }
  7. public ApplicationContext getApplicationContext() {
  8. return applicationContext;
  9. }
  10. }

这样的做法使得ApplicationContextAware和BeanFactoryAware中的ApplicationContext或BeanFactory依赖在自动装配时被忽略,而统一由框架设置依赖,如ApplicationContextAware接口的设置会在ApplicationContextAwareProcessor类中完成:

  1. private void invokeAwareInterfaces(Object bean) {
  2. if (bean instanceof Aware) {
  3. if (bean instanceof EnvironmentAware) {
  4. ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
  5. }
  6. if (bean instanceof EmbeddedValueResolverAware) {
  7. ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
  8. }
  9. if (bean instanceof ResourceLoaderAware) {
  10. ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
  11. }
  12. if (bean instanceof ApplicationEventPublisherAware) {
  13. ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
  14. }
  15. if (bean instanceof MessageSourceAware) {
  16. ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
  17. }
  18. if (bean instanceof ApplicationContextAware) {
  19. ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
  20. }
  21. }
  22. }

通过这种方式保证了ApplicationContextAware和BeanFactoryAware中的容器保证是生成该bean的容器。

但在实践中我们什么时候会使用ignoreDependencyInterface接口?
笔者使用Spring经验有限,只能给出目前的应用场景很少,但起码想到一个:假如我们想自定义一个类似的xxAware接口,比如ApplicationEventMulticasterAware。那么调用ignoreDependencyInterface方法可以保证获取到的ApplicationEventMulticaster对象就是生成该bean容器中的ApplicationEventMulticaster对象。

作者:法兰克胡
链接:https://www.jianshu.com/p/3c7e0608ff1f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。