背景
在最近的工作室项目中用到了Jwt来替代Session实现授权与鉴权。为了实现代码和Jwt的参数的解耦,将Jwt的有关参数抽离到全局配置文件application.yml中。但是又怎么样把配置文件中的参数给读到Jwt的工具类中呢?
组成
- JwtUtil.java - 将Jwt的功能实现封装成一个工具类
- JwtProperties.java - 与配置文件的映射
- application.yml - SpringBoot应用全局配置文件
实现
1. 映射
首先,声明一个Bean作与配置文件的映射。这个Bean我们就姑且称作Properties类,也就是上述所说的JwtProperties.java。其中,这个Bean需要拥有与配置文件“一样”的字段。为什么要加双引号?
其实读取配置文件有两种方式:一种是@ConfigurationProperties
,另外一种是@Value
。他们的区别如下:
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
我们将使用@ConfigurationProperties
这个注解来实现配置文件的映射。在这里我们就不详细说明两个注解区别,只需要知道@ConfigurationProperties
是支持松散绑定的,即松散语法。
什么是松散语法?概念就不给出了,直接给出例子,一看就懂。
松散语法:privatekey = privateKey = private_key = private-key
而我们一般采用的是第四种方式,即通过-
连接多个单词;而在Java变量的命名中,采用的是第二种,即驼峰法。
再者,需要注意的是:如果要使用@ConfigurationProperties
注解,还需要使用@Component
注解配套使用,把当前Propertities类交给IoC容器管理。
然后再生成getter和setter方法即可。
最终代码如下:
@Component
@ConfigurationProperties(prefix = "jwt") //prefix是yml中的前缀
public class JwtProperties {
private String privateSecret;
private int expiresSecond;
private int refreshSecond;
// 省略getter和setter
}
2. 映射结果交给工具类
首先,想一个问题先,工具类怎么使用配置文件中的参数?既然Propeties类是配置文件的映射结果,那么Properties类就替代了配置文件的地位。在工具类中要读取到配置就要获取到Properties类的成员变量。
那么问题来了,该怎么注入这个Properties类?
很明显地,工具类的方法大部分都是static修饰的方法,也就是说不需要实例化工具类的对象,直接通过类名来调用方法。然而如果是静态方法的话,也就只能调用静态成员,这时候就需要声明一个静态的jwtProperties变量,然后紧接着使用@AutoWired
自动注入,代码如下:
@AutoWired
private static JwtProperties jwtProperties;
这一切好像都很平静,没得任何问题。
但是结果是无法注入的,因为Spring的DI不支持DI目标是static的,只能是实例变量。而实例变量是可以给静态变量赋值的,这时候就需要再生成一个实例变量,提供JDK提供的@PostConstruct
注解实现实例变量给静态变量赋值,代码如下:
@Autowired
private JwtProperties jwtProperties; //依赖注入不支持给static的变量注入
private static JwtProperties staticJwtProperties;
@PostConstruct
public void init(){
staticJwtProperties = this.jwtProperties;
}
在这里简单介绍一下@PostConstuct
注解。
注解作用:在构造方法被调用后执行被注解的方法。在我们现在这个场景中,也就是说当前类被实例化后(通过IoC),借助实例变量给静态变量赋值。
Bean初始化执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
嗯,好的。这样好像解决了,能实现功能达到我的目的了,但是我看着很不爽!!!无端端又多了一个变量。
然后我就又开始查资料了,结果发现一个目前为止比较好的解决方案。
首先,把原来的成员变量转移到方法的形参中,通过形参给静态变量赋值。这时候@AutoWired
的另外一个作用就上场了,@AutoWired
不仅支持作用于变量中,还支持作用在方法上,此时的作用是给方法的形参进行DI。
最终代码如下:
private static JwtProperties JwtProperties;
/**
* spring容器会在类加载后自动注入这个方法的参数,并执行一遍方法。
* 这就像static{}块语句会初始化执行,但顺序不一样。static先执行
*/
@Autowired
public void init(JwtProperties jwtProperties){
JwtUtil.JwtProperties = jwtProperties;
}
到此为止就大功告成了,很清爽有没有!