通常,我们在写工具类时,会将其所属方法,属性规定为 static 类型的,方便调用,
如下:
// 调用
FileUtil.getFiles();
public class FileUtil {
private static String FILE_PATH = "D:\\files";
public static String getFiles() {
return FILE_PATH;
}
}
通过 FileUtil.getFiles(); 可以直接调用静态方法,不必获取 FileUtil 对象实例,或使用@Autowired自动注入。
但是,当工具类本身需要使用spring注入对象实例或者需要从配置文件获取属性值时,这时候
在使用静态方法往往就会存在一些限制。
如下:
使用spring注入对象实例
// 调用会报空指针错误
FileUtil.getFiles();
@Component
public class FileUtil {
private static String FILE_PATH = "D:\\files";
@Autowired
private static SysFileService service;
public static String getFiles() {
return service.download(FILE_PATH);
}
}
从配置文件获取属性值
// 调用结果为null
FileUtil.getFiles();
// 以下两种写法均无法正常从配置文件获取值
@Component
public class FileUtil {
@Value("${files.filePath}")
private static String FILE_PATH;
public static String getFiles() {
return FILE_PATH;
}
}
// 或
@Component
@ConfigurationProperties(prefix = "files")
public class FileUtil {
private static String FILE_PATH;
public static String getFiles() {
return FILE_PATH;
}
}
上述代码,无法正常获取值,这是因为 FILE_PATH 变量不能是静态变量(因为@Value和@ConfigurationProperties注解不能赋值到静态变量上,虽然也可以通过一些方法成功赋值静态变量,但这里不做详述), 如果把 static 修饰词去掉,则可以正常获取,但这样一来,由于 FILE_PATH 不是静态变量,也就无法在静态方法中使用了!
以上,是存在的问题,接下来,讲讲如何优雅的来解决问题。
使用spring注入对象实例
// 调用正常
FileUtil.getFiles();
@Component
public class FileUtil {
private static String FILE_PATH = "D:\\files";
private static FileUtil instance;
@Autowired
private SysFileService service;
@PostConstruct
public void init() {
instance = this;
}
public static String getFiles() {
return instance.service.download(FILE_PATH);
}
}
使用了@PostConstruct来完成对应bean的获取。
原理:@PostConstruct注解被用来修饰一个非静态void方法。该注解方法在整个Bean初始化中的执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
由此,在自动注入后,instance 对象被赋值为 this,即成功获得一个自动注入后的一个 FileUtil 类对象(手动赋值 instance),所以,getFiles方法中使用 instance 对象时就不会是一个 null 值。
从配置文件获取属性值
// 调用正常
FileUtil.getFiles();
public class FileUtils {
private static final String FILE_PATH;
static {
File file = File.getInstance();
FILE_PATH = file.FILE_PATH;
}
public static String getFiles() {
return FILE_PATH;
}
@Component
@ConfigurationProperties("files")
public static class File {
private String FILE_PATH;
public static File getInstance() {
return ApplicationContextUtil.getBean(File.class);
}
}
}
这里使用了上下文和静态代码块来完成对应bean的获取。
简单讲下原理:上下文可以让我们自己获取需要的bean实例,静态代码块会在类初始化的时候执行且仅执行一次(静态代码块是最优先被执行的,在类构造器执行之前)。
先使用 File 静态内部类(可以简单看作一个普通类)来正常获取配置文件的值! 其中 getInstance方法的 ApplicationContextUtil.getBean 方法实现了 ApplicationContextAware 接口,用来获取 File bean 本身,然后在 FileUtil 类的静态代码块中调用 File.getInstance() 获取实例。就可以成功赋值 FILE_PATH 了。
后记:
推荐使用该方法!
构造器注入
// 调用正常
FileUtil.getFiles();
@Component
public class FileUtil {
private static String FILE_PATH = "D:\\files";
private static SysFileService service;
public FileUtilss(SysFileService service) {
FileUtil.service = service;
}
public static String getFiles() {
return service.download(FILE_PATH);
}
}
以上,我们解决了如何在工具类中优雅的使用Spring(依赖注入)获取bean或属性,并且可以使用静态方法来实现具体的功能!