阅读书目,清华大学出版社 《Servlet 与 JSP 核心编程》第二版Marty Hall Larry Brown著
记录读书体会,笔记内容掺杂个人理解,必定会有大量不妥甚至不对的地方,随着知识的积累会不断修正错误之处
1 原书第 7 页,知识点
JSP 文档不过是编写 Servlet 的另一种方式,JSP 页面会被翻译成 servlet,servlet 会被编译,在请求期间运行的就是 servlet。
2 原书第 48 页,知识点
servlet 是运行在 Web 服务器或应用服务器上的程序,它担当 Web 浏览器或者其他 HTTP 客户程序与 HTTP 服务器上的数据库或应用程序之间的中间层。
3 积累
以 Servlet 为例
1 servlet 规范由 JCP(Java Community Process) 制定的,提供 api 接口,但不一定提供接口 api 的实现源码,这里所说的“实现”指的是 相关接口 api 的源代码,例如 javax.servlet, javax.servlet.http 等包下面类(Servlet、ServletConfig、HttpServletRequest类等)的源代码,因为编译时需要这些类。
2 以 Tomcat 为例,tomcat 提供的源代码包中包括了所有 JCP 组织Servlet规范 api 中接口、类的实现。
3 除实现 servlet api 中定义的接口、类之外,tomcat 还需要完成具体接口的实现,例如 api 中定义的 Interface HttpServletRequest 接口,tomcat 需要实现此接口所有的方法。
3 原书第 220 页,知识点
原书内容
JSP 页面需要转换成 Servlet,Servlet 在编译后,载入到服务器的内容中,初始化并执行。但是每一步发生在什么时候?要回答这个问题,要牢记以下两点:
1 JSP 页面仅在修改后第一次访问时,才会被转换成 Servlet 并进行编译
2 载入到内存中,初始化和执行遵循 Servlet 一般的规则
4 原书第 240 页,知识点
原书内容
使用声明
使用 JSP 声明可以将方法或者字段的定义插入到 servlet 类的注定一体中(位于对请求进行处理的 _jspService 方法之外)。声明形式如下:
<%! Filed or Method Definition %>
由于声明并不产生输出,所以,它们一般与 JSP 或者 scriptlet 结合使用。基本上,JSP 声明可以包含字段(实例变量)定义、方法定义、内部类定义、甚至是静态初始块。任何可以被方法在类中定义中但在已有方法之外的内容。然而在实践中,声明几乎总是包含字段或方法定义。
但要注意,不要是使用 JSP 声明覆盖 servlet 的标准生命周期方法(service、detory、init 方法)。由 JSP 转换成的 servlet 已经使用这些方法。由于对 service 方法的调用会自动分派给 _jspService(也就是表达式和 scriptlet 生成的代码防止的地方),因而声明不需要访问 service 方法。但对于初始化和清理工作,可以覆盖 jspInit 和 jspDestory 方法(在有 JSP 转换而生成的 servlet 中,标准的 init 和 destory 方法会保证调用这两个方法)。
除了覆盖 jspInit 和 jspDestory 方法,JSP 声明在定义方法方面的功用是值得质疑的。请将这些方法移到单独的类中,这样更有利于他们的编写(这是由于你可以使用 java 开发环境进行编写工作,而不是在类 HTML 环境中进行)、测试(不需要运行服务器)、调试(编译警告能够给出正确的行号;查看表转输出不需要使用其他技巧)和重用(许多不同的 JSP 页面可以使用同一个实用工具类)。但是,很快我们会看到,应用 JSP 声明定义实例变量(字段)可以赋予您一些应用单独的使用工具类不已完成的功能,它为跨请求持续性数据的存储提供一席之地。
我的体会
1 JSP 声明中不可以声明 service、init 和 destory 方法,更具体的说应该是不能重写上述生命周期方法。接下来看一下为什么,下面是 index.jsp 转化成 java 类的样子。
public final class index_jsp
extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent
public abstract class HttpJspBase
extends HttpServlet
implements HttpJspPage
index_jsp 继承自 HttpJspBase,HttpJspBase 继承自 HttpServlet ,毫无疑问 final class index_jsp 是一个 servlet
public final void init(ServletConfig config)
throws ServletException {
super.init(config);
jspInit();
_jspInit();
}
public final void destroy() {
jspDestroy();
_jspDestroy();
}
public final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
_jspService(request, response);
}
上面三个方法是在 HttpJspBase 类中的定义,而且全部定义为 final 类型,所以子类 index_jsp 中无法重写这些方法。
index_jsp 中有两组下面的方法
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
}
public void jspInit() {
}
public void _jspDestroy() {
}
public void jspDestroy() {
}
1 JSP 声明中无法重写 service、init 和 destory 方法,因为其父类实现类实现了上述方法,并将其定义为 final类型。
2 如果 JSP 中需要初始化操作和类卸载时做出相应的响应,那么需要通过声明的方式重写 jspInit()和 jspDestroy()方法,而不是 _jspInit()和_jspDestroy()方法。
3 JSP 跟 servlet 没有任何差别,也是单例模式,所以通过声明的方式定义的成员变量可以实现跨请求共享访问,但是需要自己提供同步机制。
5 原书第 259页,知识点
原书内容
isThreadSafe 属性控制有 JSP 页面生成的 servlet 是否允许并行访问(默认)。可以此采用以下两种形式
<%@ page isThreadSafe="true" %> <%-- default --%>
表示该 JSP 页面是线程安全的,不需要并发控制
<%@ page isThreadSafe="false" %>
表示该 JSP 页面不是线程安全的,需要并发控制
1 Jsp 页面 page 指令中的 isThreadSafe属性值设置为 false 时,生成的 servlet 类会实现 SingleThreadModel 接口
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
SingleThreadModel
2 Servlet 和 Jsp 规范已经废弃了这种并发控制安全的做法,主要原因是 SingleThreadModel 导致 jsp 页面被请求线程顺序访问,高流量的 JSP 页面可能会很慢。
6 原书第 262 页,知识点
- jsp:include
- include 指令
- jsp:forward
jsp:include 动作:在主页面被请求时,将次级页面的输出包含进来,发生在页面请求期间。
可以将任何内容插入到 JSP 的输出中
- HTML 页面的内容
- 纯文本文档的内容
- JSP 页面的输出
- servlet 的输出
include.jsp 源代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
1 <jsp:include page="/WEB-INF/included_page.jsp" flush="false" />
<br>
2 <jsp:include page="/WEB-INF/included_page.txt" flush="false" />
<br>
3 <jsp:include page="/WEB-INF/included_page.html" flush="false" />
<br>
4 <jsp:include page="/included_page_Servlet" flush="true" />
</body>
</html>
请求 include.jsp 时,include.jsp 转换成 servlet 的部分源代码
out.write(" \r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("\t<body>\r\n");
out.write("\t\t1 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/WEB-INF/included_page.jsp", out, false);
out.write("\r\n");
out.write("\t\t<br>\r\n");
out.write("\t\t2 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/WEB-INF/included_page.txt", out, false);
out.write("\r\n");
out.write("\t\t<br>\r\n");
out.write("\t\t3 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/WEB-INF/included_page.html", out, false);
out.write("\r\n");
out.write("\t\t<br>\r\n");
out.write("\t\t4 ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/included_page_Servlet", out, true);
out.write("\r\n");
out.write("\t</body>\r\n");
out.write("</html>");
浏览器显示的结果
1 included_page:Hello World!
2 included_page.txt:Hello World.
3 included_page.html:Hello World.
4 included_page_Servlet:Hello World.
html 页面的原代码
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
1
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<span>included_page:Hello World!</span>
</body>
</html>
<br>
2 included_page.txt:Hello World.
<br>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
included_page.html:Hello World.
</body>
</html>
<br>
4 included_page_Servlet:Hello World.
</body>
</html>
- jsp:include 发生在主页面请求期间,在请求期间主页面通过 include
org.apache.jasper.runtime.JspRuntimeLibrary.include(ServletRequest request,
ServletResponse response,
String relativePath,
JspWriter out,
boolean flush)
方法将请求控制权暂时转交给其他的子页面,子页面将数据写入同一个输入流,写完之后,控制权再次返回到主页面,主页面继续往输入流中写入数据。
2. 但要注意,不能再任何子页面中将输出流关闭 out.close(),否则主页面无法继续往输出流中写入数据,导致j ava.io.IOException: Stream closed 异常。
3. jsp:include 提供了一个 flush 属性,flush=”true”时,代表请求转交子页面之前,完成输出流缓存中数据的输出,即先把主页面输出的数据发送到客户端。
public static void include(ServletRequest request,
ServletResponse response,
String relativePath,
JspWriter out,
boolean flush)
throws IOException, ServletException {
if (flush && !(out instanceof BodyContent))
out.flush();
- jsp:include 动作还可以包含
<jsp:param name="bgColor" value="YELLOW">
假定调用主页面的请求 URL http://host/path/MainPage.jsp?fgColor=RED。下面的列表汇总了这种情况下各种 getParameter 的调用结果。
在主页面
- request.getParameter(“fgColor”)返回 RED
- request.getParameter(“bgColor”)返回 null
在子页面 - request.getParameter(“fgColor”)返回 RED
- request.getParameter(“bgColor”)返回 YELLOW
include 指令:将被包含文件的内容逐字节插入到主页面中去,然后合并为单个 JSP 页面进行转化处理,而子 jsp 页面不会被转化成相应的 servlet 类。include 指令在页面转换期间被激活。
include 指令
<%@ include file="" %>
include.jsp 源代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%@ include file="/WEB-INF/included_page.jsp" %>
</body>
</html>
included_page.jsp 源代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%! private int i=2; %>
<%=i %>
<span>included_page:Hello World!</span>
</body>
</html>
include.jsp 转换成的 servlet 源代码
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class include_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private int i=2;
private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
private static java.util.List _jspx_dependants;
static {
_jspx_dependants = new java.util.ArrayList(1);
_jspx_dependants.add("/WEB-INF/included_page.jsp");
}
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.AnnotationProcessor _jsp_annotationprocessor;
public Object getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
}
public void _jspDestroy() {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
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("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("\t<body>\t\r\n");
out.write("\t");
out.write("\r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\t\t");
out.write("\r\n");
out.write("\t\t");
out.print(i );
out.write("\r\n");
out.write("\t\t<span>included_page:Hello World!</span>\r\n");
out.write("</body>\r\n");
out.write("</html>");
out.write("\r\n");
out.write("\t</body>\r\n");
out.write("</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else log(t.getMessage(), t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
接下来直接将 included_page.jsp 源代码(去掉 指令)替换掉 include 指令
<%@ include file="/WEB-INF/included_page.jsp" %>
新的 include.jsp 代码如下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
// ----------------------included_page.jsp 的内容
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%! private int i=2; %>
<%=i %>
<span>included_page:Hello World!</span>
</body>
</html>
// ----------------------included_page.jsp 的内容
</body>
</html>
重新请求 include.jsp ,服务器完成 jsp 到 servlet 的转换,转换结果如下
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class include_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private int i=2;
private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
private static java.util.List _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.AnnotationProcessor _jsp_annotationprocessor;
public Object getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
}
public void _jspDestroy() {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
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("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("\t<body>\t\r\n");
out.write("\t\r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\t\t");
out.write("\r\n");
out.write("\t\t");
out.print(i );
out.write("\r\n");
out.write("\t\t<span>included_page:Hello World!</span>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\t</body>\r\n");
out.write("</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else log(t.getMessage(), t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
与上面通过 <%@ include file=”/WEB-INF/included_page.jsp” %> include 指令一样,说明 include 指令发生在在页面转换期间。
< jsp:forward page=”” >动作:发生在请求期间,主页面把请求转让给子页面,子页面会清空数据流中主页面已经写入的内容,然后将子页面数据写入数据流,发送给客户端,请求控制权不会再还给主页面。
- 使用 _jspx_page_context.forward( )方法完成请求的转让
- 如果主页面进行了主动 flush 或者被动(buffer 已满)flush,那么会导致 forward 失败,子页面在清空输出流时,发现输出流已经输出了一些内容。
public final void clear() throws IOException {
if ((bufferSize == 0) && (out != null))
// clear() is illegal after any unbuffered output (JSP.5.5)
throw new IllegalStateException(
getLocalizeMessage("jsp.error.ise_on_clear"));
if (flushed)
throw new IOException(
getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
ensureOpen();
nextChar = 0;
}
- 同样,如果你在主页面设置 <%@ page buffer=”0kb”> 也会导致 forward 失败
forward.jsp 源代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>forward.jsp</title>
</head>
<body>
<%-- <% out.flush(); %> --%>
<%-- <%@ page buffer="0kb" %>--%>
<jsp:forward page="WEB-INF/forward_page.jsp" />
</body>
</html>
如果去掉下面任何一行代码的注释, forward 会产生异常。
<%-- <% out.flush(); %> --%>
<%-- <%@ page buffer="0kb" %>--%>
forward.jsp 转换成的 servlet 部分源代码
out.write('\n');
out.write(' ');
if (true) {
_jspx_page_context.forward("WEB-INF/forward_page.jsp");
return;
}
out.write("\r\n");
out.write("</body>\r\n");
- jsp:include 动作在请求期间激活,主页面将请求暂时转交给子页面,子页面把数据写入到输出流,然后再把请求还给主页面,主页面继续执行其他操作,但要注意不能再任何子页面将输出流关闭 out.close();
- jsp:forward 动作在请求期间激活,主页面将请求永久的转让给子页面,子页面完成数据的写入和返回,注意主页面不能有任何数据 flush,已写入输出流 out ,但未 flush 的数据会在子页面写入数据之前被清空掉。
- <%@ include file=”“>指令在 jsp 转换期间被激活,将子页面的数据全部复制到主页面中,然后转换成一个 servlet 类,所以子页面的代码会影响主页面的代码。
原书 304 页
初始的请求由 servlet 来处理,servlet 调用商业逻辑或者数据处理代码,并创建 bean 来表示相应的结果(即模型),然后,servlet 确定哪一个 JSP 页面适合于表达这些特定的姐结果,并将请求转发至相应的页面(JSP 压面即为视图),由 servlet 确定那个商业逻辑适用,应该用哪个 JSP 页面表达结果(srevlet 就是控制器)。
原书 325
jsp 中的 java 脚本包括:jsp 声明(<%! …… %>)、jsp 表达式(<%= …… %>)和 java 代码段(<% …… %>)。
- EL表达式:为了计算和输出存储在标准位置的 Java 对象的值,JSP 2.0 引入了一种简洁的语言,表达式语言(Expression Language,EL)。
- jsp 指令:以非 java 代码的形式控制 Jsp 页面生成的 Servlet 的整体结构。
- jsp 标签:JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护。
- 可以看出不管是 jsp 指令还是 jsp 标签,还是 EL 表达式都有一个共同的目的,就是在 jsp 页面中避免直接使用 java 代码,以更友好的方式面向前端人员,所有 优化后的标签、指令和EL表达式都是通过后端的 servlet 实现的。