最近作一個公衆號項目,微信公衆號會要求服務端找微信請求一個access_token,獲取的過程:java
access_token是公衆號的全局惟一接口調用憑據,公衆號調用各接口時都需使用access_token。開發者須要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前爲2個小時,需定時刷新,重複獲取將致使上次獲取的access_token失效。web
公衆平臺的API調用所需的access_token的使用及生成方式說明:spring
一、建議公衆號開發者使用中控服務器統一獲取和刷新Access_token,其餘業務邏輯服務器所使用的access_token均來自於該中控服務器,不該該各自去刷新,不然容易形成衝突,致使access_token覆蓋而影響業務;api
二、目前Access_token的有效期經過返回的expire_in來傳達,目前是7200秒以內的值。中控服務器須要根據這個有效時間提早去刷新新access_token。在刷新過程當中,中控服務器可對外繼續輸出的老access_token,此時公衆平臺後臺會保證在5分鐘內,新老access_token均可用,這保證了第三方業務的平滑過渡;tomcat
三、Access_token的有效時間可能會在將來有調整,因此中控服務器不只須要內部定時主動刷新,還須要提供被動刷新access_token的接口,這樣便於業務服務器在API調用獲知access_token已超時的狀況下,能夠觸發access_token的刷新流程。服務器
因此解決方案就是作一個定時任務,服務啓動的時候獲取一次token,以後每隔兩小時自動獲取一次。這用Spring的@Scheduled註解很容易實現:微信
@Override @Scheduled(fixedRate=1000*60*59*2) public void updateToken() { try{ for(AppConfig appConfig : SysConfig.AppLists){ Token token = TokenAPI.token(appConfig.wxAppId,appConfig.wxAppSecret); appConfig.access_token = token.getAccess_token(); Ticket ticket = TicketAPI.ticketGetticket(token.getAccess_token()); appConfig.ticket = ticket.getTicket(); logger.info("access_token:" + token.getAccess_token()+",js_api_ticket:"+ticket.getTicket()); } }catch (Exception e){ logger.info("刷新access_token出錯"); } }
這裏使用fixedRate來指定任務執行的時間間隔(毫秒記)。app
而後由於如今項目只有一個服務號,沒有申請測試用的服務號,本地調試也會去獲取access_token ,結果致使服務端的生產環境的token失效,必須重啓服務器的tomcat來刷新token。顯然這種作法是很是愚蠢的,目前還沒正式投入使用,投入使用後這顯然不能接受。ide
解決方案就是,統一一個接口來向服務端獲取token,當token失效時,執行一次updateToken的方法,以刷新token。spring-boot
===================================================================手動分割線============================================================
而後我就比較好奇一個事,那就是,當加了@Scheduled(fixedRate=1000*60*59*2)的方法被其餘方法調用的時候,定時任務會有變化嗎?好比,我調用一次,那麼任務是仍是按照之前預約的時間跑呢,仍是會在我調用以後的2小時(預定的間隔時間)纔會再跑?百度Google找了找,包括官方文檔也沒給個說法。爲這個也不想去看源代碼,仍是寫個demo驗證下算了。
首先建立一個Spring Boot的項目,引入web模塊(使用rest請求來手動調用定時任務)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
項目結構:
Service接口:
package com.xiatingfei.schedule.demo.service; public interface TaskService { void SayHelloTask(); }
Service實現(每隔10秒鐘在控制檯打印一次Hello!):
package com.xiatingfei.schedule.demo.service.impl; import com.xiatingfei.schedule.demo.service.TaskService; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; import java.util.Date; @Service public class TaskServiceImpl implements TaskService { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); @Override @Scheduled(fixedRate = 10000) public void SayHelloTask() { System.out.println("Hello! Now is " + dateFormat.format(new Date())); } }
Controller:
package com.xiatingfei.schedule.demo.controller; import com.xiatingfei.schedule.demo.service.TaskService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class TriggerController { @Autowired TaskService taskService; @RequestMapping("/sayHello") public void TriggerTask(){ taskService.SayHelloTask(); } }
而後在SpringBoot的啓動類加入定時任務的註解@EnableScheduling
package com.xiatingfei.schedule.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
啓動應用,能夠看到控制檯能夠定時打印Hello和時間:
在Postman給服務端發一個請求,手動調用定時任務:
get http://localhost:8080/demo/sayHello
結果:
能夠看到定時任務沒有受到調用的影響,仍是按照本身的節奏10秒鐘一次~~
(並且,還會發現Spring會在第一次收到Http請求的時候纔會初始化dispatcherServlet,而不是服務啓動的時候就初始化)