From 1bca854b52e416be66916d4e85dec998bd734298 Mon Sep 17 00:00:00 2001 From: Daniel Qian Date: Tue, 20 Jan 2015 13:52:40 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20mp=20=E8=B0=83=E6=95=B4access=20tok?= =?UTF-8?q?en=E5=88=B7=E6=96=B0=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/resources/test-config.sample.xml | 2 +- .../weixin/mp/api/WxMpConfigStorage.java | 44 +++++---- .../mp/api/WxMpInMemoryConfigStorage.java | 60 +++++++----- .../me/chanjar/weixin/mp/api/WxMpService.java | 3 +- .../weixin/mp/api/WxMpServiceImpl.java | 92 +++++++++---------- .../mp/bean/result/WxMpOAuth2AccessToken.java | 2 +- .../chanjar/weixin/mp/api/ApiTestModule.java | 2 +- .../weixin/mp/api/WxMpBaseAPITest.java | 2 +- .../demo/WxMpDemoInMemoryConfigStorage.java | 2 +- .../src/test/resources/test-config.sample.xml | 2 +- 10 files changed, 115 insertions(+), 96 deletions(-) diff --git a/weixin-java-cp/src/test/resources/test-config.sample.xml b/weixin-java-cp/src/test/resources/test-config.sample.xml index 02c98f3f..aa99a962 100644 --- a/weixin-java-cp/src/test/resources/test-config.sample.xml +++ b/weixin-java-cp/src/test/resources/test-config.sample.xml @@ -5,7 +5,7 @@ 企业号应用Token 企业号应用EncodingAESKey 可以不填写 - 可以不填写 + 可以不填写 企业号通讯录里的某个userid 企业号通讯录的某个部门id 企业号通讯录里的某个tagid diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java index 68d42785..c54a119d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java @@ -9,6 +9,15 @@ import me.chanjar.weixin.common.bean.WxAccessToken; */ public interface WxMpConfigStorage { + public String getAccessToken(); + + public boolean isAccessTokenExpired(); + + /** + * 强制将access token过期掉 + */ + public void expireAccessToken(); + /** * 应该是线程安全的 * @param accessToken @@ -22,17 +31,30 @@ public interface WxMpConfigStorage { */ public void updateAccessToken(String accessToken, int expiresIn); - public String getAccessToken(); - + public String getJsapiTicket(); + + public boolean isJsapiTicketExpired(); + + /** + * 强制将jsapi ticket过期掉 + */ + public void expireJsapiTicket(); + + /** + * 应该是线程安全的 + * @param jsapiTicket + */ + public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + public String getAppId(); - + public String getSecret(); - + public String getToken(); public String getAesKey(); - public int getExpiresIn(); + public long getExpiresTime(); public String getOauth2redirectUri(); @@ -42,17 +64,7 @@ public interface WxMpConfigStorage { public String getHttp_proxy_username(); - public String getHttp_proxy_password(); - - - public String getJsapiTicket(); - - public boolean isJsapiTokenExpired(); - /** - * 应该是线程安全的 - * @param jsapiTicket - */ - public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + public String getHttp_proxy_password(); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java index d278d1b7..c694dde8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java @@ -16,7 +16,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected String token; protected String accessToken; protected String aesKey; - protected int expiresIn; + protected long expiresTime; protected String oauth2redirectUri; @@ -28,17 +28,43 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected String jsapiTicket; protected long jsapiTicketExpiresTime; + public String getAccessToken() { + return this.accessToken; + } + + public boolean isAccessTokenExpired() { + return System.currentTimeMillis() > this.expiresTime; + } + public synchronized void updateAccessToken(WxAccessToken accessToken) { updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } - public synchronized void updateAccessToken(String accessToken, int expiresIn) { + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { this.accessToken = accessToken; - this.expiresIn = expiresIn; + this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; } - public String getAccessToken() { - return this.accessToken; + public void expireAccessToken() { + this.expiresTime = 0; + } + + public String getJsapiTicket() { + return jsapiTicket; + } + + public boolean isJsapiTicketExpired() { + return System.currentTimeMillis() > this.jsapiTicketExpiresTime; + } + + public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + this.jsapiTicket = jsapiTicket; + // 预留200秒的时间 + this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; + } + + public void expireJsapiTicket() { + this.jsapiTicketExpiresTime = 0; } public String getAppId() { @@ -53,8 +79,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { return this.token; } - public int getExpiresIn() { - return this.expiresIn; + public long getExpiresTime() { + return this.expiresTime; } public void setAppId(String appId) { @@ -81,8 +107,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { this.accessToken = accessToken; } - public void setExpiresIn(int expiresIn) { - this.expiresIn = expiresIn; + public void setExpiresTime(long expiresTime) { + this.expiresTime = expiresTime; } @Override @@ -127,20 +153,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { } - public String getJsapiTicket() { - return jsapiTicket; - } - - public boolean isJsapiTokenExpired() { - return System.currentTimeMillis() > this.jsapiTicketExpiresTime; - } - - public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { - this.jsapiTicket = jsapiTicket; - // 预留200秒的时间 - this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; - } - @Override public String toString() { return "WxMpInMemoryConfigStorage{" + @@ -149,7 +161,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { ", token='" + token + '\'' + ", accessToken='" + accessToken + '\'' + ", aesKey='" + aesKey + '\'' + - ", expiresIn=" + expiresIn + + ", expiresTime=" + expiresTime + ", http_proxy_host='" + http_proxy_host + '\'' + ", http_proxy_port=" + http_proxy_port + ", http_proxy_username='" + http_proxy_username + '\'' + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index e8a56787..cef912ee 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -40,9 +40,10 @@ public interface WxMpService { * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token * + * @return * @throws me.chanjar.weixin.common.exception.WxErrorException */ - public void accessTokenRefresh() throws WxErrorException; + public String getAccessToken() throws WxErrorException; /** *
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
index 636e8f62..0bb1c550 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
@@ -44,12 +44,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
 public class WxMpServiceImpl implements WxMpService {
 
   /**
-   * 全局的是否正在刷新Access Token的flag
-   * true: 正在刷新
-   * false: 没有刷新
+   * 全局的是否正在刷新access token的锁
    */
-  protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false);
-  
+  protected static final Object GLOBAL_ACCESS_TOKEN_REFRESH_LOCK = new Object();
+
+  /**
+   * 全局的是否正在刷新jsapi_ticket的锁
+   */
+  protected static final Object GLOBAL_JSAPI_TICKET_REFRESH_LOCK = new Object();
+
   protected WxMpConfigStorage wxMpConfigStorage;
   
   protected final ThreadLocal retryTimes = new ThreadLocal();
@@ -66,52 +69,45 @@ public class WxMpServiceImpl implements WxMpService {
     }
   }
   
-  public void accessTokenRefresh() throws WxErrorException {
-    if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) {
-      try {
-        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
-            + "&appid=" + wxMpConfigStorage.getAppId()
-            + "&secret=" + wxMpConfigStorage.getSecret()
-            ;
-        try {
-          HttpGet httpGet = new HttpGet(url);
-          if (httpProxy != null) {
-            RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
-            httpGet.setConfig(config);
-          }
-          CloseableHttpClient httpclient = getHttpclient();
-          CloseableHttpResponse response = httpclient.execute(httpGet);
-          String resultContent = new BasicResponseHandler().handleResponse(response);
-          WxError error = WxError.fromJson(resultContent);
-          if (error.getErrorCode() != 0) {
-            throw new WxErrorException(error);
+  public String getAccessToken() throws WxErrorException {
+    if (wxMpConfigStorage.isAccessTokenExpired()) {
+      synchronized (GLOBAL_ACCESS_TOKEN_REFRESH_LOCK) {
+        if (wxMpConfigStorage.isAccessTokenExpired()) {
+          String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
+              + "&appid=" + wxMpConfigStorage.getAppId()
+              + "&secret=" + wxMpConfigStorage.getSecret()
+              ;
+          try {
+            HttpGet httpGet = new HttpGet(url);
+            if (httpProxy != null) {
+              RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
+              httpGet.setConfig(config);
+            }
+            CloseableHttpClient httpclient = getHttpclient();
+            CloseableHttpResponse response = httpclient.execute(httpGet);
+            String resultContent = new BasicResponseHandler().handleResponse(response);
+            WxError error = WxError.fromJson(resultContent);
+            if (error.getErrorCode() != 0) {
+              throw new WxErrorException(error);
+            }
+            WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+            wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+          } catch (ClientProtocolException e) {
+            throw new RuntimeException(e);
+          } catch (IOException e) {
+            throw new RuntimeException(e);
           }
-          WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
-          wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
-        } catch (ClientProtocolException e) {
-          throw new RuntimeException(e);
-        } catch (IOException e) {
-          throw new RuntimeException(e);
         }
-      } finally {
-        GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false);
       }
-    } else {
-      // 每隔100ms检查一下是否刷新完毕了
-      while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) {
-        try {
-          Thread.sleep(100);
-        } catch (InterruptedException e) {
-        }
-      }
-      // 刷新完毕了,就没他什么事儿了
     }
+    return wxMpConfigStorage.getAccessToken();
   }
 
+
   public String getJsapiTicket() throws WxErrorException {
-    if (wxMpConfigStorage.isJsapiTokenExpired()) {
-      synchronized (wxMpConfigStorage) {
-        if (wxMpConfigStorage.isJsapiTokenExpired()) {
+    if (wxMpConfigStorage.isJsapiTicketExpired()) {
+      synchronized (GLOBAL_JSAPI_TICKET_REFRESH_LOCK) {
+        if (wxMpConfigStorage.isJsapiTicketExpired()) {
           String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
           String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
           JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
@@ -440,10 +436,7 @@ public class WxMpServiceImpl implements WxMpService {
    * @throws WxErrorException
    */
   public  T execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
-    if (StringUtils.isBlank(wxMpConfigStorage.getAccessToken())) {
-      accessTokenRefresh();
-    }
-    String accessToken = wxMpConfigStorage.getAccessToken();
+    String accessToken = getAccessToken();
     
     String uriWithAccessToken = uri;
     uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
@@ -458,7 +451,8 @@ public class WxMpServiceImpl implements WxMpService {
        * 42001 access_token超时
        */
       if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
-        accessTokenRefresh();
+        // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
+        wxMpConfigStorage.expireAccessToken();
         return execute(executor, uri, data);
       }
       /**
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java
index c2a28546..d2a6ef8e 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java
@@ -65,7 +65,7 @@ public class WxMpOAuth2AccessToken {
   public String toString() {
     return "WxMpOAuth2AccessToken{" +
         "accessToken='" + accessToken + '\'' +
-        ", expiresIn=" + expiresIn +
+        ", expiresTime=" + expiresIn +
         ", refreshToken='" + refreshToken + '\'' +
         ", openId='" + openId + '\'' +
         ", scope='" + scope + '\'' +
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
index ad006acd..e6df8e34 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
@@ -42,7 +42,7 @@ public class ApiTestModule implements Module {
     @Override
     public String toString() {
       return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken
-          + ", expiresIn=" + expiresIn + ", token=" + token + ", openId=" + openId + "]";
+          + ", expiresTime=" + expiresTime + ", token=" + token + ", openId=" + openId + "]";
     }
      
   }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
index ef9590e6..e773f117 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
@@ -22,7 +22,7 @@ public class WxMpBaseAPITest {
   public void testRefreshAccessToken() throws WxErrorException {
     WxMpConfigStorage configStorage = wxService.wxMpConfigStorage;
     String before = configStorage.getAccessToken();
-    wxService.accessTokenRefresh();
+    wxService.getAccessToken();
 
     String after = configStorage.getAccessToken();
 
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
index 22d87dd4..becfbeb4 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
@@ -16,7 +16,7 @@ class WxMpDemoInMemoryConfigStorage extends WxMpInMemoryConfigStorage {
   @Override
   public String toString() {
     return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken
-        + ", expiresIn=" + expiresIn + ", token=" + token + ", aesKey=" + aesKey + "]";
+        + ", expiresTime=" + expiresTime + ", token=" + token + ", aesKey=" + aesKey + "]";
   }
 
 
diff --git a/weixin-java-mp/src/test/resources/test-config.sample.xml b/weixin-java-mp/src/test/resources/test-config.sample.xml
index 19535f3c..6ec4825a 100644
--- a/weixin-java-mp/src/test/resources/test-config.sample.xml
+++ b/weixin-java-mp/src/test/resources/test-config.sample.xml
@@ -4,7 +4,7 @@
     公众号Token
     公众号EncodingAESKey
     可以不填写
-    可以不填写
+    可以不填写
     某个加你公众号的用户的openId
     网页授权获取用户信息回调地址