Spring注入方式解析与实践

你好,我是柳岸花开。

在Spring框架中,依赖注入(Dependency Injection)是一种重要的实现方式,它通过将对象依赖关系的管理交给Spring容器来完成,使得代码更加灵活、可维护性更高。在Spring中,有多种注入方式,本文将详细介绍手动注入和自动注入两种主要方式,并对寻找注入点和注入对象进行深入分析。

注入方式

手动注入

手动注入是通过配置XML或者Java代码来完成依赖关系的管理。在Spring中,手动注入有两种主要方式:set方式注入和构造方法注入。

set方式注入

通过XML配置文件,使用<property>标签为Bean的属性赋值,示例如下:

<bean name="userService" class="com.bob.service.UserService">
    <property name="orderService" ref="orderService"/>
</bean>

或者使用构造方法注入:

构造方法注入
<bean name="userService" class="com.bob.service.UserService">
    <constructor-arg index="0" ref="orderService"/>
</bean>

自动注入

自动注入是Spring框架中更为便捷的注入方式,它不需要手动配置每个Bean的依赖关系,而是由Spring框架自动完成。自动注入包括XML的autowire自动注入和@Autowired注解的自动注入。

XML的autowire自动注入

通过在XML配置文件中设置autowire属性,可以实现自动注入。例如:

<bean id="userService" class="com.bob.service.UserService" autowire="byType"/>

Spring会自动为userService中的所有属性自动赋值,底层实现是基于set方法注入和构造方法注入。

自动注入模式包括:

  • byType: 根据属性的类型自动装配,如果找到多个符合类型的Bean则报错。
  • byName: 根据属性的名称自动装配,通过名称匹配Bean。
  • constructor: 通过构造方法注入,可以不写set方法。
  • default / no: 关闭自动注入。
@Autowired注解的自动注入

@Autowired注解是一种更为灵活的自动注入方式,它可以用于属性、构造方法、或者set方法上。

@Component
public class Example {
    @Autowired
    private SomeService someService;

    @Autowired
    public Example(SomeService someService) {
        this.someService = someService;
    }
}

@Autowired注解提供了细粒度的控制,能够指定具体的注入方式以及要注入的Bean。

寻找注入点

在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()找出注入点并缓存。

找注入点的流程为:

  1. 遍历当前类的所有的属性字段Field
  2. 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
  3. 如果字段是static的,则不进行注入
  4. 获取@Autowired中的required属性的值
  5. 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。
  6. 遍历当前类的所有方法Method
  7. 判断当前Method是否是桥接方法,如果是找到原方法
  8. 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点
  9. 如果方法是static的,则不进行注入
  10. 获取@Autowired中的required属性的值
  11. 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中
  12. 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类
  13. 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存

注入点进行注入

Spring在AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法中,会遍历所找到的注入点依次进行注入。

字段注入:

  1. 遍历所有的AutowiredFieldElement对象。
  2. 将对应的字段封装为DependencyDescriptor对象。
  3. 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象。
  4. 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
  5. 利用反射将结果对象赋值给字段。

Set方法注入:

  1. 遍历所有的AutowiredMethodElement对象
  2. 遍历将对应的方法的参数,将每个参数封装成MethodParameter对象
  3. 将MethodParameter对象封装为DependencyDescriptor对象
  4. 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。
  5. 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
  6. 利用反射将找到的所有结果对象传给当前方法,并执行。

获取Bean对象

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
  implements ConfigurableListableBeanFactoryBeanDefinitionRegistrySerializable 
{
            // 根据DependencyDescriptor依赖描述从BeanFactory中找出对应的唯一的一个Bean对象。
            public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
   @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)
 throws BeansException 
{
                
            }
}

resolveDependency()

  1. 判断descriptor.getDependencyType()
  • 如果是Optional.class,返回包装成Optional的对象
  • ObjectFactory或ObjectProvider,返回DependencyObjectProvider对象
  • 如果是javax.inject.Provider类对象,则返回Jsr330Provider对象
  • 如果依赖描述上有@Lazy注解,那么会生成一个代理对象然后返回,那么在调用代理对象中的方法时调用doResolveDependency(descriptor)。或这,直接生成一个ObjectFactory对象,在调用ObjectFactory的getObject方法时调用调用doResolveDependency(descriptor)
  1. doResolveDependency()方法
  • 是否存在@Value注解,如果存在,获取并解析descriptor上的@Value注解,进行解析并返回
  • 如果不存在@Value注解,判断descriptor的类型

1.如果是Map,直接返回map
2.如果是Collection,则会调用方法findAutowireCandidates(beanName, type, descriptor),该方法返回一个Map,表示会根据type去找bean,Map的key为beanName,Map的value为对象(注意可能是bean对象,也可能是某个bean的class对象,因为该方法只负责根据类型找到对应的bean,如果该bean还没有实例化,那么该方法不负责去实例化,只返回该Bean对应的Class对象,表示这个Bean也是结果之一)

本文由 mdnice 多平台发布

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值