HttpUrlConnection get和post简单实现(疑惑解决)
近期研究微信的公眾平臺開發(fā)。須要和微信的server進行數(shù)據(jù)讀取,簡單研究了下jdk自帶的HttpUrlConnection類(URLConnection的子類),簡單實現(xiàn)了一下微信的access_token獲取。
獲取微信access_token的地址:微信地址
該地址是get方法請求就可以。先貼代碼,doGet:
private final String ACCESS_TOKEN_WEIXIN = "https://api.weixin.qq.com/cgi-bin/token";public String doGet(String grantType, String appId, String secret){String url = ACCESS_TOKEN_WEIXIN+"?grant_type="+grantType+"&appid="+appId+"&secret="+secret;HttpURLConnection conn = null;InputStream is = null;InputStreamReader reader = null;BufferedReader br = null;String str = "";try {URL weiUrl = new URL(url);conn = (HttpURLConnection)weiUrl.openConnection();conn.setRequestProperty("connection", "Keep-Alive");conn.connect();is = conn.getInputStream();reader = new InputStreamReader(is, "UTF-8");br = new BufferedReader(reader);String readLine = "";while((readLine=br.readLine())!=null){str+=readLine+"\n";}} catch (Exception e) {e.printStackTrace();}finally{try{if(br!=null){br.close();}if(conn!=null){conn.disconnect();}}catch(Exception e1){e1.printStackTrace();}}return str;}我將InputStream流和InputStreamReader流都單獨定義引用是為了測試流關閉的問題,最后僅僅關閉最外層流,由于外層流關閉的時候就將包裹的流一并關閉了。見以下的BufferedReader類的close方法: public void close() throws IOException {synchronized (lock) {if (in == null)return;in.close();in = null;cb = null;}}當中,in就是包裹的流。
以下貼post方法的代碼。然后綜合比較一下,代碼:
public String doPost(String grantType, String appId, String secret){HttpURLConnection conn = null;InputStream is = null;InputStreamReader reader = null;BufferedReader br = null;String str = "";try {URL url = new URL(ACCESS_TOKEN_WEIXIN);conn = (HttpURLConnection)url.openConnection();conn.setRequestMethod("POST");conn.setDoOutput(true);//默覺得false的,所以須要設置conn.setUseCaches(false);conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");conn.connect();OutputStream outStream = conn.getOutputStream();DataOutputStream out = new DataOutputStream(outStream);String params = "grant_type="+grantType+"&appid="+appId+"&secret="+secret;out.writeBytes(params);out.close();is = conn.getInputStream();reader = new InputStreamReader(is, "UTF-8");br = new BufferedReader(reader);String readLine = "";while((readLine=br.readLine())!=null){str+=readLine+"\n";}} catch (Exception e) {e.printStackTrace();}finally{try{if(br!=null){br.close();}if(conn!=null){conn.disconnect();}}catch(Exception e1){e1.printStackTrace();}}return str;}兩相比較:
1.思路是一樣的:首先建立鏈接對象(URL)->由鏈接對象得到代理對象(HttpUrlConnection)->設置一些鏈接參數(shù)->啟動鏈接通道(conn.connect();)->進行輸出\輸入操作
2.get方法參數(shù)是帶在鏈接上的,所以沒有單獨賦值操作。直接在啟動鏈接通道后得到輸入流就可以得到返回值
3.post方法參數(shù)不帶在鏈接上,須要單獨賦值(賦值是使用輸出流來進行的)。須要將輸出打開(conn.setDoOutput(true);)由于URLConnection默認輸出是false。而input是默認打開的。故不用反復設置。
4.讀取返回值方式一樣。
在研究過程中,對于URLConnection如何得到的InputStream比較好奇。查看源代碼為:
public InputStream getInputStream() throws IOException {throw new UnknownServiceException("protocol doesn't support input");}所以非常迷惑,是jdk1.7 。等下來研究出來再補上來。
共勉!
-----------------------------------------------------------------
緊跟上面。對于jdk如何實現(xiàn)的getInputStream和getOutputStream以及其它一些方法,經(jīng)過研究。最終找到了。接下來釋疑:
須要注意到:
URL url = new URL(ACCESS_TOKEN_WEIXIN);conn = (HttpURLConnection)url.openConnection();然后去尋找handler的賦值方法getURLStreamHandler。發(fā)現(xiàn)例如以下代碼-> if (handler == null) {String packagePrefixList = null;packagePrefixList= java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction(protocolPathProp,""));if (packagePrefixList != "") {packagePrefixList += "|";}// REMIND: decide whether to allow the "null" class prefix// or not.packagePrefixList += JDK_PACKAGE_PREFIX;StringTokenizer packagePrefixIter =new StringTokenizer(packagePrefixList, "|");while (handler == null &&packagePrefixIter.hasMoreTokens()) {String packagePrefix =packagePrefixIter.nextToken().trim();// do not try to instantiate the JDK gopher handler// unless the system property had been explicitly setif (protocol.equalsIgnoreCase(GOPHER) &&packagePrefix.equals(JDK_PACKAGE_PREFIX) &&!enableGopher) {continue;}try {String clsName = packagePrefix + "." + protocol +".Handler";Class cls = null;try {cls = Class.forName(clsName);
當中JDK_PACKAGE_PREFIX在類中定義的是: private static final String JDK_PACKAGE_PREFIX = "sun.net.www.protocol";再結(jié)合以下的
String clsName = packagePrefix + "." + protocol +".Handler";基本能夠定位到handler類的實現(xiàn)類在sun.net.www.protocol.http.Handler。
然后百度搜索之后找到源代碼(jdk下沒有sun包的源代碼)->
sun.net.www.protocol.http包的視圖??Handler.java的源代碼?->
protected java.net.URLConnection openConnection(URL u)throws IOException {return openConnection(u, (Proxy)null);}protected java.net.URLConnection openConnection(URL u, Proxy p)throws IOException {return new HttpURLConnection(u, p, this);}能夠看到返回值是HttpURLConnection,可是發(fā)現(xiàn)該類中并沒有引入java.net.HttpURLConnection類,故推測在Handler的同包下有一個同名類,果然發(fā)現(xiàn)該類,源代碼?-> public class HttpURLConnection extends java.net.HttpURLConnection
這是關建行,該類繼承了java.net.HttpURLConnection類。所以返回該類能夠由java.net.HttpURLConnection類的父類URLConnection接到,然后強轉(zhuǎn)成java.net.HttpURLConnection類。設計十分巧妙-> @Overridepublic synchronized OutputStream getOutputStream() throws IOException {try {if (!doOutput) {throw new ProtocolException("cannot write to a URLConnection"+ " if doOutput=false - call setDoOutput(true)");}if (method.equals("GET")) {method = "POST"; // Backward compatibility}if (!"POST".equals(method) && !"PUT".equals(method) &&"http".equals(url.getProtocol())) {throw new ProtocolException("HTTP method " + method +" doesn't support output");}// if there's already an input stream open, throw an exceptionif (inputStream != null) {throw new ProtocolException("Cannot write output after reading input.");}if (!checkReuseConnection())connect();......
這就是getOutputStream的實現(xiàn)地方了,正好doOutput屬性的設置也在這里起到了作用。
艾瑪,最終找到實現(xiàn)的地方了。其它方法原理一致了。
略微整理下:整個的類流轉(zhuǎn)過程就是:sun.net.www.protocol.http.HttpURLConnection轉(zhuǎn)化成java.net.URLConnection,然后強轉(zhuǎn)成java.net.HttpURLConnection。
暈乎~
轉(zhuǎn)載于:https://www.cnblogs.com/mfrbuaa/p/5196515.html
總結(jié)
以上是生活随笔為你收集整理的HttpUrlConnection get和post简单实现(疑惑解决)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#虚方法认识
- 下一篇: PHP——分页显示数据库内容