HttpUrlConnection get和post簡單實現(疑惑解決)
來源:程序員人生 發布時間:2014-11-25 08:48:13 閱讀次數:3372次
最近研究微信的公眾平臺開發,需要和微信的http://www.vxbq.cn/server/進行數據讀取,簡單研究了下jdk自帶的HttpUrlConnection類(URLConnection的子類),簡單實現了1下微信的http://www.vxbq.cn/access/_token獲得。
獲得微信http://www.vxbq.cn/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⑻");
br = new BufferedReader(reader);
String readLine = "";
while((readLine=br.readLine())!=null){
str+=readLine+"
";
}
} 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流都單獨定義援用是為了測試流關閉的問題,最后只關閉最外層流,由于外層流關閉的時候就將包裹的流1并關閉了。見下面的BufferedReader類的close方法:
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
in.close();
in = null;
cb = null;
}
}
其中,in就是包裹的流。
下面貼post方法的代碼,然后綜合比較1下,代碼:
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⑻");
br = new BufferedReader(reader);
String readLine = "";
while((readLine=br.readLine())!=null){
str+=readLine+"
";
}
} 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.思路是1樣的:首先建立鏈接對象(URL)->由鏈接對象得到代理對象(HttpUrlConnection)->設置1些鏈接參數->啟動鏈接通道(conn.connect();)->進行輸出輸入操作
2.get方法參數是帶在鏈接上的,所以沒有單獨賦值操作,直接在啟動鏈接通道后得到輸入流便可得到返回值
3.post方法參數不帶在鏈接上,需要單獨賦值(賦值是使用輸出流來進行的),需要將輸出打開(conn.setDoOutput(true);)由于URLConnection默許輸出是false;而input是默許打開的,故不用重復設置。
4.讀取返回值方式1樣。
在研究進程中,對URLConnection怎樣得到的InputStream比較好奇,查看源碼為:
public InputStream getInputStream() throws IOException {
throw new UnknownServiceException("protocol doesn't support input");
}
所以很迷惑,是jdk1.7 。等下來研究出來再補上來。
共勉!
-----------------------------------------------------------------
緊跟上面,對jdk怎樣實現的getInputStream和getOutputStream和其他1些方法,經過研究,終究找到了。接下來釋疑:
需要注意到:
URL url = new URL(ACCESS_TOKEN_WEIXIN);
conn = (HttpURLConnection)url.openConnection();
這里得到HttpUrlConnection的時候是使用了強轉得到的,故跟蹤到URL類的openConnection方法內->
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this);
}
這里的handler定義在URL類的上方->
transient URLStreamHandler handler;
然后去尋覓handler的賦值方法getURLStreamHandler,發現以下代碼->
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 set
if (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";
再結合下面的
String clsName = packagePrefix + "." + protocol +
".Handler";
基本可以定位到handler類的實現類在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,但是發現該類中并沒有引入java.net.HttpURLConnection類,故猜想在Handler的同包下有1個同名類,果然發現該類,源碼 ->
public class HttpURLConnection extends java.net.HttpURLConnection
這是關建行,該類繼承了java.net.HttpURLConnection類。所以返回該類可以由java.net.HttpURLConnection類的父類URLConnection接到,然后強轉成java.net.HttpURLConnection類。設計10分奇妙->
@Override
public 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 exception
if (inputStream != null) {
throw new ProtocolException("Cannot write output after reading input.");
}
if (!checkReuseConnection())
connect();
......
這就是getOutputStream的實現地方了,正好doOutput屬性的設置也在這里起到了作用。艾瑪,終究找到實現的地方了,其他方法原理1致了。
略微整理下:全部的類流轉進程就是:sun.net.www.protocol.http.HttpURLConnection轉化成java.net.URLConnection,然后強轉成java.net.HttpURLConnection。
暈乎~
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