共计 3863 个字符,预计需要花费 10 分钟才能阅读完成。
明天来讲一个可能看似没有用然而理论又有点用的一个小东西,那就是 @Autowired 反对注入哪些 Bean 的类型。
为啥要讲这个呢?
故事说起来可能就比拟长了。
不过长话能够短说,仅仅就是忽然想起来之前有一个妹子问过我这个问题!
1、一般对象
这没什么好说的,大家都这么用的,比方须要用到 UserService,间接 @Autowired 就能够了。
@Autowired
private UserService userService;
2、Collection 及其子接口
除了反对注入一个繁多的对象之外,@Autowired 还反对注入一个 Collection 对象。
比如说,当初有个音讯告诉的接口MessageNotifier
。
这种接口个别都会有不同的实现,比如说通过邮件告诉,或者 app,短信等等,所以就有多种实现,此时如果须要注入MessageNotifier
,就能够应用注入 Collection 的形式,比方
@Autowired
private List<MessageNotifier> messageNotifiers;
不过这种形式有个规定,那就是注入的类型必须是 Collection 及其子接口,如果你间接注入一个ArrayList
,那么此时是不反对的。
3、数组
同理,@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 注解的循环依赖的场景
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 提供的两接口
ObjectProvider 继承了 ObjectFactory
@Autowired 也能够间接注入这两个接口。
@Autowired
private ObjectFactory<MessageNotifier> messageNotifierObjectFactory;
@Autowired
private 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 注入都是一样的。