3

非常小的一个东西,Spring依赖注入Bean类型的8种情况 - 三友的java日记

 1 year ago
source link: https://www.cnblogs.com/zzyang/p/17261200.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

大家好,我是三友~~

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

为啥要讲这个呢?

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

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

微信公众号:三友的java日记

1、普通对象

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

@Autowired
private UserService userService;

2、Collection及其子接口

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

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

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

@Autowired
private List<MessageNotifier> messageNotifiers;

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

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

@Autowired
private MessageNotifier[] messageNotifiers;

代码如下:

4、Map

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

@Autowired
private Map<String, MessageNotifier> messageNotifierMap;

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

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

5、@Lazy

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

@Autowired
@Lazy
private MessageNotifier messageNotifier;

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

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

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

代码如下:

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

@Lazy的一个使用场景就是用来解决Spring无法处理的循环依赖场景,比如使用了@Async注解的循环依赖的场景,不了解的小伙伴可以看一下 @Async注解的坑,小心 这篇文章

6、Optional

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

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

@Autowired
private Optional<MessageNotifier> messageNotifier;

代码如下:

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

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

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

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

那Optional存在的作用是啥?

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

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

7、ObjectFactory和ObjectProvider

ObjectFactory和ObjectProvider是Spring提供的两接口

ObjectFactory

ObjectProvider继承了ObjectFactory

ObjectProvider

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

@Autowired
private ObjectFactory<MessageNotifier> messageNotifierObjectFactory;

@Autowired
private ObjectProvider<MessageNotifier> messageNotifierObjectProvider;

代码如下:

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

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

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

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

MessageNotifier messageNotifier = messageNotifierObjectFactory.getObject();

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注入都是一样的。

往期热门文章推荐

为什么RocketMQ会重复消费,我扒出了7种原因

如何去阅读源码,我总结了18条心法

如何实现延迟任务,我总结了11种方法

如何写出漂亮代码,我总结了45个小技巧

三万字盘点Spring/Boot的那些常用扩展点

两万字盘点那些被玩烂了的设计模式

扫码或者搜索关注公众号 三友的java日记 ,及时干货不错过,公众号致力于通过画图加上通俗易懂的语言讲解技术,让技术更加容易学习,回复 面试 即可获得一套面试真题。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK