- AUTOWIRE_NO按类型装配
-
- 如果发现有多个同类型的处理方法
-
- DefaultListableBeanFactory的determineAutowireCandidate再次候选
- 总结
- AUTOWIRE_BY_NAME按set的方法名装配
-
- AbstractAutowireCapableBeanFactory的autowireByName根据名字装配
-
- PropertyInfo的get
AUTOWIRE_NO按类型装配
我们了解的自动装配就是@Autowired
吧,基本上我们会在类的属性上用这个,其实这个默认的装配模式是AUTOWIRE_NO
也就是用注入的方式,用的是AutowiredAnnotationBeanPostProcessor
的postProcessProperties
方法处理的,这个确实是以Type
为主,比如DefaultListableBeanFactory
的doResolveDependency
方法,他会先获取响应的类型:
然后后面是根据这个type
去寻找的:
里面就是根据type
去找到相应的bean
名字:
不过最终都是这样:
所以我们常说Autowired
注解是根据类型的,而且找不到类型的话可能会直接报错的,或者返回了,并不会再去找bean
名字:
如果发现有多个同类型的处理方法
比如有同一个接口不同的实现类,这个时候要怎么装配呢,其实他会先进行候选,按照Primary
和Priority
优先级选,其实就是注解啦,如果都没有就看要依赖的名字和候选类的名字(首字母小写)是不是一样,一样的话就可以,否则还是找不到,返回null
:
DefaultListableBeanFactory的determineAutowireCandidate再次候选
这里就是刚才说的候选原则,也就是说,Primary
的优先,Priority
次之,最后再看名字对不对的上。
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback最后去匹配参数依赖名字是否和类型名字一样
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}
这里要注意@Primary
只能再一个类上,不然会报异常,具体的可以去看determinePrimaryCandidate
源码,还有@Priority(xx)
可以在多个类上,只要xx
不一样就行,越小优先级越高,一样也会报异常。这两种一样就还是不知道要用哪个,最后才会看名字跟类名是不是一样。
总结
所以常说的Autowired
注解是先看类型再看名字,其实不太严谨,还有考虑优先级,而且名字也要跟类名对的上。
AUTOWIRE_BY_NAME按set的方法名装配
这个就是按setXXX
方法名,也就是set
后的XXX
首字母小写,xXX
名字进行装配。
我们可以改变自动装配的模式,比如用ImportBeanDefinitionRegistrar
:
public class AutoWiredTypeRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition autowiredBean = (RootBeanDefinition) registry.getBeanDefinition("getAutowiredBean");
autowiredBean.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
}
}
我们的配置类MyConfig
:
@Configuration
@ComponentScan(value = {
"com.ww.postprocessors"})
@Import({
AutoWiredTypeRegistrar.class})
public class MyConfig {
@Bean
public AutowiredBean getAutowiredBean() {
return new AutowiredBean();
}
}
我们看看他会怎么做,在填充注入的AbstractAutowireCapableBeanFactory
的populateBean
中有两种方式:
AbstractAutowireCapableBeanFactory的autowireByName根据名字装配
我们以这个类AutowiredBean
为例:
public class AutowiredBean {
private Bean1 autoBean1;
public Bean1 getBean12() {
return autoBean1;
}
public void settYBean1(Bean1 bean1) {
System.out.println(bean1);
}
public void setmyBean1(Bean1 bean1) {
System.out.println(bean1);
}
public Bean1 getReturnBean1(int i) {
return null;
}
public static void setStaticBean1(Bean1 bean1) {
System.out.println(bean1);
}
public void setBean1(Bean1 bean1) {
System.out.println(bean1);
}
}
其实就是获取set
开头的方法名字,然后去找是否有这个名字的bean
定义,如果有就直接获取,没有就没有了。
PropertyInfo的get
最终会调用到这里,这个才是真正的寻找的方法,代码比较多,我截取了主要的,这里所有的读写方法都会保存,这里就会筛选出一些方法:
...
for (Method method : methods) {
if (!Modifier.isStatic(method.getModifiers())) {
Class<?> returnType = method.getReturnType();
String name = method.getName();
switch (method.getParameterCount()) {
case 0:
if (returnType.equals(boolean.class) && isPrefix(name, "is")) {
PropertyInfo info = getInfo(map, name.substring(2), false);
info.read = new MethodInfo(method, boolean.class);
} else if (!returnType.equals(void.class) && isPrefix(name, "get")) {
PropertyInfo info = getInfo(map, name.substring(3), false);
info.readList = add(info.readList, method, method.getGenericReturnType());
}
break;
case 1:
if (returnType.equals(void.class) && isPrefix(name, "set")) {
PropertyInfo info = getInfo(map, name.substring(3), false);
info.writeList = add(info.writeList, method, method.getGenericParameterTypes()[0]);
} else if (!returnType.equals(void.class) && method.getParameterTypes()[0].equals(int.class) && isPrefix(name, "get")) {
PropertyInfo info = getInfo(map, name.substring(3), true);
info.readList = add(info.readList, method, method.getGenericReturnType());
}
break;
case 2:
if (returnType.equals(void.class) && method.getParameterTypes()[0].equals(int.class) && isPrefix(name, "set")) {
PropertyInfo info = getInfo(map, name.substring(3), true);
info.writeList = add(info.writeList, method, method.getGenericParameterTypes()[1]);
}
break;
}
}
...
最终可以拿到,里面的class
方法,是object
的getClass
:
接下去就是过滤啦,过滤出所有写方法,还有不要简单的类型,还有数组内的类型,比如下面的一些类型是不要的:
最终过滤出了4
个set
方法:
可惜他们既不在单例缓存中,也不在bean
定义中,下面的条件都是返回false
,所以不会自动装配:
所以按AUTOWIRE_BY_NAME
名字装配是可能装配不上的。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。