近期研究微信的公众平台开发。须要和微信的server进行数据读取,简单研究了下jdk自带的HttpUrlConnection类(URLConnection的子类),简单实现了一下微信的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)->设置一些链接參数->启动链接通道(conn.connect();)->进行输出\输入操作

2.get方法參数是带在链接上的,所以没有单独赋值操作。直接在启动链接通道后得到输入流就可以得到返回值

3.post方法參数不带在链接上,须要单独赋值(赋值是使用输出流来进行的)。须要将输出打开(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如何实现的getInputStream和getOutputStream以及其它一些方法,经过研究。最终找到了。接下来释疑:

须要注意到:

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的同包下有一个同名类,果然发现该类,源代码 ->

public class HttpURLConnection extends java.net.HttpURLConnection

这是关建行,该类继承了java.net.HttpURLConnection类。所以返回该类能够由java.net.HttpURLConnection类的父类URLConnection接到,然后强转成java.net.HttpURLConnection类。设计十分巧妙->

@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属性的设置也在这里起到了作用。

艾玛,最终找到实现的地方了。其它方法原理一致了。

略微整理下:整个的类流转过程就是:sun.net.www.protocol.http.HttpURLConnection转化成java.net.URLConnection,然后强转成java.net.HttpURLConnection。

晕乎~

最新文章

  1. Java学习笔记,前两章总结
  2. Android 另类方法监听软键盘的弹出收起事件
  3. Java for LeetCode 049 Anagrams
  4. javaScript判断鼠标滚轮的上下滚动
  5. 转:SQL SERVER数据库中实现快速的数据提取和数据分页
  6. MySQL主从复制技术(纯干货)
  7. css块级标签,行内标签,行内块标签的转换(2)
  8. join和wait
  9. TCP建立连接和断开连接图解
  10. Git 中 SSH key 生成步骤
  11. Python安装与使用的常见问题
  12. 封装一个 员工类 使用preparedStatement 查询数据 (2) 使用 arrayList 集合
  13. 适用于app.config与web.config的ConfigUtil读写工具类
  14. Oracle exp和expdp对数据进行备份
  15. linux服务基础(一)之CentOS6编译安装httpd2.4
  16. python基础---列表生成器、迭代器等
  17. RN-环境配置
  18. python实战案例--银行系统
  19. Azure系列2.1.3 —— BlobEncryptionPolicy
  20. 【Python】使用torrentParser1.03对多文件torrent的分析结果

热门文章

  1. Dialog 不能全屏,左右有间距解决方案
  2. struts2笔记07-action扩展名
  3. [原创]linux简单之美(一)
  4. 9.java.lang.ClassCastException
  5. [Leetcode][Python]29: Divide Two Integers
  6. poj1658
  7. poj1528---(数论)
  8. Java面试题之五
  9. 不要再坑人啦!NSOperation才是苹果推荐使用的多线程技术!
  10. HTML系列(八):表格