1、access_token簡介html
爲了使第三方開發者可以爲用戶提供更多更有價值的個性化服務,微信公衆平臺 開放了許多接口,包括自定義菜單接口、客服接口、獲取用戶信息接口、用戶分組接口、羣發接口等,java
開發者在調用這些接口時,都須要傳入一個相同的參數 access_token,它是公衆帳號的全局惟一票據,它是接口訪問憑證。mysql
access_token是公衆號的全局惟一票據,公衆號調用各接口時都需使用access_token。開發者須要進行妥善保存。web
access_token的存儲至少要保留512個字符空間。sql
access_token的有效期目前爲2個小時,需定時刷新,重複獲取將致使上次獲取的 access_token失效。數據庫
若是第三方不使用中控服務器,而是選擇各個業務邏輯點各自去刷新access_token,那麼就可能會產生衝突,致使服務不穩定。apache
公衆號可使用AppID和AppSecret調用本接口來獲取access_token。AppID和AppSecret可在微信公衆平臺官網-開發者中心頁中得到(須要已經成爲開發者,且賬號沒有異常狀態)。注意調用全部微信接口時均需使用https協議。json
access_token的有效期是7200秒(兩小時),在有效期內,能夠一直使用,只有當access_token過時時,才須要再次調用接口 獲取access_token。在理想狀況下,一個7x24小時運行的系統,天天只須要獲取12次access_token,即每2小時獲取一次。若是在 有效期內,再次獲取access_token,那麼上一次獲取的access_token將失效。api
目前,獲取access_token接口的調用頻率限制爲2000次/天,若是每次發送客服消息、獲取用戶信息、羣發消息以前都要先調用獲取 access_token接口獲得接口訪問憑證,這顯然是不合理的,一方面會更耗時(多了一次接口調用操做),另外一方面2000次/天的調用限制恐怕也不 夠用。所以,在實際應用中,咱們須要將獲取到的access_token存儲起來,而後按期調用access_token接口更新它,以保證隨時取出的 access_token都是有效的。數組
咱們先看看官方的說明:
接口調用請求說明
http請求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
參數說明
參數 | 是否必須 | 說明 |
---|---|---|
grant_type | 是 | 獲取access_token填寫client_credential |
appid | 是 | 第三方用戶惟一憑證 |
secret | 是 | 第三方用戶惟一憑證密鑰,即appsecret |
返回說明
正常狀況下,微信會返回下述JSON數據包給公衆號:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
參數 | 說明 |
---|---|
access_token | 獲取到的憑證 |
expires_in | 憑證有效時間,單位:秒 |
錯誤時微信會返回錯誤碼等信息,JSON數據包示例以下(該示例爲AppID無效錯誤):
{"errcode":40013,"errmsg":"invalid appid"}
2、封裝基本類
封裝一下token類:
package com.souvc.weixin.pojo; /** * 類名: Token </br> * 描述: 憑證 </br> * 開發人員: souvc </br> * 建立時間: 2015-9-30 </br> * 發佈版本:V1.0 </br> */ public class Token { // 接口訪問憑證 private String accessToken; // 憑證有效期,單位:秒 private int expiresIn; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } }
3、獲取token。
1.問題:如何經過獲取token?
解決方案:(1)直接經過瀏覽器訪問。(2)編寫程序,模擬https鏈接,得到token。
解決詳細步驟以下:
(1)瀏覽器中直接輸入連接:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET ,而後把APPID和APPSECRET替換成本身的appID和appsecret,在瀏覽器便可得到token。
(2)如何在程序中模擬發送https請求,而且獲取到token呢?
對於https請求,咱們須要一個證書信任管理器,這個管理器類須要本身定義,但須要實現X509TrustManager接口,
首先定義一個MyX509TrustManager 類。
package com.souvc.weixin.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * 類名: MyX509TrustManager </br> * 描述: 信任管理器 </br> * 開發人員: souvc </br> * 建立時間: 2015-9-30 </br> * 發佈版本:V1.0 </br> */ public class MyX509TrustManager implements X509TrustManager { // 檢查客戶端證書 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 檢查服務器端證書 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 返回受信任的X509證書數組 public X509Certificate[] getAcceptedIssuers() { return null; } }
創建一個token測試類:
package com.souvc.weixin.test; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import com.souvc.weixin.util.MyX509TrustManager; public class TokenTest { public static void main(String[] args) throws Exception { //修改appID,secret String tokenUrl="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; //創建鏈接 URL url = new URL(tokenUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); // 建立SSLContext對象,並使用咱們指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 從上述SSLContext對象中獲得SSLSocketFactory對象 SSLSocketFactory ssf = sslContext.getSocketFactory(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); // 設置請求方式(GET/POST) httpUrlConn.setRequestMethod("GET"); // 取得輸入流 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); //讀取響應內容 StringBuffer buffer = new StringBuffer(); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); httpUrlConn.disconnect(); //輸出返回結果 System.out.println(buffer); } }
微信服務器返回的結果:
{"access_token":"E3kRcQTati3QBPz97ou7zG0NXFrZFbA5No_hs5FNUZ62ROT0jr0txWr-gG1w-t06kk0zBW0kFmJiicJAydFyHNZhIh2uqIw4B5t85huRLs4","expires_in":7200}
2.問題:微信服務器返回的是json數據,如何從json裏面解析出來的值?
方案:能夠經過一款開源的json開發工具包json-lib,將他轉換爲java對象。
詳細實現方法:
首先引入jar包
jar包下載: http://yunpan.cn/cH9UCqsQ9XVdE 訪問密碼 2711
封裝一個通用的工具類 CommonUtil ,用於專門獲取token:
package com.souvc.weixin.util; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.souvc.weixin.pojo.Token; /** * 類名: CommonUtil </br> * 描述: 通用工具類 </br> * 開發人員: souvc </br> * 建立時間: 2015-9-30 </br> * 發佈版本:V1.0 </br> */ public class CommonUtil { private static Logger log = LoggerFactory.getLogger(CommonUtil.class); // 憑證獲取(GET) public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * 發送https請求 * * @param requestUrl 請求地址 * @param requestMethod 請求方式(GET、POST) * @param outputStr 提交的數據 * @return JSONObject(經過JSONObject.get(key)的方式獲取json對象的屬性值) */ public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; try { // 建立SSLContext對象,並使用咱們指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 從上述SSLContext對象中獲得SSLSocketFactory對象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 設置請求方式(GET/POST) conn.setRequestMethod(requestMethod); // 當outputStr不爲null時向輸出流寫數據 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意編碼格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 從輸入流讀取返回內容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } // 釋放資源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("鏈接超時:{}", ce); } catch (Exception e) { log.error("https請求異常:{}", e); } return jsonObject; } /** * 獲取接口訪問憑證 * * @param appid 憑證 * @param appsecret 密鑰 * @return */ public static Token getToken(String appid, String appsecret) { Token token = null; String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret); // 發起GET請求獲取憑證 JSONObject jsonObject = httpsRequest(requestUrl, "GET", null); if (null != jsonObject) { try { token = new Token(); token.setAccessToken(jsonObject.getString("access_token")); token.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { token = null; // 獲取token失敗 log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return token; } }
修改Token測試類:
package com.souvc.weixin.test; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.junit.Test; import com.souvc.weixin.pojo.Token; import com.souvc.weixin.util.CommonUtil; import com.souvc.weixin.util.MyX509TrustManager; public class TokenTest { @Test public void testGetToken1() throws Exception { String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=appID&secret=appsecret"; // 創建鏈接 URL url = new URL(tokenUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); // 建立SSLContext對象,並使用咱們指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 從上述SSLContext對象中獲得SSLSocketFactory對象 SSLSocketFactory ssf = sslContext.getSocketFactory(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); // 設置請求方式(GET/POST) httpUrlConn.setRequestMethod("GET"); // 取得輸入流 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader( inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); // 讀取響應內容 StringBuffer buffer = new StringBuffer(); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); httpUrlConn.disconnect(); // 輸出返回結果 System.out.println(buffer); } @Test public void testGetToken2() { Token token = CommonUtil.getToken("appID","appsecret"); System.out.println("access_token:"+token.getAccessToken()); System.out.println("expires_in:"+token.getExpiresIn()); } }
控制檯輸出效果以下,說明咱們獲取到了access_token和expires_in:
access_token:2amR6pr1eN-BuSBgho-nzo5tofxJ6BdEnRJQ87Zs5bj4ny4CGB8w-1D3YtjG2PzmEvVm1INrsVg-5BjyHCkWmBKsLPDSF3r_bdaPxMpKtbw
expires_in:7200
4、緩存access_token。
問題:問題又來來,但是咱們如何把這個值緩存起來呢,而且須要2倆小時獲取一次?
方案:
(1)能夠經過緩存框架把該值經過key-values形式緩存在內存中 。
(2)能夠把該值存入數據庫中,須要的時候就去提取。
使用數據庫的話,大概思路就是這樣的。第一次使用將其access_token存儲起來,下次須要access_token則將其查出。如果失效則,從新建立並更新數據庫.如果沒有失效,則直接使用。
創建數據庫 souvc
create database souvc ;
創建數據庫表 t_token 來存放token值
CREATE TABLE `t_token` ( `id` int(11) NOT NULL AUTO_INCREMENT, `access_token` varchar(1024) NOT NULL, `expires_in` int(11) NOT NULL, `createTime` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
如今咱們經過jdbc來讀取這個token值。
導入jdbc的jar包
http://yunpan.cn/cH9IRaKemL3II 訪問密碼 2ffa
導入mysql 的jar包
http://yunpan.cn/cmv3BGe9CkUr3 訪問密碼 3601
新建一個數據庫配置文件db.properties
#Oracle #jdbc.driverClassName=oracle.jdbc.OracleDriver #jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl #jdbc.username=root #jdbc.password=123456 #Mysql jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/souvc jdbc.username=root jdbc.password=123456 dataSource.initialSize=10 dataSource.maxIdle=20 dataSource.minIdle=5 dataSource.maxActive=50 dataSource.maxWait=1000
新建一個jdbc的鏈接工具類
package com.souvc.weixin.util; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; /** * 類名: DBUtility </br> * 描述: 鏈接數據庫工具類 </br> * 開發人員: souvc </br> * 建立時間: 2015-10-5 </br> * 發佈版本:V1.0 </br> */ public class DBUtility { private static BasicDataSource dataSource = null; public DBUtility() { } public static void init() { Properties dbProps = new Properties(); // 取配置文件能夠根據實際的不一樣修改 try { dbProps.load(DBUtility.class.getClassLoader().getResourceAsStream("db.properties")); } catch (IOException e) { e.printStackTrace(); } try { String driveClassName = dbProps.getProperty("jdbc.driverClassName"); String url = dbProps.getProperty("jdbc.url"); String username = dbProps.getProperty("jdbc.username"); String password = dbProps.getProperty("jdbc.password"); String initialSize = dbProps.getProperty("dataSource.initialSize"); String minIdle = dbProps.getProperty("dataSource.minIdle"); String maxIdle = dbProps.getProperty("dataSource.maxIdle"); String maxWait = dbProps.getProperty("dataSource.maxWait"); String maxActive = dbProps.getProperty("dataSource.maxActive"); dataSource = new BasicDataSource(); dataSource.setDriverClassName(driveClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); // 初始化鏈接數 if (initialSize != null) dataSource.setInitialSize(Integer.parseInt(initialSize)); // 最小空閒鏈接 if (minIdle != null) dataSource.setMinIdle(Integer.parseInt(minIdle)); // 最大空閒鏈接 if (maxIdle != null) dataSource.setMaxIdle(Integer.parseInt(maxIdle)); // 超時回收時間(以毫秒爲單位) if (maxWait != null) dataSource.setMaxWait(Long.parseLong(maxWait)); // 最大鏈接數 if (maxActive != null) { if (!maxActive.trim().equals("0")) dataSource.setMaxActive(Integer.parseInt(maxActive)); } } catch (Exception e) { e.printStackTrace(); System.out.println("建立鏈接池失敗!請檢查設置!!!"); } } /** * 數據庫鏈接 * * @return * @throws SQLException */ public static synchronized Connection getConnection() throws SQLException { if (dataSource == null) { init(); } Connection conn = null; if (dataSource != null) { conn = dataSource.getConnection(); } return conn; } /** * 關閉數據庫 * * @param conn */ public static void closeConnection(Connection conn) { if (conn != null) { try { conn.close(); } catch (SQLException e) { System.out.println("關閉資源失敗"); e.printStackTrace(); } } } }
新建測試鏈接類
package com.souvc.weixin.test; import java.sql.SQLException; import org.junit.Test; import com.souvc.weixin.util.DBUtility; public class TestDBUtility { /** * 方法名:testgetConnection</br> * 詳述:測試是否鏈接</br> * 開發人員:souvc </br> * 建立時間:2015-10-5 </br> * @throws SQLException * @throws */ @Test public void testgetConnection() throws SQLException { DBUtility db = new DBUtility(); System.out.println(db.getConnection()); } }
測試結果以下,說明了能連上數據庫:
jdbc:mysql://localhost:3306/souvc, UserName=root@localhost, MySQL-AB JDBC Driver
接下來咱們封裝一個從數據庫讀取token和保存token的類
package com.souvc.weixin.util; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.souvc.weixin.pojo.Token; /** * 類名: TokenUtil </br> * 描述: Token </br> * 開發人員: souvc </br> * 建立時間: 2015-10-5 </br> * 發佈版本:V1.0 </br> */ public class TokenUtil { /** * 方法名:getToken</br> * 詳述:從數據庫裏面獲取token</br> * 開發人員:souvc </br> * 建立時間:2015-10-5 </br> * @return * @throws */ public static Map<String, Object> getToken(){ Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; String sql ="select * from t_token order by createTime desc limit 0,1"; String access_token=""; Map<String, Object> map=new HashMap<String, Object>(); Integer expires_in=0; try { //建立數據庫連接 con = DBUtility.getConnection(); //建立處理器 stmt = con.prepareStatement(sql); //查詢Token,讀取1條記錄 rs = stmt.executeQuery(); if (rs.next()) { access_token = rs.getString("access_token"); expires_in=rs.getInt("expires_in"); map.put("access_token", access_token); map.put("expires_in", expires_in); } } catch (SQLException ex) { System.out.println("數據庫操做異常:" + ex.getMessage()); } finally { DBUtility.closeConnection(con); } return map; } /** * 方法名:saveToken</br> * 詳述:保存token</br> * 開發人員:souvc </br> * 建立時間:2015-10-5 </br> * @return * @throws */ public static void saveToken(Token token){ //存入數據庫中 Connection conn = null; PreparedStatement pst = null; try { //建立數據庫連接 conn = DBUtility.getConnection(); //建立預處理器 pst = conn.prepareStatement("insert into t_token(access_token,expires_in,createTime)values(?,?,?)"); pst.setString(1, token.getAccessToken()); pst.setInt(2, token.getExpiresIn()); long now = new Date().getTime(); pst.setTimestamp(3, new java.sql.Timestamp(now)); pst.execute(); } catch (SQLException ex) { System.out.println("數據庫操做異常:" + ex.getMessage()); } finally { DBUtility.closeConnection(conn); } } }
創建一個測試類測試
@Test public void testGetToken3() { Map<String, Object> token=TokenUtil.getToken(); System.out.println(token.get("access_token")); System.out.println(token.get("expires_in")); } @Test public void testSaveToken4() { Token token=CommonUtil.getToken("appID", "appsecret"); TokenUtil.saveToken(token); }
獲取token的結果:
6bbRDvtxIG9jwVhJQBOXoi6-bN6hlMNagxZjsh5QAf447shWcCi9LC1GL5PFgrn4zTdeFMpaU4EqE0CTQDQ2_PE0qOdXTHEM2gt6CV_zlRY
7200
保存token的結果:
這樣基本完成了。
也許你們以爲這個方法有點麻煩,也但願你們多多提出修改的建議,或是在評論中說一下本身的方法。貼出代碼,讓你們一塊兒進步。
五,問題:可是如何讓這個保存工做隔7200 秒進行保存呢?
方案:
作一個定時器。
作一個web實現監聽器。
按期獲取並存儲access_token的流程爲:
Web服務器啓動時就加載一個Servlet,在Servlet的init()方法中啓動一個線 程,在線程的run()方法中經過死循環+Thread.sleep()的方式按期獲取access_token,而後將獲取到的 access_token保存在public static修飾的變量中。
在工程中建立一個InitServlet類,該類的代碼以下:
package com.souvc.weixin.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.souvc.weixin.thread.TokenThread; import com.souvc.weixin.util.CommonUtil; /** * 類名: InitServlet </br> * 描述: 初始化servlet </br> * 開發人員: souvc </br> * 建立時間: Oct 6, 2015 </br> * 發佈版本:V1.0 </br> */ public class InitServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static Logger log = LoggerFactory.getLogger(CommonUtil.class); public void init() throws ServletException { // 獲取web.xml中配置的參數 TokenThread.appid = getInitParameter("appid"); TokenThread.appsecret = getInitParameter("appsecret"); log.info("weixin api appid:{}", TokenThread.appid); log.info("weixin api appsecret:{}", TokenThread.appsecret); // 未配置appid、appsecret時給出提示 if ("".equals(TokenThread.appid) || "".equals(TokenThread.appsecret)) { log.error("appid and appsecret configuration error, please check carefully."); } else { // 啓動定時獲取access_token的線程 new Thread(new TokenThread()).start(); } } }
從上面的代碼 能夠看出,InitServlet類只重寫了init()方法,並無重寫doGet()和doPost()兩個方法,
由於咱們並不打算讓 InitServlet來處理訪問請求。init()方法的實現也比較簡單,先獲取在web.xml中配置的參數appid和appsecret,
再啓動 線程TokenThread定時獲取access_token。
InitServlet在web.xml中的配置以下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>coreServlet</servlet-name> <servlet-class> com.souvc.weixin.servlet.CoreServlet </servlet-class> </servlet> <!-- url-pattern中配置的/coreServlet用於指定該Servlet的訪問路徑 --> <servlet-mapping> <servlet-name>coreServlet</servlet-name> <url-pattern>/coreServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>initServlet</servlet-name> <servlet-class> com.souvc.weixin.servlet.InitServlet </servlet-class> <!-- 配置獲取access_token所需參數appid和appsecret --> <init-param> <param-name>appid</param-name> <param-value>appid</param-value> </init-param> <init-param> <param-name>appsecret</param-name> <param-value>appsecret</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> </web-app>
InitServlet 在web.xml中的配置與普通Servlet的配置有幾點區別:
1)經過配置<init-param>向Servlet中傳入參數;
2)通 過配置<load-on-startup>使得Web服務器啓動時就加載該Servlet;
3)沒有配置<servlet- mapping>,由於InitServlet並不對外提供訪問。
TokenThread的源代碼以下:
package com.souvc.weixin.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.souvc.weixin.pojo.Token; import com.souvc.weixin.util.CommonUtil; import com.souvc.weixin.util.TokenUtil; /** * 類名: TokenThread </br> * 描述: 定時獲取微信access_token的線程 </br> * 開發人員: souvc </br> * 建立時間: Oct 6, 2015 </br> * 發佈版本:V1.0 </br> */ public class TokenThread implements Runnable { private static Logger log = LoggerFactory.getLogger(TokenThread.class); // 第三方用戶惟一憑證 public static String appid = ""; // 第三方用戶惟一憑證密鑰 public static String appsecret = ""; public static Token accessToken = null; public void run() { while (true) { try { accessToken = CommonUtil.getToken(appid, appsecret); if (null != accessToken) { //調用存儲到數據庫 TokenUtil.saveToken(accessToken); log.info("獲取access_token成功,有效時長{}秒 token:{}", accessToken.getExpiresIn(), accessToken.getAccessToken()); // 休眠7000秒 Thread.sleep((accessToken.getExpiresIn() - 200)*1000); } else { // 若是access_token爲null,60秒後再獲取 Thread.sleep(60 * 1000); } } catch (InterruptedException e) { try { Thread.sleep(60 * 1000); } catch (InterruptedException e1) { log.error("{}", e1); } log.error("{}", e); } } } }
配置一下log4j.properties 日誌文件:
log4j.rootLogger=info,console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n
代碼中經過while(true){}構造了一個死循環(永久執行);
調用公衆平臺接口獲取access_token;
讓線程休眠7000秒再運行,即每隔7000秒獲取一次access_token,保證access_token永不失效。
舒適提示:
能夠不存入數據庫。
在項目中的其餘類,能夠經過調用 TokenThread.accessToken.getToken() 來獲得接口訪問憑證access_token。
其餘文章關聯:
第一篇:微信公衆平臺開發實戰Java版之瞭解微信公衆平臺基礎知識以及資料準備
第二篇 :微信公衆平臺開發實戰Java版之開啓開發者模式,接入微信公衆平臺開發
第三篇 :微信公衆平臺開發實戰Java版之請求消息,響應消息以及事件消息類的封裝
第四篇 :微信公衆平臺開發實戰Java版之完成消息接受與相應以及消息的處理
第五篇 :微信公衆平臺開發實戰Java版之如何獲取公衆號的access_token以及緩存access_token
第六篇 :微信公衆平臺開發實戰Java版之如何自定義微信公衆號菜單
第七篇 :微信公衆平臺開發實戰Java版之如何獲取微信用戶基本信息
第八篇 :微信公衆平臺開發實戰Java版之如何網頁受權獲取用戶基本信息
第九篇 :微信公衆平臺開發實戰Java版之如何實現自定義分享內容