调试tomcat9.0.19源码
本文所用到的环境:
- IntelliJ IDEA
- Apache Maven 3.3.9
- jdk1.8
1. 查看Tomcat版本
$TOMCAT_HOME\bin\version.bat
D:\Program_Files\apache-tomcat-9.0.19\bin>version.bat
Using CATALINA_BASE: "D:\Program_Files\apache-tomcat-9.0.19"
Using CATALINA_HOME: "D:\Program_Files\apache-tomcat-9.0.19"
Using CATALINA_TMPDIR: "D:\Program_Files\apache-tomcat-9.0.19\temp"
Using JRE_HOME: "D:\Program Files\Java\jdk1.8.0_77"
Using CLASSPATH: "D:\Program_Files\apache-tomcat-9.0.19\bin\bootstrap.jar;D:\Program_Files\apache-tomcat-9.0.19\bin\tomcat-juli.jar"
Server version: Apache Tomcat/9.0.19
Server built: Apr 12 2019 14:22:48 UTC
Server number: 9.0.19.0
OS Name: Windows 10
OS Version: 10.0
Architecture: amd64
JVM Version: 1.8.0_77-b03
JVM Vendor: Oracle Corporation
D:\Program_Files\apache-tomcat-9.0.19\bin>
这里使用的是JDK1.8,如果不是JDK1.8,则需要查看tomcat的兼容,选择适合自己的版本
2. 下载源码
根据自己的Tomcat版本,下载对应的源码
选择对应版本的tomcat,这里我使用的tomcat9,选择Archives
下载完成后解压,apache-tomcat-9.0.19-src
3. 创建项目结构
在D盘创建“D:\tomcat9.0.19”文件夹,然后将解压后的文件拷贝到该目录中,同时创建“catalina-home”目录
拷贝“apache-tomcat-9.0.19-src\conf”,到“catalina-home”目录下
创建“D:\tomcat9.0.19\pom.xml”文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>Tomcat9.0.19</artifactId>
<name>Tomcat 9.0 Study</name>
<version>1.0</version>
<packaging>pom</packaging> <modules>
<module>apache-tomcat-9.0.19-src</module>
</modules>
</project>
创建“D:\tomcat9.0.19\apache-tomcat-9.0.19-src\pom.xml”文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>Tomcat9.0</artifactId>
<name>Tomcat9</name>
<version>9.0</version> <build>
<finalName>Tomcat9</finalName>
<sourceDirectory>java</sourceDirectory>
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build> <dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-apache-log4j</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-commons-logging</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>javax.xml.rpc</groupId>
<artifactId>javax.xml.rpc-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
</project>
最终的目录结构为:
Administrator@git MINGW64 /d/tomcat9.0.19
$ ls -l total 6
drwxr-xr-x 1 Administrator 197121 0 3月 30 17:50 apache-tomcat-9.0.19-src
drwxr-xr-x 1 Administrator 197121 0 3月 30 17:41 catalina-home
-rw-r--r-- 1 Administrator 197121 533 3月 30 17:27 pom.xml
Administrator@git MINGW64 /d/tomcat9.0.19
$ ls -l catalina-home/ total 8
drwxr-xr-x 1 Administrator 197121 0 3月 30 17:38 conf
4. 将该项目导入到IDEA中
5. 编译
由于这里使用是JDK1.8,所以需要注释掉这些代码,否则容易报错:
编译:
6. 运行
1)创建Application
vm option 配置:
-Dfile.encoding=UTF-8 -Dcatalina.home=catalina-home -Dcatalina.base=catalina-home -Djava.endorsed.dirs=catalina-home/endorsed -Djava.io.tmpdir=catalina-home/temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=catalina-home/conf/logging.properties
在Bootstrap的main方法当中加入测试代码:
System.out.println("======================Tomcat Study======================");
下面是部分启动日志:
======================Tomcat Study======================
30-Mar-2020 17:38:32.009 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
30-Mar-2020 17:38:32.017 警告 [main] org.apache.catalina.startup.ClassLoaderFactory.validateFile Problem with directory [D:\tomcat9.0.19\catalina-home\lib], exists: [false], isDirectory: [false], canRead: [false]
但是启动日志会有乱码,解决方法:
为何会有乱码:
原因:一般是中文编码不匹配导致;经跟踪,发现是ResourceBundle读取org\apache\catalina\startup\LocalStrings_zh_CN.properties文件时没有用utf8解码导致;
解决:修改org.apache.tomcat.util.res.StringManager类中的getString函数;
由if (bundle != null) {
str = bundle.getString(key);
}
改为
if (bundle != null) {
str = new String(bundle.getString(key).getBytes(“ISO-8859-1”), “UTF-8”);
}
再次运行,一切正常
2)实验
- 在“catalina-home\webapps”目录中创建“demo\index.html”文件:
<html>
<head><title>Tomcat9.0.19_Test</title></head>
<body>
<h1>Hello World!!!</h1>
</body>
</html>
访问: http://127.0.0.1:8080/demo/
- 在“catalina-home\webapps”目录中创建“demo\index.jsp”文件:
<%@ page contentType="text/html;charset=utf-8" pageEncoding="utf-8"%>
<html>
<head><title>Test-title</title></head>
<body>
<%
request.setCharacterEncoding("utf-8");
String method = request.getMethod() ; // 取得提交方式
String ip = request.getRemoteAddr() ; // 取得客户端的IP地址
String path = request.getServletPath() ; // 取得访问路径
String contextPath1= request.getContextPath() ; // 取得上下文资源名称
String contextPath2 = getServletContext().getContextPath() ;// 取得上下文资源名称
String realPath=getServletContext().getRealPath("/");//取得虚拟目录所对应的真实路径
%>
<h3>请求方式:<%=method%></h3>
<h3>IP地址:<%=ip%></h3>
<h3>访问路径:<%=path%></h3>
<h3>上下文名称1:<%=contextPath1%></h3>
<h3>上下文名称2:<%=contextPath2%></h3>
<h3>真实路径:<%=realPath%></h3>
</body>
</html>
访问: http://127.0.0.1:8080/demo/index.jsp
调试程序
index.jsp在被编译后会生成“index_jsp.java”和“index_jsp.class”文件,位置如下:
Administrator@git MINGW64 /d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
$ pwd
/d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
Administrator@git MINGW64 /d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
$ ls index_jsp.java
index_jsp.java
Administrator@git MINGW64 /d/tomcat9.0.19/catalina-home/work/Catalina/localhost/demo/org/apache/jsp
$ ls index_jsp.class
index_jsp.class
下面是“index_jsp.java”文件的内容:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/9.0.x-dev
* Generated at: 2020-03-30 10:23:23 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP åªå
è¸ GETãPOST æ HEADãJasper è¿å
è¸ OPTIONS");
return;
}
}
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html;charset=utf-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head><title>Test-title</title></head>\r\n");
out.write("<body>\r\n");
request.setCharacterEncoding("utf-8");
String method = request.getMethod() ; // 取得提交方式
String ip = request.getRemoteAddr() ; // 取得客户端的IP地址
String path = request.getServletPath() ; // 取得访问路径
String contextPath1= request.getContextPath() ; // 取得上下文资源名称
String contextPath2 = getServletContext().getContextPath() ;// 取得上下文资源名称
String realPath=getServletContext().getRealPath("/");//取得虚拟目录所对应的真实路径
out.write("\r\n");
out.write("<h3>请求方式:");
out.print(method);
out.write("</h3>\r\n");
out.write("<h3>IP地址:");
out.print(ip);
out.write("</h3>\r\n");
out.write("<h3>访问路径:");
out.print(path);
out.write("</h3>\r\n");
out.write("<h3>上下文名称1:");
out.print(contextPath1);
out.write("</h3>\r\n");
out.write("<h3>上下文名称2:");
out.print(contextPath2);
out.write("</h3>\r\n");
out.write("<h3>真实路径:");
out.print(realPath);
out.write("</h3>\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
这些是JSP的内置对象,另外还有request和response内置对象,在方法参数上
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
下面是它们的赋值操作:
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
能够看到它们都是通过pageContext的对应方法来进行赋值的。
实际上我们也可以在界面上将这些内置对象打印出来:
test.jsp
<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ page import="java.util.*"%> <!-- 导入java.util包 -->
<html>
<head><title>PageScope</title></head>
<body>
<h4>request:<%=request%></h4>
<h4>session:<%=session%></h4>
<h4>application:<%=application%></h4>
<h4>response:<%=response%></h4>
<h4>out:<%=out%></h4>
<h4>config:<%=config%></h4>
<h4>page:<%=page%></h4>
<h4>pageContext:<%=pageContext%></h4>
<h4>getException:<%=pageContext.getException()%></h4>
<h4>getPage:<%=(pageContext.getPage()==page)%></h4>
<h4>getRequest:<%=(pageContext.getRequest()==request)%></h4>
<h4>getResponse:<%=(pageContext.getResponse()==response)%></h4>
<h4>getSession:<%=(pageContext.getSession()==session)%></h4>
<h4>getServletConfig:<%=(pageContext.getServletConfig()==config) %></h4>
<h4>getServletContext:<%=(pageContext.getServletContext()==application)%></h4>
</body>
</html>
运行结果:
再回到调试的问题上来,想要调试“index_jsp.java”,需要创建“org/apache/jsp”文件夹,然后将该文件移动到该文件夹下,打上断点就可以开始调试了:
3)注意
默认情况下解析JSP会报错
- 现象:
Type Exception Report
Message java.lang.NullPointerException
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
org.apache.jasper.JasperException: java.lang.NullPointerException
org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:638)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:514)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
Root Cause
java.lang.NullPointerException
org.apache.jsp.index_jsp._jspService(index_jsp.java:165)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:476)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
Note The full stack trace of the root cause is available in the server logs.
问题分析和解决方法
原因是我们直接启动org.apache.catalina.startup.Bootstrap的时候没有加载org.apache.jasper.servlet.JasperInitializer,从而无法编译JSP。解决办法是在tomcat的源码org.apache.catalina.startup.ContextConfig中手动将JSP解析器初始化:
————————————————
版权声明:本文为CSDN博主「yehong1225」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yekong1225/article/details/81000446再次测试正常
4)切换到Tomcat主目录,将VM options修改为如下配置即可
-Dcatalina.home=apache-tomcat-9.0.19-src -Dcatalina.base=apache-tomcat-9.0.19-src
-Djava.endorsed.dirs=apache-tomcat-9.0.19-src/endorsed -Djava.io.tmpdir=apache-tomcat-9.0.19-src/temp
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=apache-tomcat-9.0.19-src/conf/logging.properties
参考链接:
调试tomcat9.0.19源码的更多相关文章
- Ubuntu12.04编译Android4.0.1源码全过程-----附wubi安装ubuntu编译android源码硬盘空间不够的问题解决
昨晚在编译源码,make一段时间之后报错如下: # A fatal error has been detected by the Java Runtime Environment: # # SIGSE ...
- openvswitch2.11.0修改源码后重新编译(2)
一:前提 已经正常安装了SDN环境(mininet和openswitch2.11.0和Ryu) 使用前面教程安装环境SDN实验---使用git安装Mininet (一)测试ovs是否正常使用 1.ry ...
- MySQL学习之路 一 : MySQL 5.7.19 源码安装
MySQL 5.7.19 源码安装 查看系统: # cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core) 安装依赖包 # yum - ...
- windows 下使用 mingw编译器 调试时 无法跟进源码
windows 下使用 mingw编译器 调试时 无法跟进源码 最近在公司使用QT 开发,官方在线下载的 安装的QT mingw 都是没有debug版本的 由于没有debug版本动态库 所以你调试的时 ...
- 一起学习jQuery2.0.3源码—1.开篇
write less,do more jQuery告诉我们:牛逼的代码不仅精简而且高效! 2006年1月由美国人John Resig在纽约的barcamp发布了jQuery,吸引了来自世界各地众多Ja ...
- XposedNoRebootModuleSample 不需要频繁重启调试的Xposed 模块源码例子
XposedNoRebootModuleSample(不需要频繁重启调试的Xposed 模块源码例子) Xposed Module Sample No Need To Reboot When Debu ...
- 英蓓特Mars board的android4.0.3源码编译过程
英蓓特Mars board的android4.0.3源码编译过程 作者:StephenZhu(大桥++) 2013年8月22日 若要转载,请注明出处 一.编译环境搭建及要点: 1. 虚拟机软件virt ...
- jQuery 2.0.3 源码分析Sizzle引擎解析原理
jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...
- Android L(5.0)源码之手势识别onTouchEvent
onTouchEvent同样也是在view中定义的一个方法.处理传递到view 的手势事件.通过MotionEvent的getAction()方法来获取Touch事件的类型,类型包括ACTION_DO ...
随机推荐
- MySQL(二)表的操作与简单数据操作
六大约束:主键约束.外键约束.非空约束.唯一约束.默认约束.自动增加 1.not null非空 2.defaul默认值,用于保证该字段的默认值 ; 比如年龄:1900-10-10 3.primar k ...
- UWP 自定义密码框控件
1. 概述 微软官方有提供自己的密码控件,但是控件默认的行为是输入密码,会立即显示掩码,比如 *.如果像查看真实的文本,需要按查看按钮. 而我现在自定义的密码控件是先显示你输入的字符2s,然后再显示成 ...
- # SpringBoot-环境搭建
SpringBoot-环境搭建 标签(空格分隔): java,SpringBoot 1.创建Maven工程 2.编写pom文件 <parent> <groupId>org.sp ...
- Dockerfile镜像优化,减小镜像
前言镜像的优化注意几条: 选择最精简的基础镜像减少镜像的层数清理镜像构建的中间产物注意优化网络请求尽量去用构建缓存使用多阶段构建镜像接下来我们以rhel7镜像构建容器,并在容器中安装nginx的源码包 ...
- IO流——Properties类、序列化流、反序列化流、打印流、commons-IO
一. Properties类 1. Properties类介绍 Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串 ...
- Django开发之Datetime类型JSON序列化时报错
前提回顾 在进行django开发view视图时,如果数据库字段是 datetime类型,在JSON序列化返回时,会出现异常 异常现象 TypeError: Object of type datetim ...
- PHP unixtojd() 函数
------------恢复内容开始------------ 实例 把 Unix 时间戳转换为儒略日计数: <?phpecho unixtojd();?> 运行实例 » 定义和用法 uni ...
- UOJ 422 [集训队作业2018] 小Z的礼物 min-max容斥 期望 轮廓线dp
LINK:小Z的礼物 太精髓了 我重学了一遍min-max容斥 重写了一遍按位或才写这道题的. 还是期望多少时间可以全部集齐. 相当于求出 \(E(max(S))\)表示最后一个出现的期望时间. 根据 ...
- 4.22 省选模拟赛 三元组 manacher 回文自动机
容易发现可以枚举j 那么只需要计算出 l~j这段是回文串的l的和 以及j+1~r这段是回文串的r的和. 可以manacher 之后想要求出以j为右端点的回文串左端点的和 这个东西我们通过某个点为中心的 ...
- 精讲RestTemplate第2篇-多种底层HTTP客户端类库的切换
本文是精讲RestTemplate第2篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 RestTemplate只是对其他的HTTP客 ...