Spring之依赖注入底层源码解析(一)
Spring之依赖注入底层源码解析
- Spring依赖注入底层流程图
- Spring中依赖注入的方式
- 手动注入
- 自动注入
- @Autowired自动注入
- 寻找注入点
- static的字段或方法为什么不支持依赖注入
- 桥接方法
- 注入点进行注入
- 字段注入
- set方法注入
- 注入点注入流程图
Spring依赖注入底层流程图
- 首先先去找到所有注入点,注入点分为两种: 被@Autowired注解了的属性 以及 被@Autowired注解了的方法
- 遍历每个注入点
- 根据注入点去找Bean,这里如果注解了属性的会根据属性的类型去找Bean,如果注解了的是方法,那就根据方法中的参数类型去找Bean
- 对于找到有几个Bean
- 如果找到一个Bean,直接把这个Bean进行注入
- 如果找到多个Bean,会从多个Bean中确定出一个Bean
- 先判断是不是isAutowiredCandidate
- 再判断是不是复合Qualifier
- 如果还有多个,取@Primary的Bean
- 取出优先级最高的Bean
- 根据名字进行筛选,筛选出唯一的Bean
- 如果是根据属性注入的,那么就是根据属性名字去找
- 如果是根据set方法,那就是参数名字
- 如果没有找到Bean
- 如果@Autowired的reuqired属性为true,也就是说注入点必须被注入,那么就会报错
Spring中依赖注入的方式
对于Spring中依赖注入的方式,分为两种:
- 手动注入
- set方法注入
- 构造方法注入
- 自动注入
- XML的autowire自动注入
- @Autowired注解的自动注入
手动注入
在XML中定义Bean时,就是手动注入,因为是程序员手动给某个属性指定的值,下面这种底层是通过set方法进行注入
<bean name="userService" class="linc.fun.service.UserService"> <property name="orderService" ref="orderService"/> </bean>
下面这种是通过构造方法进行注入
<bean name="userService" class="linc.fun.service.UserService"> <constructor‐arg index="0" ref="orderService"/> </bean>
自动注入
自动注入又分为两种
- XML的autowire自动注入
- @Autowired注解的自动注入
XML的autowire自动注入
在XML中,我们可以在定义一个Bean时去指定这个Bean的自动注入模式:
- byType
- byName
- constructor
- default
- no
<bean id="userService" class="linc.fun.service.UserService" autowire="byType"/>
如果这么写,表示Spring会自动的给userService中所有的属性进行自动赋值,不需要在属性上加@Autowired注解,但需要这个属性有对应的set方法
PropertyDescriptor对象
在创建Bean的过程中,在填充属性时,Spring会去解析当前类,把当前类的所有方法都解析出来,Spring会解析每个方法得到对应的PropertyDescriptor对象,PropertyDescriptor有几个属性:
public class PropertyDescriptor extends FeatureDescriptor { private Reference<? extends Class<?>> propertyTypeRef; private final MethodRef readMethodRef = new MethodRef(); private final MethodRef writeMethodRef = new MethodRef(); private Reference<? extends Class<?>> propertyEditorClassRef; private boolean bound; private boolean constrained; // The base name of the method name which will be prefixed with the // read and write method. If name == "foo" then the baseName is "Foo" private String baseName; private String writeMethodName; private String readMethodName; public PropertyDescriptor(String propertyName, Class<?> beanClass) throws IntrospectionException { this(propertyName, beanClass, Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName), Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName)); }.....}
static final String GET_PREFIX = "get";static final String SET_PREFIX = "set";static final String IS_PREFIX = "is";
- name: 这个name并不是方法的名字,而是拿方法名字进行过处理后的名字
- 如果方法名字以"get"开头,比如"getXXX",那么name=XXX
- 如果方法名字以"is"开头,比如"isXXX",那么name=XXX
- 如果方法名字以"set"开头,比如"setXXX",那么name=XXX
- readMethodRef: 表示get方法的Method对象的引用
- readMethodName: 表示get方法的名字
- writeMethodRef: 表示set方法的Method对象的引用
- writeMethodName: 表示set方法的名字
- propertyTypeRef: 如果有get方法那么对应的就是返回值的类型,如果set方法那么对应的就是set方法中唯一参数的类型
get,set方法的定义
对于get方法的定义是: 方法参数个数为0,并且方法名字以"get"开头或者方法名字以"is"开头并且方法的返回类型为boolean
对于set方法的定义是: 方法参数个数为1,并且方法以"set"开头并且方法返回类型为void
byName自动填充属性时的流程
Spring在通过byName的自动填充属性时的流程是:
- 找到所有set方法所对应的XXX部分的名字
- 根据XXX部分的名字去获取Bean
byType的自动填充属性时的流程
- 获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取Bean
- 如果找到多个,会报错
constructor注入
以上,分析了autowire的byType和byName情况,那么接下来分析constructor,constructor表示通过构造方法注入,其实这种情况就比较简单了,没有byType和byName那么复杂
如果是constructor,那么就可以不写set方法了,当某个Bean是通过构造方法来注入时,Spring利用构造方法的参数信息从Spring容器中去找Bean,找到Bean之后作为参数传给构造方法,从而实例化得到一个Bean对象,并完成属性赋值(属性赋值的代码需要程序员来实现)
这里,我们只考虑只有一个有参数构造方法,其实构造方法注入相当于 byType+byName ,普通的byType是根据set方法的参数类型去找Bean,找到多个会报错,而constructor就是通过构造方法中的参数类型去找Bean,如果找到多个会根据参数名确定
为什么选择使用@Autowired,而不用XML的形式呢
@Autowired注解相当于XML中的autowire属性的注解方式的替代
官网如下解释: 从本质上将,@Autowired注解提供了与autowire相同的功能,但是拥有更细粒度的控制和更广泛的适用性
Essentially, the @Autowired annotation provides the same capabilities as described in Autowiring Collaborators but with more fine‐grained control and wider applicability
这里强调的是更细粒度的控制
XML中的autowire控制的是整个Bean的所有属性,而@Autowired注解是直接写在某个属性上、某个set方法、某个构造方法上的
倘若我们使用XML的形式,如果用XML中的autowire=constructor,在拥有多个构造方法的情况下,无法进行很好的控制,但是通过@Autowired注解可以直接指定想用哪个构造方法
同时,用@Autowired注解,还可以控制哪些属性想被自动注入,哪些属性不想,这也是细粒度的控制
但是@Autowird无法区分byType和byName,@Autowired是先byType,如果找到多个就会选择byName
那么XML的自动注入底层其实也就是
- set方法注入
- 构造方法注入
@Autowired注解的自动注入
@Autowired注解是byType+byName的结合
对于@Autowired注解可以有如下几种使用方式
- 属性上: 先根据属性类型去找Bean,如果找到多个再根据属性名去确定一个
- 构造方法: 现根据方法参数类型去找Bean,如果找到多个再根据参数名去确定一个
- set方法上: 先根据方法参数类型去找Bean,如果找到多个再根据参数名去确定一个
对于底层到了
- 属性注入
- set方法注入
- 构造方法注入
寻找注入点
在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition找出注入点并且缓存
/** * 实例化后的合并BeanDefinition之后的BeanDefinition进行处理 * 找注入点 */@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);}
/** * 寻找注入点,看哪些需要注入,哪些set方法上加了注解 */private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// Fall back to class name as cache key, for backwards compatibility with custom callers.String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// Quick check on the concurrent map first, with minimal locking.InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}// 解析注入点并缓存metadata = buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;}
找注入点的流程
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {// 如果一个Bean的类型是String...,那么则根本不需要进行依赖注入if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();// 遍历targetClass中的所有FieldReflectionUtils.doWithLocalFields(targetClass, field -> {// field上是否存在@Autowired、@Value、@Inject中的其中一个MergedAnnotation<?> ann = findAutowiredAnnotation(field);if (ann != null) {// static filed不是注入点,不会进行自动注入// 在两个bean都是原型bean的情况下,其中一个是static bean注入,可能导致对象内部属性的值发生变化if (Modifier.isStatic(field.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static fields: " + field);}return;}// 构造注入点boolean required = determineRequiredStatus(ann);currElements.add(new AutowiredFieldElement(field, required));}});// 遍历targetClass中的所有MethodReflectionUtils.doWithLocalMethods(targetClass, method -> {// 处理桥接方法 泛型的时候,字节码会有两个set方法Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}// method上是否存在@Autowired、@Value、@Inject中的其中一个MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {// static method不是注入点,不会进行自动注入if (Modifier.isStatic(method.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static methods: " + method);}return;}// set方法最好有入参,没有就会打印一条日志if (method.getParameterCount() == 0) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation should only be used on methods with parameters: " +method);}}// 判断是否是required = true 如果是,但是没找到可以赋值给它就会报错,false就没事boolean required = determineRequiredStatus(ann);PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new AutowiredMethodElement(method, required, pd));}});// 父类的会放在最前面elements.addAll(0, currElements);targetClass = targetClass.getSuperclass(); // 找父类的}while (targetClass != null && targetClass != Object.class);// 返回并且缓存return InjectionMetadata.forElements(elements, clazz);}
- 遍历当前类的所有的属性字段field
- 查看字段上是否存在@Autowired、@Value、@Inject中其中任意一个,存在则认为该字段是一个注入点
- 如果字段是static,则不进行注入
- 获取@Autowired中的required属性的值
- 将字段信息构建成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中
- 遍历当前类的所有方法Method
- 判断当前Method是否存在桥接方法,如果是,找到原方法
- 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点
- 如果方法是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中
- 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类
- 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并且缓存
static的字段或方法为什么不支持依赖注入
@Component@Scope("prototype")public class CService {}@Component@Scope("prototype")public class DService {@Autowiredprivate static CService cService;private void test() {System.out.println("test...");}}
在CService和DService都是原型Bean的情况下,倘若Spring支持static字段进行自动注入,那么现在调用两次
@Testpublic void testPrototypeStatic() {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);DService d1 = ctx.getBean(DService.class);DService d2 = ctx.getBean(DService.class);}
对于此时,d1的CService值是什么?还是它自己注入的值吗
很显然不是,一旦d2创建好了,static Cservice字段的值就发生了改变了,从而会出现bug
桥接方法
// class version 52.0 (52)// access flags 0x21// signature Ljava/lang/Object;Llinc/cool/service/UserInterface;// declaration: linc/cool/service/UserService implements linc.cool.service.UserInterfacepublic class linc/cool/service/UserService implements linc/cool/service/UserInterface { // compiled from: UserService.java // access flags 0x2 private Llinc/cool/service/OrderService; orderService // access flags 0x1 public <init>()V L0 LINENUMBER 10 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V RETURN L1 LOCALVARIABLE this Llinc/cool/service/UserService; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public test()V L0 LINENUMBER 13 L0 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V ALOAD 0 INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; INVOKEVIRTUAL java/lang/Class.getName ()Ljava/lang/String; INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; LDC ".test()" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L1 LINENUMBER 14 L1 RETURN L2 LOCALVARIABLE this Llinc/cool/service/UserService; L0 L2 0 MAXSTACK = 3 MAXLOCALS = 1 // access flags 0x1 public setOrderService(Llinc/cool/service/OrderService;)V @Lorg/springframework/beans/factory/annotation/Autowired;() L0 LINENUMBER 22 L0 ALOAD 0 ALOAD 1 PUTFIELD linc/cool/service/UserService.orderService : Llinc/cool/service/OrderService; L1 LINENUMBER 24 L1 RETURN L2 LOCALVARIABLE this Llinc/cool/service/UserService; L0 L2 0 LOCALVARIABLE orderService Llinc/cool/service/OrderService; L0 L2 1 MAXSTACK = 2 MAXLOCALS = 2 // access flags 0x1041 桥接方法 public synthetic bridge setOrderService(Ljava/lang/Object;)V @Lorg/springframework/beans/factory/annotation/Autowired;() L0 LINENUMBER 10 L0 ALOAD 0 ALOAD 1 CHECKCAST linc/cool/service/OrderService INVOKEVIRTUAL linc/cool/service/UserService.setOrderService (Llinc/cool/service/OrderService;)V RETURN L1 LOCALVARIABLE this Llinc/cool/service/UserService; L0 L1 0 MAXSTACK = 2 MAXLOCALS = 2}
可以看到UserService的字节码中有两个setOrderService方法
public setOrderService(Llinc/cool/service/OrderService;)Vpublic synthetic bridge setOrderService(Ljava/lang/Object;)V
并且都是存在@Autowired注解的,所以在Spring中需要处理这种情况,当遍历到桥接方法时,得找到原方法
注入点进行注入
Spring在AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,会遍历所找到的注入点依次进行注入
@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 找注入点(所有被@Autowired注解了的Field或Method) 此时已经可以直接从缓存中拿出来了InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {// 给字段赋值,给方法参数赋值metadata.inject(bean, beanName, pvs);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;}
/** * 注入 */public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {// 遍历每个注入点进行依赖注入for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}}
字段注入
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { ... /** * 字段的注入 */ @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 这里没有进行判断Pvs有没有,和method有点区别 Field field = (Field) this.member; Object value; if (this.cached) { // 对于原型Bean,第一次创建的时候,也找注入点,然后进行注入,此时cached为false,注入完了之后cached为true // 第二次创建的时候,先找注入点(此时会拿到缓存好的注入点),也就是AutowiredFieldElement对象,此时cache为true,也就进到此处了 // 注入点内并没有缓存被注入的具体Bean对象,而是beanName,这样就能保证注入到不同的原型Bean对象 try { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // Unexpected removal of target bean for cached argument -> re-resolve value = resolveFieldValue(field, bean, beanName); } } else { // 根据filed从BeanFactory中查到的匹配的Bean对象 value = resolveFieldValue(field, bean, beanName); } // 反射给filed赋值 if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } }}
@Nullableprivate Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set<String> autowiredBeanNames = new LinkedHashSet<>(1); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); Object value; try { value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } synchronized (this) { if (!this.cached) { Object cachedFieldValue = null; if (value != null || this.required) { cachedFieldValue = desc; // 注册一下beanName依赖了autowiredBeanNames, registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { // 构造一个ShortcutDependencyDescriptor作为缓存,保存了当前filed所匹配的autowiredBeanName,而不是对应的bean对象 (考虑原型bean) cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType()); } } } this.cachedFieldValue = cachedFieldValue; this.cached = true; } } return value;}
- 遍历所有的AutowiredFieldElement对象
- 将对应的字段封装为DependencyDescriptor对象
- 调用BeanFactory#resolveDependency方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortCutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该对象时,就可以拿缓存的结果对象beanName去BeanFactory中去拿Bean对象了,不用再次去进行查找
- 利用反射将结果赋值给对象的字段
Set方法注入
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { ... @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 如果pvs中已经有当前注入点的值了,则跳过注入 看属性描述器中对应属性的名字在不在pvs里,在就跳过 if (checkPropertySkipping(pvs)) { return; } Method method = (Method) this.member; Object[] arguments; // 找值的 // 第一次注入是没有缓存的 if (this.cached) { try { arguments = resolveCachedArguments(beanName); } catch (NoSuchBeanDefinitionException ex) { // Unexpected removal of target bean for cached argument -> re-resolve arguments = resolveMethodArguments(method, bean, beanName); } } else { arguments = resolveMethodArguments(method, bean, beanName); } if (arguments != null) { try { ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }}
@Nullableprivate Object[] resolveMethodArguments(Method method, Object bean, @Nullable String b // 方法参数个数 int argumentCount = method.getParameterCount(); // 保存参数对应的Bean对象 Object[] arguments = new Object[argumentCount]; // 有几个方法参数就有几个依赖描述器 DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount]; Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); // 遍历每个方法参数,找到匹配的bean对象 for (int i = 0; i < arguments.length; i++) { MethodParameter methodParam = new MethodParameter(method, i); // this.required: 是不是一定要赋值 DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.req currDesc.setContainingClass(bean.getClass()); descriptors[i] = currDesc; try { Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBe if (arg == null && !this.required) { arguments = null; break; } arguments[i] = arg; } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoin } } synchronized (this) { if (!this.cached) { if (arguments != null) { DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descripto registerDependentBeans(beanName, autowiredBeans); if (autowiredBeans.size() == argumentCount) { Iterator<String> it = autowiredBeans.iterator(); Class<?>[] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, paramTypes[cachedMethodArguments[i] = new ShortcutDependencyDescripto descriptors[i], autowiredBeanName, paramTypes[i]); } } } this.cachedMethodArguments = cachedMethodArguments; } else { this.cachedMethodArguments = null; } this.cached = true; } } return arguments;}
- 遍历所有的AutowiredMethodElement对象
- 遍历将对应的方法的参数,将每个参数封装成MethodParameter对象
- 将MethodParameter对象封装成DependencyDescriptor对象
- 调用BeanFactory#resolveDependency方法,传入DependencyDescriptor对象,进行依赖查找,摘到当前方法参数所匹配的Bean对象
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建对象时,就可以直接拿缓存的结果对象beanName去BeanFactory中去拿Bean对象了,不用再次去进行查找
- 利用反射将找到的所有结果对象传给当前方法,并且执行