前情提要
众所周知,没有Util
工具类的Java
程序是不完整的
这天,我们打开了一个Springboot
项目,并打算加入一个通用方法(用来拼接静态资源请求路径),那就写一个Util
类吧
GPT-4: 在Java中,一个典型的工具(Util)类通常包含一些静态方法,这些方法用于提供通用的、非特定于对象的功能。这些类通常被设计成不可实例化和不可继承的
typescript
public final class Util {
@Value("${staticServer}")
private static String staticServer; //静态资源服务器地址
// 私有构造函数,防止实例化
private MyUtils() {
throw new AssertionError("No MyUtils instances for you!");
}
//拼接完整静态资源URL,如 https://static.server/images/cat.jpg
public static String getStaticResourceURI(String prefix, String filename) {
return staticServer + prefix + '/' + encodeUri(filename);
}
public static String encodeUri(String uri) {
return UriUtils.encode(uri, StandardCharsets.UTF_8);
}
优雅,太优雅了
编译!
运行!
Bomb!
好的,完犊子了,你猜这怎么着,staticServer == null
可怜 弱小 又无助
这不核理,为什么会这样呢,对静态成员的注入从技术上应该是可以实现的
不过静态成员并不属于实例对象,可能与IOC
容器 & 依赖注入的理念不符,所以Springboot
并不直接支持
辣怎办
但是这个需求很合理好吧,在工具类中用到配置文件中的键值
Pre
不过丑话说在前头,如果要想进行依赖注入,那么Util
类必须是一个Bean
,因为只有Bean
才能被IOC
容器管理
所以,Util
的构造函数不能是私有的,还真就得开放给Springboot
使用
总不能又要隐私,又要躺着把钱挣了吧(哪有这好事)
所以:
kotlin
@Component //注册为Bean
public final class Util {
//private MyUtils() { 公开啦!哭
// throw new AssertionError("No MyUtils instances for you!");
//}
}
1. 利用set方法注入
typescript
@Component
public final class Util {
private static String staticServer; //静态资源服务器地址
@Value("${staticServer}")
public void setStaticServer(String staticServer) { //
Util.staticServer = staticServer;
}
public static String getStaticResourceURI(String prefix, String filename) {
return staticServer + prefix + '/' + encodeUri(filename);
}
...
}
把@Value
写在非静态成员方法上,就可以正常被执行
然后在函数体里对static
成员赋值,狸猫换太子
2. 利用@PostConstruct注解
typescript
@Component
public final class Util {
@Value("${staticServer}")
private String staticServer;
private static Util INS;
@PostConstruct // 在构造函数和依赖注入完成后执行;构造方法 > @Autowired > @PostConstruct,毕竟构造完成才能注入成员
public void init() {
INS = this; // 获取Bean实例,赋值给静态变量,方便static函数访问
} // 容器管理的Bean默认是单例的,所以这里的INS是唯一的
// 拼接完整静态资源URL,只对filename进行编码
public static String getStaticResourceURI(String prefix, String filename) {
return INS.staticServer + prefix + '/' + encodeUri(filename);
}
// 对uri进行编码,注意'/'也会被编码
public static String encodeUri(String uri) {
return UriUtils.encode(uri, StandardCharsets.UTF_8); //URLEncoder会把' '编码为'+'而非'%20'造成错误
}
}
看到这个名字(@PostConstruct
),不知道大家有没有想起什么
二叉树的后序遍历:PostOrder
诶,为什么都是Post
,马萨卡
对喽,PostConstruct
是在构造函数之后执行的,核理
由于执行顺序是:构造方法 > @Autowired > @PostConstruct
所以,执行@PostConstruct
的时候,staticSever
已经注入完成,不用担心,一切就绪
我们只要把IOC
容器中的Util
实例的this
指针赋值给static
变量INS
即可(指针的赋值啊,问题不大)
什么,你问我哪来的
this
?你加了@Component注册为了Bean,IOC容器默认会构造单例对象的
什么,你问我Util
开放了构造函数有多个实例怎么办?@Component默认是单例的,问题不大,只要你不要自己构造一个出来
什么,你问我要是同事构造了Util
怎么办?砍了ta
问题不大,本来也没存什么状态
3. @ConfigurationProperties
typescript
@Component
@ConfigurationProperties
public final class Util {
private static String staticServer;
public void setStaticServer(String staticServer) {
Util.staticServer = staticServer;
}
}
这个其实本质上也是利用set
方法进行注入的,所以并不违反规则
好的好的,下班
Peace
Ref
springboot注入静态变量的两种方式_springboot 注入静态变量-CSDN博客