文章目录
Spring中的ignoreDependencyInterface 和 ignoreDependencyType
起因:在学习spring启动的源码过程中,发现其方法prepareBeanFactory中有大量的调用ignoreDependencyInterface方法,如下图所示
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//......
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);
//.......
}
很好奇这个方法是做什么的,结果发现只是在Set集合中添加元素,如下图所示
private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
//......
public void ignoreDependencyInterface(Class<?> ifc) {
this.ignoredDependencyInterfaces.add(ifc);
}
于是便上网查询相关的资料,发现类似的方法有两个ignoreDependencyInterface 和 ignoreDependencyType,便一探究竟。
ignoreDependencyType
根据spring的注释,翻译出来为:忽略给定的依赖类型以进行自动装配
根据字面意思,接下来我们便来试试水
CASE1
- 先创建两个类A.java 和 C.java,并且在xml中配置,具体如下图所示:
//A类 使用@Autowired注入C
public class A {
@Autowired
private C c;
public A() {
System.out.println("A初始化了");
}
}
//C类
public class C {
private String name;
public C() {
System.out.println("C初始化了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//启动类
public class MainAutowired {
public static void main(String[] args) {
ApplicationContext applicationContext1 = new ClassPathXmlApplicationContext("classpath:application.xml");
A bean = applicationContext1.getBean(A.class);
}
}
- 配置文件
<context:annotation-config/>
开启注解
- 首先先在正常情况下debug跑起来,能看到A类中的属性C不为Null,并且name是zhangsan
CASE2
- 接下来添加一个IgnoreDependencyTest类,调用beanFactory的ignoreDependencyType方法,并且在xml中配置此类。
public class IgnoreDependencyTest implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.ignoreDependencyType(C.class);
}
}
- debug程序,发现和我们预期的不太一样,C类还是被注入进去了,难道是ignoreDependencyType无效吗?
CASE3
-
结合网上资料来看,在beans标签中使用default-autowire属性来注入依赖。对例子进行改造:
去掉A.java的@Autowired
//A类 使用@Autowired注入C public class A { private C c; public A() { System.out.println("A初始化了"); } }
去掉配置文件xml中的
<context:annotation-config>
,并且增加default-autowire="byType"
,其值为“byType",意思是按照对象的类型进行装配。
-
其他类不改变,此时debug启动,发现A.java中的C属性为null了,ignoreDependencyType生效了,结果就符合期待了:
小结
经过实验发现,发现英语中的autowiring特定指的是通过beans标签default-autowire属性来依赖注入的方式,而不是指使用@Autowired注解进行的依赖注入。区别在于,使用default-autowire会自动给所有的类都会从容器中查找匹配的依赖并注入,而使用@Autowired注解只会给这些注解的对象从容器查找依赖并注入。自动装配和@Autowired注解的装配不是同一回事。从这次例子来看,ignoreDependencyType方法和我们期待的完全一致,可以在自动装配的时候忽略C类的对象。
ignoreDependencyInterface
根据翻译可以知道:忽略给定的依赖接口以进行自动装配
/**
* 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);
ignoredDependencyInterface方法并不是让我们在自动装配时直接忽略实现了该接口的依赖。这个方法的真正意思是忽略该接口的实现类中和接口setter方法入参类型相同的依赖。
CASE4
1.新增接口D、All类,并且在配置文件增加all的配置信息
//接口D
public interface D {
void setA(A a);
}
//类All 实现了D并且实现了set方法
public class All implements D{
private A a;
private C c;
@Override
public void setA(A a) {
this.a = a;
}
public A getA() {
return a;
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
}
- debug运行先注释掉
//beanFactory.ignoreDependencyInterface(D.class);
,可以看到a和c都是有值的
- 打开
beanFactory.ignoreDependencyInterface(D.class);
可以看到a的值为null,说明ignoreDependencyInterface生效了
小结
其意思和我们最初理解的存在一定的差距。我们最初理解是在自动装配时忽略该接口的实现,实际上是在自动装配时忽略该接口实现类中和setter方法入参相同的类型,也就是忽略该接口实现类中存在依赖外部的bean属性注入。
源码跟踪
通过跟踪源码,我们可以发现,调用ignoreDependencyInterface方法后,被忽略的接口会存储在BeanFactory的名为ignoredDependencyInterfaces的Set集合中,而调用ignoreDependencyType则存储在ignoredDependencyTypes的Set集合中,如下所示:
在该方法中,会判断给定的bean属性在依赖检测中要被排除,假如该方法返回true,也就是在依赖检测中这个bean的属性要被排除,在自动装配时就会被忽略。
protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||
this.ignoredDependencyTypes.contains(pd.getPropertyType()) ||
AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));
}
其中,this.ignoredDependencyTypes.contains()就是判断我们往ignoreDependencyType集合中添加的忽略注入的类型是否存在,而AutowireUtils.isSetterDefinedInInterface就是判断ignoredDependencyInterface的相关代码了,我们继续走下去:
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> 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;
}
DEBUG进来可以看到,interfaces中的D类就是我们先前定义的接口,首先获取该bean(All.java)的setter方法,如果不存在setter,直接返回false,否则判断setter的targetClass(也就是All.java)是否是interfaces中接口的子类/子接口/相同(PS:isAssignableFrom用法可自行百度),并且该interfaces中的接口是否拥有该setter方法,如果都满足,则返回true。
总结
1.ignoreDependencyType(Class class)方法只影响xml配置自动装配的依赖注入方式.
2.ignoreDependencyType方法虽然在源码中有使用,但我觉得一般情况下不会用到它,一是因为在使用过程中都希望自动注入依赖,二是因为有@AutoWired注解,可以决定哪些依赖必须注入
3.是因为目前使用最多最流行的是SpringBoot方式配置bean,xml方式将更少触及.
4.ignoreDependency方法忽略指定依赖类型的自动装配,ignoreDependency和ignoreDependencyInterface不同的是一个前者按照class类型进行忽略自动装配,后者按照接口中的set方法进行忽略自动装配.**
补充:
研究下来,也算解决了自己的困惑,但是对于spring源码中使用它的用意还没有了解到,其次如果在xml中的bean标签中,如果增加<property >
标签会导致这两个方法失效,目前不知道是什么原因,此处留个坑,日后来填!!!
参考资料:https://www.jianshu.com/p/3c7e0608ff1f?from=singlemessage