Spring 5.x 源码之旅五十五自动装配的秘密一

AUTOWIRE_NO按类型装配

我们了解的自动装配就是@Autowired吧,基本上我们会在类的属性上用这个,其实这个默认的装配模式是AUTOWIRE_NO也就是用注入的方式,用的是AutowiredAnnotationBeanPostProcessorpostProcessProperties方法处理的,这个确实是以Type为主,比如DefaultListableBeanFactorydoResolveDependency方法,他会先获取响应的类型:
在这里插入图片描述
然后后面是根据这个type去寻找的:
在这里插入图片描述
里面就是根据type去找到相应的bean名字:
在这里插入图片描述
不过最终都是这样:
在这里插入图片描述
所以我们常说Autowired注解是根据类型的,而且找不到类型的话可能会直接报错的,或者返回了,并不会再去找bean名字:
在这里插入图片描述

如果发现有多个同类型的处理方法

比如有同一个接口不同的实现类,这个时候要怎么装配呢,其实他会先进行候选,按照PrimaryPriority优先级选,其实就是注解啦,如果都没有就看要依赖的名字和候选类的名字(首字母小写)是不是一样,一样的话就可以,否则还是找不到,返回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();
    }
}

我们看看他会怎么做,在填充注入的AbstractAutowireCapableBeanFactorypopulateBean中有两种方式:
在这里插入图片描述

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方法,是objectgetClass
在这里插入图片描述
接下去就是过滤啦,过滤出所有写方法,还有不要简单的类型,还有数组内的类型,比如下面的一些类型是不要的:
在这里插入图片描述

最终过滤出了4set方法:
在这里插入图片描述
可惜他们既不在单例缓存中,也不在bean定义中,下面的条件都是返回false,所以不会自动装配:
在这里插入图片描述
所以按AUTOWIRE_BY_NAME名字装配是可能装配不上的。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值