写点什么

非常小的一个东西,Spring 依赖注入 Bean 类型的 8 种情况

作者:小小怪下士
  • 2023-03-27
    湖南
  • 本文字数:2859 字

    阅读完需:约 9 分钟

今天来讲一个可能看似没有用但是实际又有点用的一个小东西,那就是 @Autowired 支持注入哪些 Bean 的类型。


为啥要讲这个呢?


故事说起来可能就比较长了。


不过长话可以短说,仅仅就是突然想起来之前有一个妹子问过我这个问题!


1、普通对象

这没什么好说的,大家都这么用的,比如需要用到 UserService,直接 @Autowired 就可以了。


@Autowiredprivate UserService userService;
复制代码

2、Collection 及其子接口

除了支持注入一个单一的对象之外,@Autowired 还支持注入一个 Collection 对象。


比如说,现在有个消息通知的接口MessageNotifier


这种接口一般都会有不同的实现,比如说通过邮件通知,或者 app,短信等等,所以就有多种实现,此时如果需要注入MessageNotifier,就可以使用注入 Collection 的方式,比如


@Autowiredprivate List<MessageNotifier> messageNotifiers;
复制代码


不过这种方式有个规定,那就是注入的类型必须是 Collection 及其子接口,如果你直接注入一个ArrayList,那么此时是不支持的。


3、数组

同理,@Autowired 可实现了注入一个数组的功能。


@Autowiredprivate MessageNotifier[] messageNotifiers;
复制代码


代码如下:


4、Map

同样的,@Autowired 还可以注入一个 Map。


@Autowiredprivate Map<String, MessageNotifier> messageNotifierMap;
复制代码


此时注入的 map,key 的类型就是 bean 的名称,这种方式可以配合策略模式使用。


不过,这种方式只支持注入的是 Map 接口,不支持子类型接口,代码如下。


5、@Lazy

当一个注入的字段加了 @Lazy 注解之后,那么此时就代表这个字段是延迟注入。


@Autowired@Lazyprivate MessageNotifier messageNotifier;
复制代码


延迟注入并不是不注入,而是注入目标对象类型的代理对象,真正的目标是当需要用到的时候在创建。



如图所示,当注入的MessageNotifier时加了 @Lazy 注解,那么此时注入的其实是MessageNotifier的代理对象,而真正的MessageNotifier对象并没有创建,图中代理对象我称为MessageNotifierProxy


由于注入的是对象是代理对象MessageNotifierProxy,那么真正被使用的就是MessageNotifierProxy,一旦调用了MessageNotifierProxy的方法,此时MessageNotifierProxy会去 Spring 容器中查找真正的MessageNotifier对象,然后再调用MessageNotifier对象的方法。


代码如下:



这就是 @Lazy 延迟注入的原理。并不是不注入,而是注入一个代理对象,可以理解为一个占位符,一个空壳子,先占着位置,等用到这个壳子的时候,这个壳子会去查找到真正的对象,调用真正对象的方法。


@Lazy 的一个使用场景就是用来解决 Spring 无法处理的循环依赖场景,比如使用了 @Async 注解的循环依赖的场景

6、Optional

Optional 是 JDK1.8 提供的一个 api,可以优雅的解决判空的问题。


@Autowired 也支持了注入 Optional 类型。


@Autowiredprivate Optional<MessageNotifier> messageNotifier;
复制代码


代码如下:



注入 Optional 这种方式可以解决注入的对象不存在的导致异常问题,也就是安全注入。


比如说,MessageNotifier这个对象 Spring 容器中并没有,如果直接注入,此时会抛NoSuchBeanDefinitionException异常



而直接通过注入 Optional 的方式就可以解决这个问题。


除了通过 Optional 的方式之外,也可以直接把 @Autowired 的required的属性设置为 false 来解决注入对象不存在的问题。


那 Optional 存在的作用是啥?


其实 Optional 的作用仅仅是不用写为空的判断,这也是 Optional 这个类的作用作用,除了这个,跟直接 @Autowired 对象并没有其它区别。


注入 Optional 这种方式其实用的不多,在我的映像中,我在源码中几乎没有看见这种注入方式。

7、ObjectFactory 和 ObjectProvider

ObjectFactory 和 ObjectProvider 是 Spring 提供的两接口



ObjectProvider 继承了 ObjectFactory



