今天依要求做一个数据字典的模块,其中要做的一个业务是更新数据字典后删除对应的缓存,这里要求使用类方法。一开始我是这么写的。(改动的是refreshCacheByEntryId() )
错误写法1:类方法里含有没有static修饰的对象
@Component
@Log4j2
public class DictUtils {
private static DictUtils dictUtils;
@Autowired
private DictEntryService dictEntryService;
@Value("${spring.profiles.active}")
private String env;
public static void initMap(){
bitMap = MapUtil.newHashMap();
log.error("缓存Map清除完毕");
}
// 刷单个redis的dict
public static void refreshCacheByEntryId(@NotNull String entryId){
boolean b = false;
// 这里也会爆红
if( this.env.equals("dev") ){
// 此处会在这里报红
DictEntry entry = dictEntryService.getByEntityId(entryId);
...
}
}
这种方式在DictEntry entry = dictEntryService.getByEntityId(entryId);这一行就开始报红了,也就是说不能后在类方法里访问本类的属性
错误写法2:没有用static修饰方法
然后试了下吧static给去掉
@Component
@Log4j2
public class DictUtils {
private static DictUtils dictUtils;
@Autowired
private DictEntryService dictEntryService;
// 加上了static
@Value("${spring.profiles.active}")
private static String env;
public static void initMap(){
bitMap = MapUtil.newHashMap();
log.error("缓存Map清除完毕");
}
// 刷单个redis的dict
public void refreshCacheByEntryId(@NotNull String entryId){
boolean b = false;
if( DictUtils.env.equals("dev") ){
DictEntry entry = dictEntryService.getByEntityId(entryId);
...
}
}
这样是不报红了,但是如果在另外一方直接引入的话,比如下方
@RestController("dictEntryController")
@RequestMapping("/api/v1/dict/dictEntry")
public class DictEntryController extends BaseController {
@Autowired
private DictEntryService dictEntryService;
@PutMapping(value = "{id}")
@ApiOperation(value = "更新对象记s录", notes = "更新对象记录", produces = "application/json")
public R update(@RequestBody DictEntry entity) {
R<DictEntry> entryR = dictEntryService.update(entity);
// 清空缓存
if(entryR.isSuccess()){
// 此处在方法执行的时候会报红
DictUtils.refreshCacheByEntryId(entity.getId());
System.out.println("111");
}
return entryR;
}
}
会发现执行到 DictUtils.refreshCacheByEntryId(entity.getId());爆红了,因为不能不能直接使用对象方法,只能用new来使用对象方法,那就和一开始我们要的使用类方法来实现不符了
错误写法3:直接同时用static修饰方法,用static修饰依赖
那既然这样,把方法和依赖同时用static来修饰不就可以了么?此处做法会发现一个很大的问题。先黏代码
@Component
@Log4j2
public class DictUtils {
private static DictUtils dictUtils;
// 用static修饰
@Autowired
private static DictEntryService dictEntryService;
// 用static修饰
@Value("${spring.profiles.active}")
private static String env2;
public static void initMap(){
...
}
// 用static修饰方法
public static void refreshCacheByEntryId(@NotNull String entryId){
boolean b = false;
// 用DictUtils.env获取
if( DictUtils.env.equals("dev") ){
// 此处不会爆红,但是会引发注入问题,下文说
DictEntry entry = dictEntryService.getByEntityId(entryId);
...
}
}
然后另一方的调用
@RestController("dictEntryController")
@RequestMapping("/api/v1/dict/dictEntry")
public class DictEntryController extends BaseController {
@Autowired
private DictEntryService dictEntryService;
@PutMapping(value = "{id}")
@ApiOperation(value = "更新对象记s录", notes = "更新对象记录", produces = "application/json")
public R update(@RequestBody DictEntry entity) {
R<DictEntry> entryR = dictEntryService.update(entity);
if(entryR.isSuccess()){
// 此处不报红了
DictUtils.refreshCacheByEntryId(entity.getId());
}
return entryR;
}
}
然后接下来发现运行的时候,报错了,报了个null指针,在哪里报错了?
而且发现env也为null ,@Value注解获取配置文件的值env失败了
// 就是这一行,env为null
if( DictUtils.env.equals("dev") ){
// 这一行也是,此时dictEntryService为null
DictUtils.refreshCacheByEntryId(entity.getId());
}
DictEntry entry = dictEntryService.getByEntityId(entryId);
...
奇怪,那dictEntryService不是已经写了@Autowired了么?为什么为null?是注入失败了么?
答案是是的
那该如何解决这个问题 ?
解决问题
答案是如果想要既实用类方法,又要类方法里面的依赖成功注入,那么要这么做
@Component
@Log4j2
public class DictUtils {
// 此处要加static
private static DictUtils dictUtils;
// 此处不用加static
@Autowired
private DictEntryService dictEntryService;
// 此处不用加static
@Value("${spring.profiles.active}")
private String env;
private static Map<String,List<DictEntry>> bitMap;
// 进行初始化操作赋值
@PostConstruct
public void init() {
dictUtils = this;
dictUtils.dictEntryService = this.dictEntryService;
dictUtils.env = this.env;
bitMap = MapUtil.newHashMap();
}
public static void initMap(){
...
}
public static void refreshCacheByEntryId(@NotNull String entryId){
boolean b = false;
// 此处改成dictUtils
if( dictUtils.env.equals("dev") ){
// 此处改成dictUtils
DictEntry entry = dictUtils.dictEntryService.getByEntityId(entryId);
...
}
}
此时是可行的,为什么呢?
因为在@PostConstruct注解中,他的执行顺序是在@Autowired之后的,而@Autowired是在装载类之后的
即顺序:装载类——Autowired——PostConstruct
此时dictUtils = this;此时的this才是已经被装配的(类上有 @Component 注解),然后之后使用这种方式就能够完美获得注入的依赖,而且@Value注解也生效了
实现了在spring里使用类方法来实现业务,并且同时能在类方法里使用已经注入的依赖