本文所用到的环境:
- 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
参考链接: