开发工作中经常需要写一些Util工具类提供给其他地方使用,工具类中的方法一般都要求是static的,便于使用者用ClassName.methodName()直接调用。但是有时候发现有些方法不能使用static修饰,因为他们引用了bean,bean的加载顺序是迟于静态属性。这里简单记录下把工具类中的非静态方法改造为静态方法;
如下是最近写的一个工具类,可以看到工具类提供的getNewAccessToken()方法为非静态方法,如果加了static会报错,因为这里使用到了redisTemplateInstance实例bean了,静态方法中不能引用非静态属性。
@Slf4j
@Component
@RefreshScope
public class XXXXTokenUtil {
@Value("${XXX.grant.clientId}")
private String clientIdInstance;
@Value("${XXX.grant.clientSecret}")
private String clientSecretInstance;
@Value("${XXX.grant.grantType}")
private String grantTypeInstance;
@Value("${XXX.grant.username}")
private String usernameInstance;
@Value("${XXX.grant.password}")
private String passwordInstance;
@Value("${XXX.domain}")
private String domainInstance;
private static final String CACHED_TOKEN_KEY = "XXX_TOKEN";
@Autowired
private RedisTemplate<String, String> redisTemplateInstance;
public String getAccessToken() {
String cache = redisTemplate.opsForValue().get(CACHED_TOKEN_KEY );
if (StringUtils.isNotEmpty(cache)) {
return cache;
}
XXX newAccessToken = getNewAccessToken();
redisTemplate.opsForValue().set(CACHED_TOKEN_KEY, newAccessToken.getAccessToken(), newAccessToken.getExpiresIn() - 300, TimeUnit.MILLISECONDS);
return newAccessToken.getAccessToken();
}
private XXXXToken getNewAccessToken() {
String response = HttpUtil.createPost(domain + "/auth/oauth/token")
.form("client_id", clientId)
.form("client_secret", clientSecret)
.form("username", username)
.form("password", password)
.form("grant_type", grantType)
.timeout(1000 * 10)
.execute()
.body();
if (StringUtils.isEmpty(response)) {
log.error("get access token from xxxis empty.");
throw new BusinessException("获取访问token结果为空");
}
return JacksonUtil.convertToObject(response, XXX.class);
}
}
改造为静态方法:
这里使用延迟的思想,工具类静态方法中使用的属性全部改造为静态属性,只是属性值为空,在bean加载最后阶段,借助Spring提供的扩展机制延迟赋值,这个延迟是无需担心的,他是在bean初始化完成后执行的,这个时间点是早于bean可用时间点的。这里使用的是InitializingBean完成延迟赋值的,也可以借助其他的方法,如@PostConstruct注解,Aware相关接口,PostProcessor接口等。
需要注意的是如下工具类注入的nacos配置属性,只会在首次加载时读取,后续修改是不会动态变化的。如果静态配置也想一起动态变化,可能需要去监听nacos的通知事件额外处理,这里暂未研究过。
@Slf4j
@Component
// @RefreshScope
public class XXXXTokenUtil implements InitializingBean {
private static String clientId;
private static String clientSecret;
private static String grantType;
private static String username;
private static String password;
private static String domain;
private static RedisTemplate<String, String> redisTemplate;
@Value("${XXX.grant.clientId}")
private String clientIdInstance;
@Value("${XXX.grant.clientSecret}")
private String clientSecretInstance;
@Value("${XXX.grant.grantType}")
private String grantTypeInstance;
@Value("${XXX.grant.username}")
private String usernameInstance;
@Value("${XXX.grant.password}")
private String passwordInstance;
@Value("${XXX.domain}")
private String domainInstance;
private static final String CACHED_TOKEN_KEY = "XXXX_TOKEN";
@Autowired
private RedisTemplate<String, String> redisTemplateInstance;
@Override
public void afterPropertiesSet() {
clientId = clientIdInstance;
clientSecret = clientSecretInstance;
grantType = grantTypeInstance;
username = usernameInstance;
password = passwordInstance;
domain = domainInstance;
redisTemplate = redisTemplateInstance;
}
public static String getAccessToken() {
String cache = redisTemplate.opsForValue().get(CACHED_TOKEN_KEY);
if (StringUtils.isNotEmpty(cache)) {
return cache;
}
XXXXX newAccessToken = getNewAccessToken();
redisTemplate.opsForValue().set(CACHED_TOKEN_KEY, newAccessToken.getAccessToken(), newAccessToken.getExpiresIn() - 300, TimeUnit.MILLISECONDS);
return newAccessToken.getAccessToken();
}
private static XXXX getNewAccessToken() {
String response = HttpUtil.createPost(domain + "/auth/oauth/token")
.form("client_id", clientId)
.form("client_secret", clientSecret)
.form("username", username)
.form("password", password)
.form("grant_type", grantType)
.timeout(1000 * 10)
.execute()
.body();
if (StringUtils.isEmpty(response)) {
log.error("get access token from XXX is empty.");
throw new BusinessException("获取访问token结果为空");
}
return JacksonUtil.convertToObject(response, XXXXX.class);
}
}