spring依赖注入

一、 注入方式

spring依赖注入有三种典型的方式,一种通过xml中property标签注入,一种通过注解注入,一种是自动注入(默认关闭)

1. xml


	

标签定义的kv项解析在beanDefinition的PropertyValues里。

 

2. 注解

@Resource,@Value,@Autowire,@Inject

其中@Resource注解被CommonAnnotationBeanPostProcessor拦截处理,该processor过滤每一个bean,逐个判断类成员变量和方法入参有没有被@Resource修饰,从而决定是否注入依赖或回调方法(CommonAnnotationBeanPostProcessor中处理javax定义的注解)。

@Value,@Autowire,@Inject注解被AutowiredAnnotationBeanPostProcessor拦截处理,逻辑同CommonAnnotationBeanPostProcessor一致,这几个是spring定义的注解。

两个Processor中注解的解析和注入均在postProcessPropertyValues方法里,成员变量赋值使用反射,不依赖set方法。注解标记的成员变量已经在postProcessPropertyValues方法里实现注入,而pvs中的内容在之后才会注入(一个原因是pvs还需要对property做转化)。

这两个processor不是默认添加的。

 

3. 自动注入

自动注入有byName和byType两种,默认自动注入处于关闭状态。自动注入扫描发生在populateBean方法执行阶段,扫描到符合条件的结果被存放在pvs里,随后被统一注入。

 

 

二、注入逻辑

此处重点描述pvs的注入操作。部分被注入的内容是一个表达式,实际效果会首先对表达式做渲染,随后再赋值。这一块内容比较复杂,接下来逐一举例分析。

1. 标签


	

示例中SocketAddressTestBean有成员变量version,属性表达式${java.version}。这种配置会形成一个pv对象,附着在beanDefinition上。创建bean时populateProperty方法最后的pv复制阶段,首先对v做解析,默认使用StandardBeanExpressionResolver计算表达式,该resolver基于spel解析器,随后针对计算出的结果做类型转换,最终赋值。spel支持的渲染格式是#{value},因此无法正确对本例version(${java.version})做渲染。

一种改进措施是为beanFactory添加PropertyPlaceholderConfigurer的bean,该bean是spring提供的用于属性替换的类。原理上,PropertyPlaceholderConfigurer是BeanFactoryPostProcessor的实现类,在postProcessBeanFactory方法的实现逻辑中,遍历beanFactory中所有definition,用环境变量渲染所有内容表达式,渲染规则为 ${value:default},实现见:PropertyPlaceholderConfigurer.processProperties(...)方法。因为是beanFactoryPostProcessor的子类,因此会在bean实例化之前就完成definition订正。真正bean创建填充属性时,${java.version}已经被替换。

 

2. @Value / @Autowire注解

@Value("${java.version}")
private String version;

@Autowired(required = false)
private Yzb808 yzb808;

之前提到过,这一组注解依赖AutowiredAnnotationBeanPostProcessor,表达式解析也定义在该processor里,原理上利用beanFactory.resolveDependency(...)方法解析可用依赖。resolveDependency判断注解中是否携带表达式,从而决定进入表达式计算环节还是对应bean获取环节。

表达式计算环节(适用@Value):

1. 嵌入值转化,形如${value:default},默认用Environment转化

2. spel计算

3. TypeConverter类型转换

bean获取环节(适用@Autowire):

1. 根据类型获取bean

2. 如果没有,判断require属性

3. 如果存在多个,则试图通过primary和quafily找出一个

由于该注解在AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues方法里被解析(postProcessPropertyValues方法在populateBean方法里被调用),且渲染出来后直接做了填充(反射赋值),因而不会再受随后的pvs渲染过程影响。

 

3. @Resource

@Resource
private BeanBeDependended beanBeDependended;

Resoure注解被CommonAnnotationBeanPostProcessor解析处理,首先寻找bean中需要被注入的成员变量和方法,随后寻找符合条件的bean做注入,寻找逻辑见:CommonAnnotationBeanPostProcessor.autowireResource方法。

寻找时包含以下场景:

1. 如果使用默认名称(注解的name属性没填),但beanFactory中没有该名称的bean,则使用类型搜索

2. 如果使用指定名称,或使用默认名称且beanFactory中有该名称的bean,则使用名称加类型的方式搜索。

所以一开始理解的Resource优先使用名称,其次使用类型是错误的,因为无论如何,类型一定要匹配。Resource的行为应该这样理解(对于同一个类型有多个bean的场景):

1. 如果指定name,则使用name加类型获取(由于beanName的唯一性,一定能获取到多个中的一个)

2. 如果没指定name,但正巧factory中有名为默认名称的bean,则使用该bean(会做类型校验),否则就用类型获取(可能获得多个)

Resource注解获取bean的逻辑里,获取不到或者获得多个,会直接抛异常,不像Autowire还有required可以处理获取不到的情况。

决定名称和类型的逻辑见CommonAnnotationBeanPostProcessor$ResourceElement构造函数。

决定名称时,优先使用Resource注解的name属性,其次使用成员变量名称或方法名(setXXX方法会去除set前缀),这是之前提到过的默认名称。无论是默认名称还是指定名称,都会使用embeddedValueResolver渲染一次(先用beanFactory.resolveEmbeddedValue获得嵌入值,再用spel解析表达式),得到最终名称。

决定类型时,优先使用Resource注解的type属性,Object类型不算,type未指定时使用被修饰变量类型或被修饰方法第一个入参的类型(也只能修饰只有一个入参的方法)。

 

你可能感兴趣的:(java,spring)