JSP 生命周期
翻译、编译、初始化、服务、销毁五个阶段,以及 jspInit / jspDestroy。
五个阶段
| Stage | What happens | Count |
|---|---|---|
| Translation | .jsp → _jsp.java | 首次或文件改动后 |
| Compilation | _jsp.java → .class | 同上 |
| Initialisation | jspInit() | 1 |
| Service | _jspService() | 每个请求 |
| Destruction | jspDestroy() | 1 |
翻译出来的类
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase {
// 你在 JSP 里声明的 <%! %> 成员会变成类的字段
public void _jspInit() { }
public void _jspDestroy() { }
public void _jspService(HttpServletRequest request,
HttpServletResponse response) {
// 隐式对象在这里被赋值
// 你的 HTML 变成 out.write("...")
// 你的 <% %> 变成普通语句
// 你的 <%= %> 变成 out.print(...)
}
}That is why request, session and out are available in a JSP: they are local variables of _jspService, assigned by the container at the top of the method.
jspInit 与 jspDestroy
<%!
private java.time.LocalDateTime startedAt;
@Override
public void jspInit() {
startedAt = java.time.LocalDateTime.now();
getServletContext().log("页面已初始化");
}
@Override
public void jspDestroy() {
getServletContext().log("页面已销毁");
}
%>
<p>本页面初始化于:<%= startedAt %></p><%! %> declares class members, initialised once and shared by every request — fine for config, constants and read-only references, never for per-request data.
<%! %> is a concurrency bug by construction; int counter plus <% counter++ %> is the classic bad example.首次访问为什么慢
The first request pays translation, compilation and class loading — hundreds of milliseconds to seconds; later requests hit the cache. To avoid a slow first visit, precompile via jsp-config in web.xml or the JSPC tool at build time.
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>五个阶段
| 阶段 | 发生什么 | 次数 |
|---|---|---|
| 翻译 | .jsp → _jsp.java | 首次或文件改动后 |
| 编译 | _jsp.java → .class | 同上 |
| 初始化 | jspInit() | 1 |
| 服务 | _jspService() | 每个请求 |
| 销毁 | jspDestroy() | 1 |
翻译出来的类
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase {
// 你在 JSP 里声明的 <%! %> 成员会变成类的字段
public void _jspInit() { }
public void _jspDestroy() { }
public void _jspService(HttpServletRequest request,
HttpServletResponse response) {
// 隐式对象在这里被赋值
// 你的 HTML 变成 out.write("...")
// 你的 <% %> 变成普通语句
// 你的 <%= %> 变成 out.print(...)
}
}这就是为什么 JSP 里能直接用 request、session、out——它们是 _jspService 方法的局部变量,由容器在方法开头赋值。
jspInit 与 jspDestroy
<%!
private java.time.LocalDateTime startedAt;
@Override
public void jspInit() {
startedAt = java.time.LocalDateTime.now();
getServletContext().log("页面已初始化");
}
@Override
public void jspDestroy() {
getServletContext().log("页面已销毁");
}
%>
<p>本页面初始化于:<%= startedAt %></p><%! %> 声明的是类的成员,只初始化一次,所有请求共享——所以适合放配置、常量、只读引用,绝不放请求数据。
<%! %> 里写可变字段等于给自己埋并发 bug;<%! %> 里放 int counter 然后 <% counter++ %> 是最经典的错误示范。首次访问为什么慢
第一次请求要经历翻译、编译、加载三步,通常几百毫秒到几秒。之后就走缓存。想避免用户第一次访问就卡,可以在 web.xml 里配 jsp-config 预编译,或在构建时用 JSPC 提前编译。
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>