@Autowired 也可以直接注入这两个接口。


@Autowiredprivate ObjectFactory<MessageNotifier> messageNotifierObjectFactory;
@Autowiredprivate ObjectProvider<MessageNotifier> messageNotifierObjectProvider;
复制代码


代码如下:



从这段代码也可以看出,最终注入的其实是DependencyObjectProvider实现。


ObjectFactory 也是用来做延迟注入的操作,跟 @Lazy 作用差不多,但是实现原理不一样。


用上面的例子来说,注入 ObjectFactory 的时候并有创建 MessageNotifier 对象。


当需要使用 MessageNotifier 的时候需要通过 ObjectFactory 的 getObject 方法获取,此时才会真正创建 MessageNotifier 对象。


MessageNotifier messageNotifier = messageNotifierObjectFactory.getObject();
复制代码


所以 @Async 注解导致的循环依赖异常不仅可以通过 @Lazy 注解解决,也可以通过注入 ObjectFactory 的方式解决。


同理,ObjectProvider 也有延迟加载的功能,但是除了延迟加载之外,ObjectProvider 额外提供了跟 Optional 安全注入的功能,这个功能 ObjectFactory 是没有的。


上面的例子中,当使用 ObjectFactory 的 getObject 方法时,如果 Spring 容器中不存在 MessageNotifier 对象,此时也会抛NoSuchBeanDefinitionException异常。


但是 ObjectProvider 额外提供的 getIfAvailable 方法就支持获取不存在的对象的功能,当通过 getIfAvailable 获取的对象不存在时,只会返回 null,并不会出抛异常。



ObjectFactory 和 ObjectProvider 在框架内部中使用的还是比较多的。


就比如说,在 MybatisPlus 自动装配的时候就大量使用 ObjectProvider



并且泛型类型就是数组或者是集合,跟前面说的都对应上了。


通过这种方式就可以安全的注入,当 Spring 容器有这些对象的时候 MybatisPlus 就使用这些,没有也不会报错。

8、JSR-330 Provider

首先,来讲一下什么是 JSR-330。


JSR 是 Java Specification Requests 的缩写,是一种 Java 标准规范。


而 330 算是一个版本,除了 330,听到的比较多的还有 250。


这个规范定义了一些 IOC 的注解,我们熟知的比如 @Resource、@PostConstruct、@PreDestroy 注解都是 JSR-250 中提出的。


一些 IOC 的框架会基于这个标准来实现这些接口的功能,比如 Spring、Dagger2 等 IOC 框架都实现了这些注解的功能。


所以,如果你不使用 Spring 框架,使用其它的 IOC 框架,那么 @Resource、@PostConstruct、@PreDestroy 注解都是可以生效的。


在 JSR-330 中,提出了javax.inject.Provider这个接口



不过,想使用 JSR-330 这个接口,需要引入依赖


<dependency>    <groupId>javax.inject</groupId>    <artifactId>javax.inject</artifactId>    <version>1</version></dependency>
复制代码


Spring 也支持注入这个类型的接口



这个接口的功能跟前面提到的 ObjectFactory 功能是一样的,也支持延迟注入的功能。

总结

到这 Spring 能够注入的 Bean 的 8 种类型就讲完了,其实这 8 种类型可以分为以下几种功能:


  • 单一注入,就是注入一个单一的对象

  • 集合注入,可以注入数组或者集合

  • 延迟注入,比如 @Lazy、ObjectFactory、ObjectProvider、JSR-330 Provider

  • 安全注入,不存在不会抛异常,比如 Optional、ObjectProvider


这几种方式并不是互斥的,比如说延迟注入也可以注入的是一个集合,前面举的 MyBaisPlus 自动装配时 ObjectProvider 的使用就是很好的例子。


同时虽然本文举例的是 @Autowird 注解和字段注入的方式,但上面提到的注入的 Bean 类型跟使用注解和注入方式没什么关系,@Resource 注解,构造器注入,setter 注入都是一样的。

用户头像

还未添加个人签名 2022-09-04 加入

热衷于分享java技术,一起交流学习,探讨技术。 需要Java相关资料的可以+v:xiaoyanya_1

评论

发布
暂无评论
非常小的一个东西,Spring依赖注入Bean类型的8种情况_Java_小小怪下士_InfoQ写作社区