在阅读Spring容器扩展部分源码的过程中,我了解到BeanFactory接口中有个方法叫ignoreDependencyInterface。从官方文档的“字面”来看,其作用指定自动装配(autowiring)的时候忽略的接口。还有一个很相似的方法叫ignoreDependencyType,同样其官方字面意思是指自动装配(autowiring)的时候忽略的类。
究竟这两个方法是不是我们的理解相同呢?真的可以让指定的接口和类在自动装配的时候被忽略?有没有注意不到的坑?
/*** Ignore the given dependency interface for autowiring.* <p>This will typically be used by application contexts to register* dependencies that are resolved in other ways, like BeanFactory through* BeanFactoryAware or ApplicationContext through ApplicationContextAware.* <p>By default, only the BeanFactoryAware interface is ignored.* For further types to ignore, invoke this method for each type.* @param ifc the dependency interface to ignore* @see org.springframework.beans.factory.BeanFactoryAware* @see org.springframework.context.ApplicationContextAware*/void ignoreDependencyInterface(Class<?> ifc);/*** Ignore the given dependency type for autowiring:* for example, String. Default is none.* @param type the dependency type to ignore*/void ignoreDependencyType(Class<?> type);
先上结论:
- 自动装配时忽略指定接口或类的依赖注入,使用ignoreDependencyType已经足够
- ignoreDependencyInterface的真正意思是在自动装配时忽略指定接口的实现类中,对外的依赖。
具体详述见下文的逐步踩坑。
“自动装配(autowiring)”的坑
首先我们来试试水。我们试试能不能忽略一些我们常用的类。
假设有一个类,叫ListHolder,就只有一个用@Autowired注解的ArrayList对象。
public class ListHolder {@Autowiredprivate ArrayList<String> list;public ArrayList<String> getList() {return list;}public void setList(ArrayList<String> list) {this.list = list;}}
xml配置文件配置了ArrayList bean和ListHolder bean:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><bean id="list" class="java.util.ArrayList"><constructor-arg><list><value>foo</value><value>bar</value></list></constructor-arg></bean><bean id="listHolder" class="com.huxuecong.ignoreDependency.ListHolder"/><bean class="com.huxuecong.autowire.IgnoreAutowiringProcessor"/><context:annotation-config></context:annotation-config></beans>
我们定义一个BeanFactoryPostProcessor接口实现类,在接口实现类中调用ignoreDependencyType方法:
public class IgnoreAutowiringProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.ignoreDependencyType(ArrayList.class);}}
运行结果:
[foo, bar]
没有起作用。即使调用了ignoreDependencyType方法,ArrayList还是被注入了。说明ignoreDependencyType方法完全不起到该有的作用。
由于我对Spring的了解尚浅,我很怀疑自动装配是不是真的和我了解的相同,使用@Autowired注解就可以了呢?
经过一番中英文搜索之后,我终于发现有博客专门讲ignoreDependencyType和ignoreDependencyInterface方法。
在博客中提到了我之前没用过的自动装配方式:在beans标签中使用default-autowire属性来注入依赖。
于是我这次按照网友的说法,对我的例子进行改造:
(1)ListHolder中的list对象不使用@Autowired注解
public class ListHolder {private ArrayList<String> list;public ArrayList<String> getList() {return list;}public void setList(ArrayList<String> list) {this.list = list;}}
(2)xml配置文件中去掉context:annotation-config/标签,并且在beans标签添加default-autowire的属性,其值为“byType”,意思是按照对象的类型进行装配。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"default-autowire="byType"><bean id="list" class="java.util.ArrayList"><constructor-arg><list><value>foo</value><value>bar</value></list></constructor-arg></bean><bean id="listHolder" class="com.huxuecong.ignoreDependency.ListHolder"/><bean class="com.huxuecong.autowire.IgnoreAutowiringProcessor"/></beans>
这次运行之后,结果就符合期待了:
null
经过这次踩坑,发现英语中的autowiring特定指的是通过beans标签default-autowire属性来依赖注入的方式,而不是指使用@Autowired注解进行的依赖注入。区别在于,使用default-autowire会自动给所有的类都会从容器中查找匹配的依赖并注入,而使用@Autowired注解只会给这些注解的对象从容器查找依赖并注入。
自动装配和@Autowired注解的装配不是同一回事。
但从这次例子来看,ignoreDependencyType方法和我们期待的完全一致,可以在自动装配的时候忽略ArrayList类的对象。
ignoreDependencyInterface的坑
在愉快地使用ignoreDependencyType方法后,立即如法炮制ignoreDependencyInterface方法,如无意外,应该效果一样,使得某个接口的实现类被忽略。
于是在上面的例子上进行改造:
(1)ListHolder类中的ArrayList类型改为List类型:
public class ListHolder {private List<String> list;public List<String> getList() {return list;}public void setList(List<String> list) {this.list = list;}}
(2)ignoreDependencyType改为使用ignoreDependencyInterface方法:
public class IgnoreAutowiringProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.ignoreDependencyInterface(List.class);}}
运行结果:
[foo, bar]
完全不起作用,ignoreDependencyInterface不按照我们的想法工作。曾经一度怀疑Spring是否有bug,但是从Spring的流行性和网上搜索结果来看,这种可能微乎其微,更多是对该方法的理解不对。
我突发奇想,能不能也把接口List.class传给ignoreDependencyType,而不是使用ArrayList.class呢?
于是又在上面的代码基础上,改为:
public class IgnoreAutowiringProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.ignoreDependencyType(List.class);}}
这次的运行结果:null
从这次的结果来看,如果真的要忽略某个接口的实现类,大不必使用ignoreDependencyInterface方法,ignoreDependencyType方法已经居家必用了。
迷之一样的ignoreDependencyInterface方法到底是用来干嘛的呢?
ignoreDependencyInterface的真正奥义
谷歌苦寻无果,无论中英文都没有发现比较详细的阐述,也说明该方法甚少被使用。只能自行在源码中寻找答案。
经过若干个小时在咖啡厅的不断调试,终于发现最终的奥义,我们只看最终关键的代码。
调用ignoreDependencyInterface方法后,被忽略的接口会存储在BeanFactory的名为ignoredDependencyInterfaces的Set集合中,而调用ignoreDependencyType则存储在ignoredDependencyTypes的Set集合中:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactoryimplements AutowireCapableBeanFactory {private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();private final Set<Class<?>> ignoredDependencyTypes = new HashSet<>();public void ignoreDependencyType(Class<?> type) {this.ignoredDependencyTypes.add(type);}public void ignoreDependencyInterface(Class<?> ifc) {this.ignoredDependencyInterfaces.add(ifc);}...}
ignoredDependencyInterfaces集合在同类中被使用仅在一处——isExcludedFromDependencyCheck方法中:
protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {return (AutowireUtils.isExcludedFromDependencyCheck(pd) || this.ignoredDependencyTypes.contains(pd.getPropertyType()) || AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));}
isExcludedFromDependencyCheck方法的意思是判断给定的bean属性在依赖检测中要被排除,假如该方法返回true,也就是在依赖检测中这个bean的属性要被排除,在自动装配时就会被忽略。
通过这个方法的源码也就明白,实际上我们说的在自动装配时忽略某个类或者接口的实现,使用ignoreDependencyType方法已经足够了,因为在isExcludedFromDependencyCheck方法中使用ignoredDependencyTypes集合是否包含属性的类型来判断。
因此在我们例子中,ListHolder对象中的list属性是List接口的实现,而我们又把List.class参数传给ignoreDependencyType方法,自然就会在自动装配时被忽略。
而ignoredDependencyInterface的真正作用还得看AutowireUtils类的isSetterDefinedInInterface方法。
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {//获取bean中某个属性对象在bean类中的setter方法Method setter = pd.getWriteMethod();if (setter != null) {// 获取bean的类型Class<?> targetClass = setter.getDeclaringClass();for (Class<?> ifc : interfaces) {if (ifc.isAssignableFrom(targetClass) && // bean类型是否接口的实现类ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) { // 接口是否有入参和bean类型完全相同的setter方法return true;}}}return false;}
这个方法的意思就是判断这一堆接口中有没有某个接口是拥有该bean属性的setter方法的。在我的例子中就是判断List接口有没有list属性类型的setter方法,也就是有无自己本身类型的setter方法。List接口的方法当中当然没有setList(List list)的方法啊,因此也不会生效。
所以ignoredDependencyInterface方法并不是让我们在自动装配时直接忽略实现了该接口的依赖。
这个方法的真正意思是忽略该接口的实现类中和接口setter方法入参类型相同的依赖。
举个例子。首先定义一个要被忽略的接口。
public interface IgnoreInterface {void setList(List<String> list);void setSet(Set<String> set);}
然后需要实现该接口,在实现类中注意要有setter方法入参相同类型的域对象,在例子中就是List和Set。
public class IgnoreInterfaceImpl implements IgnoreInterface {private List<String> list;private Set<String> set;@Overridepublic void setList(List<String> list) {this.list = list;}@Overridepublic void setSet(Set<String> set) {this.set = set;}public List<String> getList() {return list;}public Set<String> getSet() {return set;}}
定义xml配置文件:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"default-autowire="byType"><bean id="list" class="java.util.ArrayList"><constructor-arg><list><value>foo</value><value>bar</value></list></constructor-arg></bean><bean id="set" class="java.util.HashSet"><constructor-arg><list><value>foo</value><value>bar</value></list></constructor-arg></bean><bean id="ii" class="com.huxuecong.ignoreDependency.IgnoreInterfaceImpl"/><bean class="com.huxuecong.autowire.IgnoreAutowiringProcessor"/></beans>
最后调用ignoreDependencyInterface:
beanFactory.ignoreDependencyInterface(IgnoreInterface.class);
运行结果:
null
null
而如果不调用ignoreDependencyInterface,则是:
[foo, bar]
[bar, foo]
忽略接口生效。但其意思和我们最初理解的存在一定的差距。我们最初理解是在自动装配时忽略该接口的实现,实际上是在自动装配时忽略该接口实现类中和setter方法入参相同的类型,也就是忽略该接口实现类中存在依赖外部的bean属性注入。
典型应用就是BeanFactoryAware和ApplicationContextAware接口。
首先看该两个接口的源码:
public interface BeanFactoryAware extends Aware {void setBeanFactory(BeanFactory beanFactory) throws BeansException;}public interface ApplicationContextAware extends Aware {void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}
在Spring源码中在不同的地方忽略了该两个接口:
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);ignoreDependencyInterface(BeanFactoryAware.class);
使得我们的BeanFactoryAware接口实现类在自动装配时不能被注入BeanFactory对象的依赖:
public class MyBeanFactoryAware implements BeanFactoryAware {private BeanFactory beanFactory; // 自动装配时忽略注入@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}public BeanFactory getBeanFactory() {return beanFactory;}}
ApplicationContextAware接口实现类中的ApplicationContext对象的依赖同理:
public class MyApplicationContextAware implements ApplicationContextAware {private ApplicationContext applicationContext; // 自动装配时被忽略注入@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}public ApplicationContext getApplicationContext() {return applicationContext;}}
这样的做法使得ApplicationContextAware和BeanFactoryAware中的ApplicationContext或BeanFactory依赖在自动装配时被忽略,而统一由框架设置依赖,如ApplicationContextAware接口的设置会在ApplicationContextAwareProcessor类中完成:
private void invokeAwareInterfaces(Object bean) {if (bean instanceof Aware) {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);}}}
通过这种方式保证了ApplicationContextAware和BeanFactoryAware中的容器保证是生成该bean的容器。
但在实践中我们什么时候会使用ignoreDependencyInterface接口?
笔者使用Spring经验有限,只能给出目前的应用场景很少,但起码想到一个:假如我们想自定义一个类似的xxAware接口,比如ApplicationEventMulticasterAware。那么调用ignoreDependencyInterface方法可以保证获取到的ApplicationEventMulticaster对象就是生成该bean容器中的ApplicationEventMulticaster对象。
作者:法兰克胡
链接:https://www.jianshu.com/p/3c7e0608ff1f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
