问题描述
在Springboot中,对于MVC对应的组件如Service,Controller,Model等组件我们会习惯性的使用@Value,@Autowired等方法完成组件和变量值的自动绑定工作,但是在工具类中,如果我们是无法直接使用自动绑定的。
重点写在前面
Spring的依赖注入实际上是依赖于Set方法进行注入值的,Spring是基于对象层面的依赖注入,而静态属性/静态变量实际上是属于类的。
问题1:在静态工具类中自动绑定静态变量
问题代码
在配置文件application.properties中定义了如下变量 link.host=www.csdn.net
,在代码中尝试使用该自动绑定该变量。
程序代码:
public class TestUtil {
@Value("${link.host}")
private static String linkHost; // 想要将link.host的值自动绑定到linkHost中
public static JSONObject getAccessToken(String appid, String secret) {
String apiURL = linkHost + "/test/"; //这里希望直接使用linkHost的值
String responseStr = HttpUtil.sendGet(apiURL);
JSONObject json = JSONObject.fromObject(responseStr);
return json.getString("access_token");
return json;
}
}
问题说明
当在方法getAccessToken中尝试使用静态变量linkHost的值时,会发现该值为空。经过研究发现springboot不允许/不支持把值注入到静态变量中。
问题解决方法
springboot支持通过set方法实现注入,我们可以利用非静态set方法注入静态变量
修正后的代码
@Component // 需要添加Component注释才会被springboot管理
public class TestUtil {
@Value("${link.host}")
private static String linkHost;
// *核心:通过非静态set方法实现注入*
@Value("${link.host}")
public void setLinkHost(String linkHost) {
XunTongUtil.linkHost = linkHost;
}
public static JSONObject getAccessToken(String appid, String secret) {
String apiURL = linkHost + "/test/;
String responseStr = HttpUtil.sendGet(apiURL);
JSONObject json = JSONObject.fromObject(responseStr);
return json.getString("access_token");
return json;
}
}
问题2: 在静态工具类中注入Bean
问题代码
public class AccountManageClient {
@Autowired
private PcodeService pcodeService; // 尝试自动绑定Service
private static String getDepartmentPcode(String department) {
try {
List<PcodePO> pcodes = pcodeService.findAll(); // 尝试使用自动绑定的service
for (PcodePO pcode : pcodes) {
if (department.contains(pcode.getDepartment())) {
return pcode.getPcode();
}
}
} catch (Exception e) {
}
return "";
}
}
问题解决方法
要解决该问题,需要在保留原静态的属性(@Autowired)的同时,添加一个该类的静态属性。同时声明一个返回值为void的方法,在其中将非静态属性赋值给静态属性,该方法需要使用@PostConstruct注释
修正后的代码
@Component // 1. 需要添加Component注释才会被springboot管理
public class AccountManageClient {
public static AccountManageClient accountManageClient; // 2.添加一个该类的静态对象作为属性
@Autowired
private PcodeService pcodeService;
// 3. 使用@PostConstruct方法引导绑定
@PostConstruct
public void init() {
accountManageClient = this;
accountManageClient.pcodeService = this.pcodeService;
}
private static String getDepartmentPcode(String department) {
try {
List<PcodePO> pcodes = accountManageClient.pcodeService.findAll(); // 4. 使用时需要这样实现
for (PcodePO pcode : pcodes) {
if (department.contains(pcode.getDepartment())) {
return pcode.getPcode();
}
}
} catch (Exception e) {
}
return "";
}
}
重点总结
- 一定要对工具类添加@Component注释
- 要使用非静态方法对静态变量/对象进行手动绑定
引申阅读 - @PostConstruct
@PostConstruct
是Java EE5规范之后,Servlet新增的两个影响servlet声明周期的注解之一,另外一个是@PreConstruct
。这两个都可以用来修饰一个非静态的返回值为void的方法,并且该方法不能抛出异常。- 被
@PostConstruct
注解修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet中的init方法。被该注解修饰的方法会在构造器执行之后,init方法执行之前执行。Spring中允许开发者在受理的Bean中去使用它,当IOC容器被实例化管理当前bean时,被该注解修饰的方法会执行,完成一些初始化的工作。 - 被
@PreConstruct
注解修饰的方法会在服务器卸载Servlet的时候运行,类似于Servlet中的destroy方法。被该注解修饰的方法会在destroy方法执行之后,Servlet彻底卸载之前执行。