文章目录
背景
Java的一大特性是封装,我们经常在实际开发中会封装工具类,甚至是慢慢积累自己的通用工具类,然后用于之后的项目开发。
我们在封装工具类的时候,常常会将工具方法设置为static
,方便我们使用类名.方法名
快速调用。但有些时候,工具方法封装为static
会出现问题,最常见的空指针异常(稍后举例)。所以我们不能那样封装。
如果使用普通方法,正常情况下我们想要去调用的话,就要先实例化类的对象,然后使用对象.方法名
去调用方法。这样的话会引发一些问题。
- 每次都要实例化对象,然后调用,在代码层面上不够简洁
- 如果方法使用的比较频繁,会实例化很多的对象,影响整个系统的效率
解决上述的问题,我们可能会想到单例模式来解决。手写单例模式是比较麻烦的,Spring的IOC容器管理对象,正是用到了单例模式的思想,可以将我们的对象交由Spring来管理。
源码
出自于我和小伙伴们搭建的开源框架:SMPE-ADMIN
参考地址:https://github.com/shiwei-Ren/smpe-admin/blob/main/smpe-common/src/main/java/marchsoft/utils/SpringContextHolder.java
Spring注入方式对比
Spring Bean 的注入方式一般分为三种:
- 构造器注入
- Setter注入
- 基于注解的 @Autowired 自动装配(Field 注入)
Spring官方推荐使用构造器注入。在IDEA中使用@Autowired
注入时会进行提示:
Field injection is not recommended.
Inspection info: Spring Team recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”
自动装配(Field 注入)
使用方法
该种注入方式是使用开发中最常见的注入方式。
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private IUserService userService;
}
优点
- 注入方式简单,只需要使用
@Autowired
注解或者@Resource
注解 - 整体代码简洁明了
- 新增依赖十分方便,不需要修改原有代码
缺点
- 容易出现空指针异常,Field 注入允许构建对象实例的时候依赖的示例对象为空。对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NullPointException的存在
- 对单元测试不友好,如果使用 Field 注入,那么进行单元测试就需要初始化整个Spring 环境,将所有 Bean 实例化
- 会出现循环依赖的隐患
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
- 容易破坏单一职责原则
构造器注入
在Spring4.x版本中推荐的注入方式就是这种
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
简单的翻译一下:这个构造器注入的方式啊,能够保证注入的组件不可变,并且确保需要的依赖不为空。此外,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态。
使用方法
@RestController
@RequestMapping("/api/users")
public class UserController {
private final IUserService userService;
private final IDeptService deptService;
private final IRoleService roleService;
private final UserCacheClean userCacheClean;
public UserController(IUserService userService, IDeptServic