access_token是公众号的全局唯一接口调用凭据,
公众号调用各接口时都需使用access_token,access_token 有效期2小时,过期后需要重新获取 , 两次获取access_token中间有5分钟的顺延时间。开发者需要进行妥善保存。
access_token的存储至少要保留512个字符空间。
access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
access_token每日调用上限次数是2000次/日。
问题:
每隔两个小时获取一次sccess_token;
解决方案:
-
SpringTask定时任务
-
存在redis中,设置redis过期时间,过期后重新获取(完整配置redis)
-
线程
-
存在mysql中,但是考虑访问次数与效率,这里放弃
-
IO流存在文件中,频繁读写,放弃
这是获取access_token的公共方法,在每个方法都会使用
public class AccessTokenCommon {
public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
public final static String APPID = "APPID";
public final static String APPSECRET = "APPSECRET值";
public static String ACCESS_TOKEN=null;
}
第一种方式:SpringTask定时任务
每两个小时获取一次access_token
需要判断ACCESS_TOKEN是否为null,第一次获取的静态变量ACCESS_TOKEN是null,就手动调用getAccessToken方法获取access_token
@Scheduled(cron = "* * 0/2 * * ?")//定时器Quartz,表示两个小时获取执行一次这个方法,这个是关键(启动类上还要有个注解@EnableScheduling)
public void getAccessToken() {
// System.out.println(1);
//替换变量appid和APPSECRET
String requestUrl = AccessTokenCommon.ACCESS_TOKEN_URL.replace("APPID", AccessTokenCommon.APPID).replace("APPSECRET", AccessTokenCommon.APPSECRET);
//使用微信工具类发送GET请求,这里"GET"要大写 ,获取到JSONObject类型
JsonObject jsonObject = WeixinUtil.httpRequest(requestUrl, "GET", null);
//获取accexx_token
AccessTokenCommon.ACCESS_TOKEN = jsonObject.getString("access_token");
System.out.println(AccessTokenCommon.ACCESS_TOKEN );
}
第二种方式:存在redis中
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String,String> redisTemplate;
JSONObject jsonObject = null;
/**
* @Description :
* @author : wc
* @param : access_token的获取第二种方式
* 获取access_token,如果redis里有,就直接返回
* 没有就调用getAccessToken获取,再存储到redis中,设置过期时间2小时
* @CreateDate : 2020/8/6 17:20
* @exception :
* @return :
*/
public String getAccess_token(){
//查看access_token是否在redis中是否存在
AccessTokenCommon.ACCESS_TOKEN = redisTemplate.opsForValue().get("access_token_redis");
//存在,返回access_token;
if (AccessTokenCommon.ACCESS_TOKEN !=null){
return AccessTokenCommon.ACCESS_TOKEN ;
}else {// 不存在重新调用获取access_token方法,获取,再存入redis中
AccessTokenCommon.ACCESS_TOKEN = getAccessToken();
redisTemplate.opsForValue().set("access_token_redis",AccessTokenCommon.ACCESS_TOKEN ,2, TimeUnit.HOURS);
return AccessTokenCommon.ACCESS_TOKEN ;
}
}
//获取access_tokne方法
public String getAccessToken() {
String requestUrl = AccessTokenCommon.ACCESS_TOKEN_URL.replace("APPID", AccessTokenCommon.APPID).replace("APPSECRET", AccessTokenCommon.APPSECRET);
jsonObject = WeixinUtil.httpRequest(requestUrl, "GET", null);
AccessTokenCommon.ACCESS_TOKEN = jsonObject.getString("access_token");
System.out.println(AccessTokenCommon.ACCESS_TOKEN );
return AccessTokenCommon.ACCESS_TOKEN ;
}
}
添加redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
application.yml文件配置
spring:
redis:
host: 192.168.192.131 #这是单个redis 虚拟机中运行
port: 6379 #端口号
password: 1234 #密码
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
shutdown-timeout: 100
RedisConfig.java 工具类
package com.yfl.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
/**
* @ClassName RedisConfig
* @Description TODO
* @Author guoweixin
* @Version 1.0
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
/**
* 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容) 通过Spring 的依赖注入特性进行自定义的配置注入并且此类是一个配置类可以更多程度的自定义配置
*
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 缓存配置管理器
*/
@Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) {
//以锁写入的方式创建RedisCacheWriter对象
RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
//创建默认缓存配置对象
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheManager cacheManager = new RedisCacheManager(writer, config);
return cacheManager;
}
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
RedisTemplate<String,Object> template = new RedisTemplate <>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式。
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
第三种方式:线程
AccessTokenThreadUtil 循环获取access_token,每次调用都会休眠2小时;
AccessTokenListener监听器用于启动AccessTokenThreadUtil线程
/**
*继承线程
*/
public class AccessTokenThreadUtil extends Thread {
JSONObject jsonObject = null;
String access_token = null;
@Override
public void run() {
while (true){
this.getAccessToken();//调用方法获取access_token
try {
Thread.sleep(7200000);//休眠2小时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//获取access_tokne方法
public void getAccessToken() {
// System.out.println(1);
String requestUrl = AccessTokenCommon.ACCESS_TOKEN_URL.replace("APPID", AccessTokenCommon.APPID).replace("APPSECRET", AccessTokenCommon.APPSECRET);
jsonObject = WeixinUtil.httpRequest(requestUrl, "GET", null);
AccessTokenCommon.ACCESS_TOKEN = jsonObject.getString("access_token");
System.out.println(AccessTokenCommon.ACCESS_TOKEN );
}
}
/**
*监听器,在监听器中启动线程
*/
//@Component
public class AccessTokenListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
new AccessTokenThreadUtil().start();
System.out.println("我的servlet开启了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("我的servlet关闭了");
}
}