依赖注入
2021-11-30 14:53:43 0 举报
AI智能生成
spring依赖注入,@Resource,@Autowired,@Value
作者其他创作
大纲/内容
依赖注入的自动注入是通过AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor实现的,都用到了beanPostProssor的2个后置处理方法
AutowiredAnnotationBeanPostProcessor实现了@Autowired、@Value、@Inject,CommonAnnotationBeanPostProcessor实现了@Resource
AutowiredAnnotationBeanPostProcessor
属性填充之前,找到注入点并缓存
分为字段注入和方法注入,每个注点封装成注入点对象并缓存,找到当前类以及父类当中标注了@Autowired、@Value、@Inject的字段和方法,字段封装进AutowiredFieldElement,方法封装进AutowiredMethodElement,都加进一个bean的注点集合List<InjectionMetadata.InjectedElement> elements,所有bean的注点缓存Map<String/*beanName*/, InjectionMetadata> injectionMetadataCache
类变量和方法不处理
父类的注入点也会处理
如果是编译器自动生成的桥接方法,需要找到原方法
桥接方法
方法重写时,子类的返回值类型和父类不相同,或者子类指定了父类的类型参数时,编译器自动生成的方法
桥接方法在字节码中访问标识为ACC_FLAG和ACC_sythenic,可以Method.isBridge来判断某个方法是否是桥接方法
方法重写时,子类的返回值类型可以和父类不相同,可以为父类返回值类型的子类型,这时编译器会在子类中额外生成一个访问标识为ACC_FLAG和ACC_sythenic父类的方法,这个方法就是桥接方法,它会使用invokevitual指令调用子类重写的方法
父类中有泛型的类型参数,而子类指定了具体的泛型类,因为类型擦除机制,父类的方法参数在编译期会被替换为泛型的上确界,这时在子类中也会生成一个父类签名一样的桥接方法
为什么要生成桥接方法:因为方法参数和返回值在JVM中都属于方法签名,当它们不同的时候,对JVM来说不属于方法重写,所以需要桥接方法转发调用
spring如何处理桥接方法:桥接方法在语义上是被子类重写的,因此遇到桥接方法时,需要找到被桥接的方法,具体就是找与桥接方法名称相同,参数个数相同但不是桥接方法的方法
属性填充时,遍历注点注入依赖
DefaultListableBeanFactory.resolveDependency
该方法接收依赖描述器DependencyDescriptor,里面封装了注入点的成员(字段或方法参数),以及所在的beanClass
如果注入点类型是Optional,required设为false,doResolveDependency获取真正的依赖对象封装进Optional,
如果注入点类型是ObjectFactory或ObjectProvider,直接封装成它们的实现类DependencyObjectProvider返回,在getObject的时候才会真正doResolveDependency获取依赖对象,类似于懒加载的方式,ObjectFactory或ObjectProvider还可以在嵌套一个Optional
如果注入点(字段、方法、方法参数、构造器)带了@Lazy注解,生成一个代理对象赋值给注入点,代理对象在使用时才会进行doResolveDependency依赖注入
@Lazy还可以用在类型上,表示当前bean是懒加载的,容器启动时不会创建
DefaultListableBeanFactory.doResolveDependency
该方法功能:根据注入点的依赖描述符和beanName获取真正的依赖对象,beanName是用来判断自引用的
先判断是否有@Value,如果有,获取到@Value的值,可能从environment获取,获取到后直接返回
如果注入点类型是数组、集合、map,根据元素类型从beanFactory中获取bean实例直接返回
java.lang.reflect.Type的子类型有Class、ParameterizedType(参数化类型)、GenericArrayType(泛型数组)、WildcardType(通配符类型)、TypeVariable(泛型变量),后4个就是泛型
ParameterizedType(参数化类型):List<String>,List<T>,getActualTypeArguments可以获取到类型参数,String或T
参数化类型在继承、实现、字段、方法参数、返回值中指定了具体类型时,运行期是可以通过反射获取到的
DefaultListableBeanFactory.findAutowireCandidates
该方法功能:根据类型从beanDefinitionMap中找到候选的beanName,以及beanClass,也有可能是实例化后的单例bean
如果单例池中没有,此时只会进行类型加载,不实例化,因为还需要进一步过滤,避免不需要的bean被实例化了
会从当前beanFactory和父beanFactory找,都加进结果里
对于每个注点,都会遍历所有的beanName查看类型是否匹配,如果遍历到的是FactoryBean,FactoryBean本身的类型和FactoryBean.getObjectType的类型都会参与匹配
依赖注入可以注入FactoryBean.getObject返回的对象,注入点的类型与FactoryBean.getObjectType相同即可
处理自引用:注入点beanName和当前类相同时,会先排除当前bean,如果除当前bean没有候选者,再考虑自引用
处理autowireCandidate:注解只有@Bean能指定该属性,如果false,则排除
处理泛型:容器的bean类型有泛型参数时,匹配泛型参数和它的子类型(上确界)
处理@Qualifier
Qualifier, javax.inject.Qualifier注解都会处理,可以用在类或方法上
通过注点的@Qualifier和beanDefinition的@Qualifier的equals方法来判断是否相等,注解的equals是逻辑相等,所有成员相等即为相等
bean定义上没有定义@Qualifier,则用beanName或别名来和注点匹配
找到候选者后,如果有多个,再找到@Primary,信息在beanDefinition中,如果有多个@Primary,报NoUniqueBeanDefinitionException
如果没有@Primary,找@Priority最小的
根据字段名称和beanName或者别名匹配
找到注入点的beanName后,beanFactory.getBean获取依赖对象
方法注入时,可以有多个参数,注入多个依赖,名称没有要求,只要有注解即为注入点
CommonAnnotationBeanPostProcessor
依赖注入时处理@Resource
属性填充之前,找到注入点并缓存
注入点可以加@Lazy
@Resouce可以指定注入点的名称name和类型type, 默认是字段名称和方法名称(去掉set前缀),字段类型和方法第一个参数类型
属性填充时,遍历注点注入依赖
处理@Lazy
CommonAnnotationBeanPostProcessor.autowireResource: 先根据名称从单例池和beanDefinitionMap中查看beanName是否存在,存在则直接beanFactory.getBean(beanName),否则再DefaultListableBeanFactory.resolveDependency经过byType-->autowiredcandidate-->泛型-->@Qualifier-->@Primary-->@Priority-->byName过滤获取依赖
AutowireCapableBeanFactory.resolveBeanByName,byName获取依赖bean实例
@Autowired和@Resource的区别
@Autowired可以用在构造器,方法,方法参数,字段,注解上,@Resource只能用在方法,字段,类型上
@Resource用在方法上时,只能注入第一个参数,注入点的名称就是去掉set前缀的方法名称,而@Autowired可以注入多个方法参数,并且名称是参数名称
@Autowired会经过byType-->autowiredcandidate-->泛型-->@Qualifier-->@Primary-->@Priority-->byName过滤获取依赖, @Resource则是先byName,当获取不到时再走@Autowired一样的流程
@Resource可以指定注入点的名称和类型,如果显式指定了名称,则只会byName,获取不到则报NoSuchBeanDefinitionException
0 条评论
下一页