JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"
-
JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"
- 每博一文案
- 1. JSP 概述
- 2. 第一个 JSP 程序
- 3. JSP 的本质就是 Servlet
-
4. JSP 的基础语法
- 4.1 在 JSP 文件中直接编写文字
-
4.2 在JSP中编写Java程序 <% %> 与 <%! %>
- 4.2.1 <% %>
- 4.2.2 <%! %>
- 4.3 通过JSP当中的 <%= %>向浏览器前端输入 Java变量
- 4.4 在JSP 中的专业注释
- 4.5 JSP基础语法总结:
- 5. JSP的指令
- 6. JSP的九大内置对象
- 7. 使用Servlet + JSP完成oa项目的改造
- 8. 补充:
- 9. 总结:
- 10. 最后:
每博一文案
活明白的人,一生只做好了这两件事:
每个瞬间都充满了选择和承担,就算面前是一座独木桥,也必须选择是前进后退,亦或是留在原地此时此刻你所经历的一切。
这是过往无数个选择后的结果,哪些小的选择汇聚在了一起,最终成了我们今天的时光。
其实,活明白的人一生只做好了两件事看,一是选择,二是承担。常听人争论选择和努力哪个更重要。
其实,努力并不是选择的对比面,而是拥有选择的基本条件。不努力的人往往连选择的资格都没有,努力是为了,
更好的选择。正如马伯庸说:所谓的选择只是努力所赋予人的一种资格。
所有的一夜成名,刹那的焰火,实际是过往今年默默努力埋下的伏笔,因此这里说的选择不是投机取巧的小聪明。
而是积淀后的深思熟虑。常言道:选择不对,努力白费。
李兰娟院士在为了汕头大学的毕业学生送上给予时,表示:不管是在生活还是事业上,有很多外部变化和环境因素是我们
所无法选择的。我们可以选择的是在每一个人生路口要做出怎样的选择,以及如何勇敢的前行。
我们所有的人都在共享一段岁月,却在不同的选择中分道扬镳,因为人生最重要的除了努力还有选择。
当你被生活的疲惫裹挟着,被环境的艰难牵制着,这时你要么被生活牵着鼻子走,要么接收痛苦与打击,抗住一场场暴风雪,主动迎击一地鸡毛的琐碎,你要做的不是抱怨生活的不公,而是迈出步子,夺下生活的主导权,做出选择了。
一种选择就是一种代价,不同的选择造就了不同的人生,人打从生下来就面临着很多种未知的可能。
未来都是一张白纸,任由自己去上色作画。有些人完成地很好,也有人画得一团糟,人生的一万种可能好的,不好的
都是有认真选择好。
每一支画笔,在每次选择时都要坚持原则,便是对自己的人生负责。
—————— 《一禅心灵庙语》
1. JSP 概述
JSP(全称JavaServer Pages),sun公司主导的一种动态网页技术,JSP在服务端运行,可以响应客户端的请求,根据请求内容动态的生成HTML、XML或其他格式文档的Web网页然后返回请求者。在JSP页面可以嵌入Java代码,JSP文件在运行时会被其编译器转换成更原始的Servlet代码,然后再由Java编译器来编译成能快速执行的二进制机器码。
2.特点:
- 能以模板化的方式简单、高效地添加动态网页内容。
- 可利用JavaBean和标签库技术复用常用的功能代码。
- 有良好的工具支持。
- 继承了Java语言的相对易用性。
- 继承了Java的跨平台优势,实现“一次编写,处处运行”。
- 页面中的动(控制变动内容的部分)/静(内容不需变动的部分)区域以分散但又有序的形式组合在一起,方便开发维护。
- 可与其它企业级Java技术相互配合。JSP可以只专门负责页面中的数据呈现,实现分层开发。
3.JSP页面组成:
普通的 HTML 标记符。
- JSP 标签,如指令标签、动作标签。
- 变量和方法的声明。
- Java 程序段。
- Java 表达式。
2. 第一个 JSP 程序
这里给一个小的建议,大家在阅读如下,文章时,可以带着一个这样的问题:JSP 是什么 ? 去阅读文章,有助于后面的内容上的阅读理解。
WEB-INF目录之外创建一个 index.jsp
文件,然后这个文件中没有任何内容。注意 了:我们对于这个jsp 文件当中并没有编写任何的内容,一个字符,一个标点都可以,就是一个空白的。如下显示的:
http://127.0.0.1:8080/servlet14/index.jsp 我们部署的项目的路径:具体显示如下。
重点: 实际上我们访问的以上的这个:index.jsp
,底层执行的是:index_jsp.class
这个java程序。我们可以在我们本地电脑上访问到该生成是 index_jsp.class,index_jsp.java
的文件,该生成的对应的.class,.java文件所在的路径是在我们启动 Tomcat 服务器当中提示的,一个 CATALINA_BASE
路径下的 C:\Users\huo\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\4b6bbfbb-d520-498b-b8f2-090a7ad68f62
index_jsp.java编译生成index_jsp.class
文件。访问index.jsp
,实际上执行的是index_jsp.class
中的方法。
index.jsp 在我本地电脑上生成的 CATALINA_BASE的 路径下的,index_jsp.java,index_jsp.class 的文件。 C:\Users\huo\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\4b6bbfbb-d520-498b-b8f2-090a7ad68f62\work\Catalina\localhost\servlet14\org\apache\jsp
如下是我们的一个 index.jsp(空内容)
被翻译为 index_jsp.java
文件的内容如下:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/10.0.12
* Generated at: 2023-04-21 03:20:48 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.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 jakarta.servlet.jsp.JspFactory _jspxFactory =
jakarta.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("jakarta.servlet";
_jspx_imports_packages.add("jakarta.servlet.http";
_jspx_imports_packages.add("jakarta.servlet.jsp";
_jspx_imports_classes = null;
}
private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
throws java.io.IOException, jakarta.servlet.ServletException {
if (!jakarta.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 jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html";
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(' ';
out.write(' ';
} catch (java.lang.Throwable t {
if (!(t instanceof jakarta.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;
}
}
}
3. JSP 的本质就是 Servlet
从上述我们编写的第一jsp 程序,index.jsp 空内容的,index.jsp访问的时候,会自动翻译生成index_jsp.java,会自动编译生成index_jsp.class,那么index_jsp 这就是一个类。
extends org.apache.jasper.runtime.HttpJspBase 类的,我们查阅其 Tomcat 10 的 HttpJspBase 源码如下:
如下是 HttpJspBase 的源码内容
/*
* Licensed to the Apache Software Foundation (ASF under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"; you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jasper.runtime;
import java.io.IOException;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.jsp.HttpJspPage;
import org.apache.jasper.Constants;
import org.apache.jasper.compiler.Localizer;
/**
* This is the super class of all JSP-generated servlets.
*
* @author Anil K. Vijendran
*/
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {
private static final long serialVersionUID = 1L;
protected HttpJspBase( {
}
@Override
public final void init(ServletConfig config
throws ServletException
{
super.init(config;
jspInit(;
_jspInit(;
}
@Override
public String getServletInfo( {
return Localizer.getMessage("jsp.engine.info", Constants.SPEC_VERSION;
}
@Override
public final void destroy( {
jspDestroy(;
_jspDestroy(;
}
/**
* Entry point into service.
*/
@Override
public final void service(HttpServletRequest request, HttpServletResponse response
throws ServletException, IOException
{
_jspService(request, response;
}
@Override
public void jspInit( {
}
public void _jspInit( {
}
@Override
public void jspDestroy( {
}
protected void _jspDestroy( {
}
@Override
public abstract void _jspService(HttpServletRequest request,
HttpServletResponse response
throws ServletException, IOException;
}
所以:一个index_jsp类就是一个Servlet类。总结就是:一个 index.jsp 文件会被翻译为一个 index_jsp.java 类,而 该 翻译的 index_jsp.java类是 继承
了 org.apache.jasper.runtime.HttpJspBase 类的,而 org.apache.jasper.runtime.HttpJspBase 类 是继承了 HttpServlet 类的。这下逻辑就清晰了,JSP 就是 Servlet
。
- JSP 的生命周期和Servlet的生命周期完全相同。完全就是一个东西。没有任何区别。
- JSP和servlet一样,都是单例的。(假单例,真单例是:其类的构造器是 private 私有化的,而Servlte 的构造器不是 private 私有化的,是公开的。所以为假单例),想要了解更多的,大家可以移步至: 🔜🔜🔜 javaEE Web(Tomcat深度理解 和 Servlet的本质_ChinaRainbowSea的博客-CSDN博客
现在我们可以比较清晰的回答 JSP是什么了 ?
- JSP是java程序。(JSP本质还是一个Servlet)
- JSP是:JavaServer Pages的缩写。(基于Java语言实现的服务器端的页面。)
- Servlet是JavaEE的13个子规范之一,那么JSP也是JavaEE的13个子规范之一。
- JSP是一套规范。所有的web容器/web服务器都是遵循这套规范的,都是按照这套规范进行的“翻译”
- 每一个web容器/web服务器都会内置一个JSP翻译引擎。这里所谓的翻译指的是: 将我们访问的 jsp 文件翻译成 对应的以
.java
后缀结尾的Java文件,再将 该 java 文件编译生成.class
文件。
问题:JSP第一次访问的时候是比较慢的,为什么?
同问: 为什么大部分的运维人员在给客户演示项目的时候,为什么提前先把所有的jsp文件先访问一遍。
- 第一次比较麻烦的地方:
- 要把jsp文件翻译生成java源文件
- java源文件要编译生成class字节码文件
- 然后通过class去创建servlet对象
- 然后调用servlet对象的init方法
- 最后调用servlet对象的service方法。
- 第二次就比较快了,为什么?
- 因为第二次直接调用单例servlet对象的service方法即可。
注意点:
开发JSP的最高境界: 眼前编写的是JSP代码,但是我们心里,脑袋里面当中呈现的是 java代码。
JSP既然本质上是一个Servlet,那么JSP和Servlet到底有什么区别呢?
- Servlet的职责是什么:收集数据。(Servlet的强项是后端的逻辑处理,业务处理,然后链接数据库,获取/收集数据。)
- JSP的职责是什么:展示数据。(JSP的强项是做 前端数据的展示)
4. JSP 的基础语法
上面我们学习,了解到了 JSP 的本质原理,下面我们就来学习一下有关 JSP的基础语法吧
4.1 在 JSP 文件中直接编写文字
如下我们实验一下,我们在上一个 index.jsp 文件当中,直接编写一个 “Hello Wrold” 。当我们浏览器端访问该 index.jsp 文件时,浏览器端有是显示一个则样的效果,而其本质中 “Hello Wrold” 又是被翻译到了,成了什么,翻译到了哪里。
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/10.0.12
* Generated at: 2023-04-21 06:10:10 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.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 jakarta.servlet.jsp.JspFactory _jspxFactory =
jakarta.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("jakarta.servlet";
_jspx_imports_packages.add("jakarta.servlet.http";
_jspx_imports_packages.add("jakarta.servlet.jsp";
_jspx_imports_classes = null;
}
private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
throws java.io.IOException, jakarta.servlet.ServletException {
if (!jakarta.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 jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html";
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("Hello World";
} catch (java.lang.Throwable t {
if (!(t instanceof jakarta.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;
}
}
}
servlet类的 service( 方法的 out.write("翻译到这里",直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。
补充一点: 解决浏览器端显示的乱码问题 ?
通过page指令来设置响应的内容类型,在内容类型的最后面添加:charset=UTF-8
- <%@page contentType="text/html;charset=UTF-8"%>,表示响应的内容类型是text/html,采用的字符集UTF-8
- <%@page import="java.util.List,java.util.ArrayList"%> 表示到对应的 包信息
在JSP中编写的HTML CSS JS代码,这些代码对于JSP来说只是一个普通的字符串。但是JSP把这个普通的字符串一旦输出到浏览器,浏览器就会对HTML CSS JS进行解释执行。展现一个效果。举例如下:
<%@page contentType="text/html; chatSet=UTF-8" %>
<html>
<head>
<title>my first JSP page</title>
<script type="text/javascript">
function sayHello( {
window.alert("你好 JSP"
}
</script>
</head>
<bady>
<h2>My first JSP page</h2>
<input type="button" value="hello jsp" onclick="sayHello(" />
fajsi ffasdfda
fasd
afjsfi9
Sytstem.out.println("hello JSP";
</bady>
</html>
注意:都是被翻译到了 service(方法体当中去了
public void _jspService(final jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
throws java.io.IOException, jakarta.servlet.ServletException {
if (!jakarta.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 jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.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("\r\n";
out.write("<html>\r\n";
out.write("\r\n";
out.write("<head>\r\n";
out.write(" <title>my first JSP page</title>\r\n";
out.write("\r\n";
out.write(" <script type=\"text/javascript\">\r\n";
out.write(" function sayHello( {\r\n";
out.write(" window.alert(\"你好 JSP\"\r\n";
out.write(" }\r\n";
out.write(" </script>\r\n";
out.write("</head>\r\n";
out.write("<bady>\r\n";
out.write(" <h2>My first JSP page</h2>\r\n";
out.write(" <input type=\"button\" value=\"hello jsp\" onclick=\"sayHello(\" />\r\n";
out.write("\r\n";
out.write(" fajsi ffasdfda\r\n";
out.write(" fasd\r\n";
out.write(" afjsfi9\r\n";
out.write("\r\n";
out.write(" Sytstem.out.println(\"hello JSP\";\r\n";
out.write("</bady>\r\n";
out.write("</html>";
} catch (java.lang.Throwable t {
if (!(t instanceof jakarta.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;
}
}
}
4.2 在JSP中编写Java程序 <% %> 与 <%! %>
4.2.1 <% %>
<%
// java语句;
// 在这里编写Java程序,编写 Java语句。
// 既然是编写 Java语句,自然要遵循Java的语法规范。
// 在这里编写的java语句,会被翻译到 service( 方法体内
%>
在这个<% java语句; %>
符号当中编写的被视为java程序,被翻译到Servlet类的 service方法体
内部。举例如下:
<%
System.out.println("Hello World";
%>
<%
System.out.println("Hello JSP";
%>
<%
System.out.println("你好世界";
%>
访问 浏览器前端访问:demo01.jsp 效果
如下是 对应我们访问 demo01.jsp 文件翻译为 demo01_jsp.java 文件的源码信息如下:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/10.0.12
* Generated at: 2023-04-21 06:43:13 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;
public final class demo01_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
jakarta.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("jakarta.servlet";
_jspx_imports_packages.add("jakarta.servlet.http";
_jspx_imports_packages.add("jakarta.servlet.jsp";
_jspx_imports_classes = null;
}
private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
throws java.io.IOException, jakarta.servlet.ServletException {
if (!jakarta.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 jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.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(' ';
out.write(' ';
out.write(' ';
out.write('\r';
out.write('\n';
System.out.println("Hello World";
out.write("\r\n";
out.write("\r\n";
out.write("\r\n";
System.out.println("Hello JSP";
out.write("\r\n";
out.write("\r\n";
System.out.println("你好世界";
out.write('\r';
out.write('\n';
} catch (java.lang.Throwable t {
if (!(t instanceof jakarta.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;
}
}
}
<% %>的使用的注意事项:
- 这里你要细心点,你要思考,在<% %>这个符号里面写java代码的时候,你要时时刻刻的记住你正在“
sevice( 方法体
”当中写代码,方法体中可以写什么,不可以写什么,你心里是否明白呢?
- 在service方法当中编写的代码是有顺序的,方法体当中的代码要遵循自上而下的顺序依次逐行执行。
- service方法当中不能写静态代码块,不能写方法,不能定义成员变量。
- 在同一个JSP当中
<% %>
这个符号可以出现多个。
4.2.2 <%! %>
<%!
// 在这个符号当中编写的java程序会自动翻译到service方法之外。
// service( 方法体之外是什么,自然就是在 Servlet 类里面了。
%>
<%! %>
在这个符号当中编写的java程序会自动翻译到service方法之外。简单的说就是: Servlet 类当中,service( 方法外,那不就是 成员变量,方法,代码块了。
举例如下:
启动部署,访问运行如下
- 这个语法很少用,为什么?并且也不建议使用,因为在service方法外面写静态变量和实例变量,都会存在线程安全问题,因为JSP就是servlet,servlet是单例的,多线程并发的环境下的,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题。
4.3 通过JSP当中的 <%= %>向浏览器前端输入 Java变量
错误方式: 直接在 jsp 当中编写对应变量名,因为我们的在 JSP文件当中直接编写的文字的内容是被翻译到了 service(
方法体当中去了被 out.write("</html>"
包裹的。所以翻译的不是对应变量的值,而是对应变量的名(以字符串的形式翻译到了浏览器端了)。举例如下:
正确的方式一:不推荐
<%
String name = “jack”;
out.write("name = " + name;
%>
注意: 以上代码中的 out
是JSP的九大内置对象之一。可以直接拿来用。当然,必须只能在 service(
方法内部使用(因为 JSP的内置对象是定义在 service( 方法体内的,所以其作用域也仅仅只是在 service( 方法体内部,才有效的)。
-
如果输出的内容中含有“java代码当中的变量的值”,这个时候可以使用以下语法格式:
<%=变量名%> // 在=的后面编写要输出的内容。
<%= %> 注意:在=的后面编写要输出的内容。
<%= %> 这个符号会被翻译到哪里?最终翻译成什么?
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/10.0.12
* Generated at: 2023-04-21 11:55:08 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;
public final class demo02_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
jakarta.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("jakarta.servlet";
_jspx_imports_packages.add("jakarta.servlet.http";
_jspx_imports_packages.add("jakarta.servlet.jsp";
_jspx_imports_classes = null;
}
private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
throws java.io.IOException, jakarta.servlet.ServletException {
if (!jakarta.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 jakarta.servlet.jsp.PageContext pageContext;
jakarta.servlet.http.HttpSession session = null;
final jakarta.servlet.ServletContext application;
final jakarta.servlet.ServletConfig config;
jakarta.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.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(' ';
out.write("\r\n";
out.write("\r\n";
String name = "张三";
out.write("\r\n";
out.write("\r\n";
out.print(name;
out.write("\r\n";
out.write("<br>\r\n";
out.print( 1 + 2;
out.write("\r\n";
out.write("\r\n";
} catch (java.lang.Throwable t {
if (!(t instanceof jakarta.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;
}
}
}
翻译到service方法当中了。
什么时候使用<%=%> 输出呢?输出的内容中含有java的变量,输出的内容是一个动态的内容,不是一个死的字符串。如果输出的是一个固定的字符串,直接在JSP文件中编写即可。
4.4 在JSP 中的专业注释
- 在JSP中如何编写JSP的专业注释
<%--JSP的专业注释,不会被翻译到java源代码当中。--%> 不会翻译对应的信息。该注解当中的内容会被忽略而不翻译的。
<!--这种注释属于HTML的注释,这个注释信息仍然会被翻译到java源代码当中,不建议。--> 翻译到HTML当中作为 html 的
注解存在。
4.5 JSP基础语法总结:
- JSP中直接编写普通字符串
翻译到service方法的out.write("这里"
- <%%>
翻译到service方法体内部,里面是一条一条的java语句。
- <%! %>
翻译到service方法之外。
- <%= %>
翻译到service方法体内部,翻译为:out.print(;
- <%@page contentType="text/html;charset=UTF-8"%>
page指令,通过contentType属性用来设置响应的内容类型以及字符集编码的设置。
JSP文件的扩展名必须是xxx.jsp
吗 ?
.jsp文件的扩展名是可以配置的。不是固定的。
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
xxx.jsp文件对于 Tomcat 小猫咪来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。
5. JSP的指令
指令的作用: 指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)
-
include指令: 包含指令,在JSP中完成静态包含,很少用了。(这里不讲)
- taglib指令: 引入标签库的指令。这个到JJSTL标签库的时候再学习。现在先不管。
- page指令: 目前重点学习一个page指令。
指令的使用语法是什么?
<%@指令名 属性名=属性值 属性名=属性值 属性名=属性值....%>
// 这里我们只学习 page 指令所以是:
<%@page 属性名=属性值 属性名=属性值 属性名=属性值....%>
如下是:page指令当中一些 常用的属性
<%@page session="true|false" %>
true表示启用JSP的内置对象session,表示一定启动session对象。没有session对象会创建。
如果没有设置,默认值就是session="true"
session="false" 表示不启动内置对象session。当前JSP页面中无法使用内置对象session。
session 表示的是会话机制。
<%@page contentType="text/json" %>
contentType属性用来设置响应的内容类型
但同时也可以设置字符集。
<%@page contentType="text/json;charset=UTF-8" %>
<%-- 编码也可以单独分开设置如下--%>
<%@page pageEncoding="UTF-8" %>
pageEncoding="UTF-8" 表示设置响应时采用的字符集。
<%@page import="java.util.List, java.util.Date, java.util.ArrayList" %> 可以多个包一起导入
<%@page import="java.util.*" %> 也可以单个导包
import语句,导包。
<%@page errorPage="/error.jsp" %>
当前页面出现异常之后,跳转到error.jsp页面。
errorPage属性用来指定出错之后的跳转位置。
<%@page isErrorPage="true" %>
表示启用JSP九大内置对象之一:exception
默认值是false。
errorPage,和 isErrorPage 一般是连着一起使用的,因为 errorPage 表示错误跳转到指定的页面,但是不会有错误提示,而 isErrorPage 就是为了解决这个没有错误提示的问题。
举例:
注意:想要使用该 java.lang.Throwable exception 内置对象必须,配置相关的指令:
<%--
在错误页面可以启用JSP九大内置对象,exception
exeption内置对象就是刚刚发生的异常对象
--%>
<%@page isErrorPage="true" %>
<%--isErrorPage 有两个参数一个是为:false,true :true 表示开启 isErrorPage,false 表示禁用,默认不设置也是禁用状态的--%>
error.jsp 的内容上的编写如下
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--
在错误页面可以启用JSP九大内置对象,exception
exeption内置对象就是刚刚发生的异常对象
--%>
<%@page isErrorPage="true" %>
<%--isErrorPage 有两个参数一个是为:false,true :true 表示开启 isErrorPage,false 表示禁用,默认不设置也是禁用状态的--%>
<html>
<head>
<title>程序错误</title>
</head>
<body>
<h3>网络繁忙,稍后再试</h3>
<%
// 配置了 isErrorPage 内置的异常对象则可以
// 打印异常堆栈信息,输出到后台控制台上
// exception 是JSP 九大内置对象之一。
exception.printStackTrace(;
%>
</body>
</html>
6. JSP的九大内置对象
注意:所谓的JSP的九大内置对象指的是:在 service(
方法体当中的局部的对象变量,service( 方法体外是无法使用的。
-
jakarta.servlet.http.HttpServletRequest request 请求作用域
-
jakarta.servlet.ServletContext application 应用作用域
- pageContext < request < session < application
- 以上四个作用域都有:setAttribute、getAttribute、removeAttribute方法。
- 以上作用域的使用原则:尽可能使用小的域。
-
jakarta.servlet.ServletConfig config
-
jakarta.servlet.jsp.JspWriter out (负责输出)
思考一个问题:如果我只用JSP这一个技术,能不能开发web应用?
当然可以使用JSP来完成所有的功能。因为JSP就是Servlet,在JSP的<%%>里面写的代码就是在service方法当中的,所以在<%%>当中完全可以编写JDBC代码,连接数据库,查询数据,也可以在这个方法当中编写业务逻辑代码,处理业务,都是可以的,所以使用单独的JSP开发web应用完全没问题。
- 虽然JSP一个技术就可以完成web应用,但是不建议,还是建议采用servlet + jsp的方式进行开发。这样都能将各自的优点发挥出来。JSP就是做数据展示。Servlet就是做数据的收集。(JSP中编写的Java代码越少越好。)一定要职责分明。
7. 使用Servlet + JSP完成oa项目的改造
-
完成所有页面的正常流转。(页面仍然能够正常的跳转。修改超链接的请求路径。)
- <%=request.getContextPath( %> 在JSP中动态的获取应用的根路径。
-
- 遍历结果集的过程中,取出部门编号、部门名、位置等信息,封装成java对象。
- 将java对象存放到List集合中。
- 将List集合存储到request域当中。
- 转发forward到jsp。
使用Servlet处理业务,收集数据。 使用JSP展示数据。
在JSP中:
- 从request域当中取出List集合。
模块对应的包目录如下:
DeptServlet
package com.RainbowSea.servlet;
import com.RainbowSea.DBUtil.DBUtil;
import com.RainbowSea.bean.Dept;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
// 可以使用模糊查询 @WebServlet("/dept/*"
@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"}
public class DeptServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response throws ServletException,
IOException {
String servletPath = request.getServletPath(; // 获取到浏览器当中的uri
// 双重的判断,一个是 session 会话域要存在,其次是 会话域当中存储了名为 "username" 的信息
if ("/dept/list".equals(servletPath {
doList(request, response;
} else if ("/dept/detail".equals(servletPath {
doDetail(request, response;
} else if ("/dept/delete".equals(servletPath {
doElete(request, response;
} else if ("/dept/save".equals(servletPath {
doSave(request, response;
} else if ("/dept/modify".equals(servletPath {
doModify(request, response;
}
}
/**
* 修改部门信息
*
* @param request
* @param response
*/
private void doModify(HttpServletRequest request, HttpServletResponse response throws IOException {
request.setCharacterEncoding("UTF-8"; // 设置获取的的信息的编码集
Connection connection = null;
PreparedStatement preparedStatement = null;
// 影响数据库的行数
int count = 0;
String deptno = request.getParameter("deptno";
String dname = request.getParameter("dname";
String loc = request.getParameter("loc";
try {
// 1. 注册驱动,连接数据库
connection = DBUtil.getConnection(;
// 2. 获取到操作数据库的对象,预编译sql语句,sql测试
String sql = "update dept set dname = ?,loc = ? where depton = ?";
preparedStatement = connection.prepareStatement(sql;
// 3. 填充占位符,真正执行sql语句
// 从下标 1开始
preparedStatement.setString(1, dname;
preparedStatement.setString(2, loc;
preparedStatement.setString(3, deptno;
count = preparedStatement.executeUpdate(;
} catch (SQLException e {
throw new RuntimeException(e;
} finally {
// 4. 释放资源,最后使用的优先被释放
DBUtil.close(connection, preparedStatement, null;
}
if (count == 1 {
// 更新成功
// 跳转到部门列表页面(部门列表表面是通过java程序动态生成的,所以还需要再次执行另一个Servlet)
// 转发是服务器内部的操作,“/” 不要加项目名
// request.getRequestDispatcher("/dept/list/".forward(request,response;
// 优化使用重定向,自发前端(需要指明项目名
response.sendRedirect(request.getContextPath( + "/dept/list";
}
}
/**
* 保存部门信息
*
* @param request
* @param response
*/
private void doSave(HttpServletRequest request, HttpServletResponse response throws IOException {
request.setCharacterEncoding("UTF-8";
// 获取到前端的数据,建议 name 使用复制
String deptno = request.getParameter("deptno";
String dname = request.getParameter("dname";
String loc = request.getParameter("loc";
// 连接数据库,添加数据
Connection connection = null;
PreparedStatement preparedStatement = null;
// 影响数据库的行数
int count = 0;
try {
// 1. 注册驱动,连接数据库
connection = DBUtil.getConnection(;
// 2. 获取操作数据库对象,预编译sql语句,Sql测试
String sql = "insert into dept(depton,dname,loc values(?,?,?";
preparedStatement = connection.prepareStatement(sql;
// 3. 填充占位符, 真正执行sql语句,
// 注意: 占位符的填充是从 1 开始的,基本上数据库相关的起始下标索引都是从 1下标开始的
preparedStatement.setString(1, deptno;
preparedStatement.setString(2, dname;
preparedStatement.setString(3, loc;
// 返回影响数据库的行数
count = preparedStatement.executeUpdate(;
// 5.释放资源
} catch (SQLException e {
throw new RuntimeException(e;
} finally {
DBUtil.close(connection, preparedStatement, null;
}
// 保存成功,返回部门列表页面
if (count == 1 {
// 这里应该使用,重定向
// 这里用的转发,是服务器内部的,不要加项目名
//request.getRequestDispatcher("/dept/list/".forward(request, response;
// 重定向
response.sendRedirect(request.getContextPath( + "/dept/list";
}
}
/**
* 通过部门删除部门
*
* @param request
* @param response
*/
private void doElete(HttpServletRequest request, HttpServletResponse response throws IOException {
request.setCharacterEncoding("UTF-8"; // 设置获取的的信息的编码集
// 获取到发送数据
String deptno = request.getParameter("deptno";
/*
根据部门编号删除信息,
删除成功,跳转回原来的部门列表页面
删除失败,跳转删除失败的页面
*/
Connection connection = null;
PreparedStatement preparedStatement = null;
// 记录删除数据库的行数
int count = 0;
// 连接数据库进行删除操作
try {
// 1.注册驱动,连接数据库
connection = DBUtil.getConnection(;
// 开启事务(取消自动提交机制),实现可回滚
connection.setAutoCommit(false;
// 2. 预编译sql语句,sql测试
String sql = "delete from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql;
// 3. 填充占位符,真正的执行sql语句
preparedStatement.setString(1, deptno;
// 返回影响数据库的行数
count = preparedStatement.executeUpdate(;
connection.commit(; // 手动提交数据
} catch (SQLException e {
// 遇到异常回滚
if (connection != null {
try {
// 事务的回滚
connection.rollback(;
} catch (SQLException ex {
throw new RuntimeException(ex;
}
}
throw new RuntimeException(e;
} finally {
// 4. 释放资源
// 因为这里是删除数据,没有查询操作,所以 没有 ResultSet 可以传null
DBUtil.close(connection, preparedStatement, null;
}
if (count == 1 {
// 删除成功
// 仍然跳转到部门列表页面
// 部门列表页面的显示需要执行另外一个Servlet,怎么办,可以使用跳转,不过这里最后是使用重定向
// 注意:转发是在服务器间的,所以不要加“项目名” 而是 / + web.xml 映射的路径即可
//request.getRequestDispatcher("/dept/list/".forward(request,response;
// 优化:使用重定向机制 注意: 重定向是自发到前端的地址栏上的,前端所以需要指明项目名
// 注意: request.getContextPath( 返回的根路径是,包含了 "/" 的
response.sendRedirect(request.getContextPath( + "/dept/list";
}
}
/**
* 通过部门编号,查询部门的详情
*
* @param request
* @param response
*/
private void doDetail(HttpServletRequest request, HttpServletResponse response throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"; // 设置获取的的信息的编码集
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 获取到部门编号
String dno = request.getParameter("dno";
Dept dept = new Dept(;
// 获取到部门编号,获取部门信息,将部门信息收集好,然后跳转到JSP做页面展示
try {
// 2. 连接数据库,根据部门编号查询数据库
// 1.注册驱动,连接数据库
connection = DBUtil.getConnection(;
// 2. 预编译SQL语句,sql要测试
String sql = "select dname,loc from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql;
// 3. 填充占位符,真正执行sql语句
preparedStatement.setString(1, dno;
resultSet = preparedStatement.executeQuery(;
// 4. 处理查询结果集
while (resultSet.next( {
String dname = resultSet.getString("dname";
String loc = resultSet.getString("loc";
// 封装对象(建议使用咖啡豆,因为只有一个对象
dept.setDeptno(dno;
dept.setDname(dname;
dept.setLoc(loc;
}
} catch (SQLException e {
throw new RuntimeException(e;
} finally {
// 5. 释放资源
DBUtil.close(connection, preparedStatement, resultSet;
}
// 这个咖啡豆只有一个,所以不需要袋子,只需要将这个咖啡豆放到request请求域当中,
// 用于对应的 jsp显示
request.setAttribute("dept", dept;
//String sign = request.getParameter("f";
/*if("m".equals(sign {
// 转发:多个请求为一个请求(地址栏不会发生改变
// 注意: 该路径默认是从 web 开始找的 / 表示 web
// 转发到修改页面
request.getRequestDispatcher("/edit.jsp".forward(request,response;
} else if("d".equals(sign {
// 跳转到详情页面
request.getRequestDispatcher("/detail.jsp".forward(request,response;
}*/
// 或者优化
// 注意 无论是转发还是重定向都是从 “/” 开始的
// request.getParameter(拿到的是 f=edit,还是f=detail 就是跳转到的哪个页面
//<a href="<%=request.getContextPath(%>/dept/detail?f=edit&dno=<%=dept.getDeptno(%>">修改</a>
//<a href="<%=request.getContextPath(%>/dept/detail?f=detail&dno=<%=dept.getDeptno(%>">详情</a>
String forward = "/" + request.getParameter("f" + ".jsp";
request.getRequestDispatcher(forward.forward(request, response;
}
/**
* 连接数据库,查询所有的部门信息,将部门信息收集好,然后跳转到JSP页面展示
*
* @param request
* @param response
*/
private void doList(HttpServletRequest request, HttpServletResponse response throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"; // 设置获取的的信息的编码集
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 创建一个集合List 存储查询到的信息
List<Dept> depts = new ArrayList<Dept>(;
try {
// 连接数据库,查询所有部门:
// 1. 注册驱动,获取连接
connection = DBUtil.getConnection(;
// 2. 获取操作数据库对象,预编译sql语句
String sql = "select depton as det,dname,loc from dept"; // 在mysql中测试一下是否正确
preparedStatement = connection.prepareStatement(sql;
// 3. 执行sql语句
resultSet = preparedStatement.executeQuery(;
// 4. 处理查询结果集
while (resultSet.next( {
String det = resultSet.getString("det"; // 有别名要使用别名
String dname = resultSet.getString("dname";
String loc = resultSet.getString("loc";
Dept dept = new Dept(det, dname, loc;
// 将部门对象放到List集合当中
depts.add(dept;
}
} catch (SQLException e {
throw new RuntimeException(e;
} finally {
// 5. 关闭资源
DBUtil.close(connection, preparedStatement, resultSet;
}
// 查询到数据,将数据提交给 list.jsp 显示数据
// 将集合存储的数据放到请求域当中,用于其他Servlet 使用 jsp 也是Servelt
request.setAttribute("depList", depts;
// 转发(注意不要重定向,重定向无法共用 request 请求域当中的数据
// 转发路径,/ 默认是从 web 目录开始找的
request.getRequestDispatcher("/list.jsp".forward(request, response;
}
}
UserServlet
package com.RainbowSea.servlet;
import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@WebServlet({"/user/login", "/user/exit"}
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response throws ServletException,
IOException {
// 获取到浏览器地址栏上的URL路径
String servletPath = request.getServletPath(;
if ("/user/login".equals(servletPath {
doLogin(request, response;
} else if ("/user/exit".equals(servletPath {
doExit(request, response;
}
}
private void doExit(HttpServletRequest request, HttpServletResponse response throws IOException {
}
protected void doLogin(HttpServletRequest request, HttpServletResponse response throws ServletException,
IOException {
// 一个用户登录验证的方式:验证用户名和密码是否正确
// 获取用户名和密码
// 前端提交是数据是:username=111&password=fads
// 注意:post 提交的数据是在请求体当中,而get提交的数据是在请求行当中
boolean success = false; // 标识登录成功
String username = request.getParameter("username";
String password = request.getParameter("password";
String exempt = request.getParameter("exempt";
// 连接数据库验证用户名和密码
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 1. 获取连接,注册驱动
connection = DBUtil.getConnection(;
// 2. 获取操作数据对象,预编译sql语句, ? 占位符不要加,“”,'' 单双引号,成了字符串了,无法识别成占位符了。
String sql = "select username,password from t_user where username = ? and password = ?";
preparedStatement = connection.prepareStatement(sql;
// 3. 填充占位符,真正执行sql语句
preparedStatement.setString(1, username;
preparedStatement.setString(2, password;
resultSet = preparedStatement.executeQuery(;
// 4. 处理查询结果集
// 只有一条结果集
if (resultSet.next( {
// 登录成功
success = true;
}
} catch (SQLException e {
throw new RuntimeException(e;
} finally {
// 5. 关闭资源,最后使用的最先关闭,
DBUtil.close(connection, preparedStatement, resultSet;
}
// 登录成功与否
if (success {
response.sendRedirect(request.getContextPath( + "/dept/list";
} else {
// 失败,跳转到失败页面
response.sendRedirect(request.getContextPath( + "/error.jsp";
}
}
}
所以的 html 页面替换为了对应的 jsp
文件
add.html --> add.jsp
<%@page contentType="text/html; charset=utf-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新增部门</title>
</head>
<body>
<h2>新增部门</h2>
<form action="<%=request.getContextPath(%>/dept/save" method="post">
部门编号: <input type="text" name="deptno" /><br>
部门名称: <input type="text" name="dname" /><br>
部门位置: <input type="text" name="loc" /><br>
<input type="submit" value="保存" />
</form>
</body>
</html>
detail.html --> detail.jsp
<%@ page import="com.RainbowSea.bean.Dept" %>
<%@page contentType="text/html; charset=UTF-8" %>
<%
// 获取到请求域当中的数据,
Dept dept = (Dept request.getAttribute("dept";
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门详情</title>
</head>
<body>
<h2>部门详情</h2>
部门编号: <%=dept.getDeptno(%> <br>
部门名称: <%=dept.getDname(%><br>
部门位置: <%=dept.getLoc(%><br>
<input type="button" value="后退" onclick="window.history.back(" />
</body>
</html>
edit.html --> edit.jsp
<%@ page import="com.RainbowSea.bean.Dept" %>
<%@page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<%
// 获取到存储到请求域当的 部门信息
Dept dept = (Dept request.getAttribute("dept";
%>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改部门</title>
</head>
<body>
<h2>修改部门</h2>
<form action="<%=request.getContextPath(%>/dept/modify" method="post">
<!-- readonly 表示只读,不可修改的作用 -->
部门编号: <input type="text" name="deptno" value="<%=dept.getDeptno(%>" readonly /><br>
部门名称: <input type="text" name="dname" value="<%=dept.getDname(%>" /><br>
部门位置: <input type="text" name="loc" value="<%=dept.getLoc(%>" /><br>
<input type="submit" value="修改" />
</form>
</body>
</html>
error.html --> error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page session="false" %>
<html>
<head>
<title>登录失败</title>
</head>
<body>
登录失败,密码或账号错误: <a href="<%=request.getContextPath(%>/index.jsp">请重新登录</a>
</body>
</html>
index.html --> index.jsp
<%@page contentType="text/html; charset=UTF-8" %>
<%--表示访问 jsp 的时候不生成 session 对象--%>
<%@page session="false" %> <%--该指令不是警用jsp内置对象当中的 session--%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎使用OA系统</title>
</head>
<body>
<%--<!--注意:对应前端的资源获取基本上都是要加项目名的,并且要"/"开始-->--%>
<%--这种方式不好,将 项目名写死了,--%>
<%--<a href="/servlet09/dept/list/">查看部门列表</a>--%>
<%--使用request.getContextPath( 动态获取项目名的根路径带 / 的,
注意哪里可以加 空格,哪里不能加空格--%>
<%--<a href="<%=request.getContextPath( %>/dept/list">查看部门列表</a>--%>
<%--<%=request.getContextPath(%> <%–out.print(request.getContextPath( 获取到该项目的根路径带有/的–%>--%>
<h2>Login in</h2>
<hr>
<form action="<%=request.getContextPath(%>/user/login" method="post">
username: <input type="text" name="username" /><br>
password: <input type="password" name="password" /> <br>
免十天登录 <input type="checkbox" name="exempt" value="true" /> <br>
<input type="submit" value="login" />
</form>
</body>
</html>
list.html ---> list.jsp
<%@ page import="com.RainbowSea.bean.Dept" %>
<%@ page import="java.util.List" %> <%--这是导包在jSP 当中,并翻译为import导入对于的报--%>
<%@page contentType="text/html; charset=utf-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>部门列表页面</title>
</head>
<script type="text/javascript">
function del(dno {
// 弹出确认框,用户点击确定,返回true,点击取消返回false
var ok = window.confirm("亲,删了不可恢复哦!";
if (ok {
// 发送请求进行删除数据的操作
// 在js代码当中如何发送请求给服务器
//
// document.location.href="请求路径"
// document.location = "请求路径"
// window.location.href = "请求路径"
// window.location = "请求路径"
// 注意是根据所传的部门编号删除数据的
document.location.href = "<%=request.getContextPath(%>/dept/delete?deptno=" + dno;
}
}
</script>
<body>
<h2 align="center">部门列表</h2>
<%--显示登录的用户名信息--%>
<h4><%=session.getAttribute("username"%></h4> <%--注意这里使用的是 jsp 内置的 session 对象所以不可以,不要把 session 禁用了。--%>
<a href="<%=request.getContextPath(%>/user/exit">安全退出系统(手动清除 session 会话信息</a>
<table border="1px" align="center" width="50%">
<tr>
<th>序号</th>
<th>部门编号</th>
<th>部门名称</th>
</tr>
<%
// 获取到 request 域当中的数据,并取出来,我们知道是什么类型的数据,直接强制转换
List<Dept> depList = (List<Dept> request.getAttribute("depList";
int i = 0;
// 循环遍历List,取出数据
for (Dept dept : depList {
// 在后台输出测试
//System.out.println(dept.getDname(;
// 在浏览器显示 测试
//out.write(dept.getDname( + "<br>";
%>
<%-- 浏览器显示测试--%>
<%-- <%=dept.getDname(%>--%>
<tr>
<td><%=++i%>
</td>
<td><%=dept.getDeptno(%>
</td>
<td><%=dept.getLoc(%>
</td>
<td>
<!-- javascript:void(0保持<a>超链接的格式,同时不会发生跳转
我只是希望用户点击该超链接的时候执行一段js代码,不进行页面的跳转 -->
<a href="javascript:void(0" onclick="del(<%=dept.getDeptno(%>">删除</a>
<%-- <a href="<%=request.getContextPath(%>/dept/detail?f=m&dno=<%=dept.getDeptno(%>">修改</a>--%>
<%-- <a href="<%=request.getContextPath(%>/dept/detail?f=d&dno=<%=dept.getDeptno(%>">详情</a>--%>
<%-- 这里的修改,和详情都是需要 部门编号,我们可以进行一个统一处理,通过所传的不同的 f= 值作为
标记,用于服务器端的 Servlet 判断转发处理到的--%>
<%-- 或者是--%>
<a href="<%=request.getContextPath(%>/dept/detail?f=edit&dno=<%=dept.getDeptno(%>">修改</a>
<a href="<%=request.getContextPath(%>/dept/detail?f=detail&dno=<%=dept.getDeptno(%>">详情</a>
</td>
</tr>
<%
}
%>
</table>
<hr>
<a href="<%=request.getContextPath(%>/add.jsp">新增部门</a>
</body>
</html>
当前的oa应用存在的问题:
- 加一个登录功能。登录成功的可以访问该系统,登录失败不能访问。
-
实现登录功能:
- 步骤1:数据库当中添加一个用户表:t_user
- t_user表当中存储的是用户的登录信息,最基本的也包括:登录的用户名和登录的密码。
- 密码一般在数据库表当中存储的是密文。一般不以明文的形式存储。(这里先使用明文方式。)
- 向t_user表中插入数据。
- 步骤2:再实现一个登录页面。
- 登录页面上应该有一个登录的表单。有用户名和密码输入的框。
- 用户点击登录,提交表单,提交用户名和密码。form是post方式提交。
- 登录成功:跳转到部门列表页面。
- 这个登录功能目前只是一个摆设,没有任何作用。只要用户知道后端的请求路径,照样可以在不登录的情况下访问。
解决方式:通过 session 会话机制,判断是否为同一个用户,并且登录过。因为涉及到篇幅内容的过多,所以详细内容大家可以移步至:🔜🔜🔜 B/S结构系统的会话机制(session_ChinaRainbowSea的博客-CSDN博客
8. 补充:
bean 是什么意思?
- javabean(java的logo是一杯冒着热气的咖啡。javabean被翻译为:咖啡豆)
- java是一杯咖啡,咖啡又是由一粒一粒的咖啡豆研磨而成。
- 整个java程序中有很多bean的存在。由很多bean组成。
- 什么是javabean?实际上javabean你可以理解为符合某种规范的java类,比如:
- 有无参数构造方法
- 属性私有化
- 对外提供公开的set和get方法
- 实现java.io.Serializable接口
- 重写toString
- 重写hashCode+equals
- ....
- javabean其实就是java中的实体类。负责数据的封装。
- 由于javabean符合javabean规范,具有更强的通用性。
package com.RainbowSea.bean;
import java.io.Serializable;
import java.util.Objects;
public class Dept implements Serializable {
private static final long serialVersionUID = 1L;
private String deptno;
private String dname;
private String loc;
public Dept( {
}
public Dept(String deptno, String dname, String loc {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
public String getDeptno( {
return deptno;
}
public void setDeptno(String deptno {
this.deptno = deptno;
}
public String getDname( {
return dname;
}
public void setDname(String dname {
this.dname = dname;
}
public String getLoc( {
return loc;
}
public void setLoc(String loc {
this.loc = loc;
}
@Override
public boolean equals(Object o {
if (this == o return true;
if (!(o instanceof Dept return false;
Dept dept = (Dept o;
return Objects.equals(getDeptno(, dept.getDeptno( && Objects.equals(getDname(, dept.getDname( && Objects.equals(getLoc(, dept.getLoc(;
}
@Override
public int hashCode( {
return Objects.hash(getDeptno(, getDname(, getLoc(;
}
@Override
public String toString( {
return "Dept{" +
"deptno='" + deptno + '\'' +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
'}';
}
}
9. 总结:
-
- 在JSP文件中直接编写文字,翻译的是在
service(
方法体内的,翻译到 servlet类的 service(方法的 out.write("翻译到这里",直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。 - JSP的注释:
<%--JSP的专业注释,不会被翻译到java源代码当中。--%> 不会翻译对应的信息。该注解当中的内容会被忽略而不翻译的。 <!--这种注释属于HTML的注释,这个注释信息仍然会被翻译到java源代码当中,不建议。--> 翻译到HTML当中作为 html 的 注解存在。
- <% %> 在这个
<% java语句; %>
符号当中编写的被视为java程序,被翻译到Servlet类的service方法体
内部。注意方法体当中不可以编写什么内容。同时因为编写的是 Java代码需要遵从 Java的语法。
<% // java语句; // 在这里编写Java程序,编写 Java语句。 // 既然是编写 Java语句,自然要遵循Java的语法规范。 // 在这里编写的java语句,会被翻译到 service( 方法体内 %>
-
<%! %>
在这个符号当中编写的java程序会自动翻译到service方法之外。简单的说就是: Servlet 类当中,service( 方法外,那不就是 成员变量,方法,代码块了。
<%! // 在这个符号当中编写的java程序会自动翻译到service方法之外。 // service( 方法体之外是什么,自然就是在 Servlet 类里面了。 %>
- <%= %>向浏览器前端输入 Java变量值,而不是变量名。
<% String name = “jack”; out.write("name = " + name; %>
- 在JSP文件中直接编写文字,翻译的是在
-
JSP指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)主要说明的是 page 指令。指令的格式:
JSP 的本质:一个index_jsp类就是一个Servlet类。总结就是:一个 index.jsp 文件会被翻译为一个 index_jsp.java 类,而 该 翻译的 index_jsp.java类是 继承
了 org.apache.jasper.runtime.HttpJspBase 类的,而 org.apache.jasper.runtime.HttpJspBase 类 是继承了 HttpServlet 类的。这下逻辑就清晰了,JSP 就是 Servlet
。
<%@指令名 属性名=属性值 属性名=属性值 属性名=属性值....%>
// 这里我们只学习 page 指令所以是:
<%@page 属性名=属性值 属性名=属性值 属性名=属性值....%>
- JSP 的九大内置对象:所谓的JSP的九大内置对象指的是:在
service(
方法体当中的局部的对象变量,service( 方法体外是无法使用的。 - JSP 第一次访问速度慢的原因:翻译为 java,并编译java,生成 class 文件
-
JSP文件的扩展名必须不是
xxx.jsp后缀吗,不是的,.jsp文`件的扩展名是可以配置的。
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
- 开发JSP的最高境界: 眼前编写的是JSP代码,但是我们心里,脑袋里面当中呈现的是 java代码。
10. 最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后悔有期