JSP
内容导航
Javaweb — Servlet 的cookie和监听器、过滤器 117天
JSP的引入,EL表达式
还不够,还要继续
JSP
从之前的写的简单的单表查询就可以看出来完全使用Servlet就出现了前后端代码冗杂,动态界面的输出只能使用输出流,这样就导致很多麻烦,代码臃肿,前端代码不便修改;所以急需新的技术手段来解决这种问题; java的响应体只适合将数据量少的处理结果放到响应体中,一旦响应的内容过长,那么就不利于继续使用;大大增加了开发难度
- 这里可以简单分享一下复制项目的方法
其实非常简单,cory之后粘贴,然后F2修改,就会直接将settings和web.xml中的位置全部替换;这个时候就体现出了后端代码不将路径写死的优点,只用修改简单的前端的路径即可
自我理解的JSP的最大的特点
- JSP允许java代码和HTML共存,并且是分离的,不用写在out.println中
- JSP将所有的执行标记看做一个整体,所以可以任意拆分;借助这个特点就可以将java中”嵌入“html代码,但是这也导致了可读性差,只是在一定程度上解决了代码的冗杂问题,并没有达到理想的效果,反而降低了代码的可读性
jsp规范介绍
JSP java server page— java服务端界面;JSP规范制定了如何开发JSP文件代替响应对象将处理结果写入到响应体的开发流程,JSP规范制定了http服务器应该如何调用管理JSP文件
JSP文件在执行时,自动将文件所有的内容写入到响应体中,从而节省书写out.println
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
一定要注意这里的pageEncoding和charset要同时修改,pageEncoding是java编码格式,编译存储jsp文件,charset是浏览器的解码格式,按照什么方式来解码;因为传输不是按照字符传输,而是字节流的方式
jsp文件是直接写入到响应体中,这里的第一个标签就设置了contentType ,就是之前的方法设置的,但是这里设置的charset是ISO-8859-1;所以可以在java代码中使用setCharsetEncoding来设置编码方式;或者直接修改为UTF-8
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<center><font style="font-size: 14px;color: red">我是一个JSP文件</font></center>
</body>
</html>
JSP可以看到page language是java,也就是可以编写java代码,所以jsp文件时动态资源,而不是html那种资源
JSP中书写java命令 执行标记<% %>
如果直接在jsp中直接写java代码jsp会按照普通的text也就是html来解析,不会按照java来解析
执行标记没有特殊的含义,只有书写在执行标记中的内容才会当作java代码来执行; 所以执行代码中可以书写任意的代码代码,比如声明java变量,进行运行表达式:数学运算、关系运算、逻辑运算;声明控制语句
但是光执行命令时不能将java中的结果写到浏览器中的
这个时候就需要其他的执行标记来输出
<%= %>输出标记
这个执行标记就相当于out.println,可以将<% %>中的java代码中的变量的值输出;就是jsp中专门给java的一个输出的标记,就是类似于println函数,但是整个页面的的语言时HTML,除了执行标记中的部分,之外的都是HTML
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<center><font style="font-size: 14px;color: red">我是一个JSP文件</font></center>
<%
int sum = 0;
for(int i = 1; i <= 100; i++) {
sum += i;
}
%>
从1加到100的结果为<%= sum %>
</body>
</html>
这里输出的内容就是 从1到100的结果时5050
JSP中使用包的时候也会自动导包
<%@page import="cfeng.oa.entity.Student"%>
这就类似于头文件,执行标记都是<% 但是这里时@符号
<%@page import="cfeng.oa.entity.Student"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<center><font style="font-size: 14px;color: red">我是一个JSP文件</font></center>
<%
int sum = 0;
for(int i = 1; i <= 100; i++) {
sum += i;
}
Student stu = new Student(11,"小欢","HC2006");
%>
从1加到100的结果为<%= sum %><br/>
学生编号: <%= stu.getStuno() %><br/>
学生姓名: <%= stu.getStuname() %><br/>
学生班级: <%= stu.getStuclass() %>
</body>
</html>
在JSP中,将所有的执行标记当作一个整体,所以不同的执行标记中的内容时相通的,和块不一样
所以这里可以试着输出一下表格,jsp虽然没有将前端代码写入java里面了,但是随意使用执行标记拆解代码,代码的可读性很差
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.List"%>
<%@page import="cfeng.oa.entity.Student"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
List<Student> list = new ArrayList();
Student stu1 = new Student(1,"张三","HC2001");
Student stu2 = new Student(2,"李四","HC2002");
Student stu3 = new Student(1,"王五","HC2003");
Student stu4 = new Student(1,"Json","HC2004");
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
%>
<!-- 数据输出到表格中 -->
<table border="1px" width="50%" align="center">
<tr>
<th>学生学号</th>
<th>学生姓名</th>
<th>学生班级</th>
</tr>
<%
for(Student s: list) {
%>
<tr>
<td><%=s.getStuno() %></td>
<td><%= s.getStuname() %></td>
<td><%= s.getStuclass() %></td>
</tr>
<%
}
%>
</table>
所以使用jsp的好处就是可以分离出来,这样就便于检查代码是否正确了
JSP内置对象
JSP中有很多内置对象,这些内置对象可以方便jsp代码的开发
request 请求对象
这个对象的类型为HttpServletRequest;作用就是在JSP文件运行时读取请求的信息;在请求转发过程中实现数据共享; 比如其他的servelt类可以使用请求转发或者重定向直接来访问jsp资源
如果将整个JSP文件看作一个方法体,那么内置对象就像传入的参数,是可以直接使用的,这样就可以简化开发
<%
String user = request.getParameter("user");
%>
来访者姓名 <%=user %>
这里的request不需要创建;直接就是内置的一个对象,可以直接使用
session 会话作用域对象
这个对象的类型为HttpSession;
作用: JSP文件运行时,该对象可以直接指向访问该jsp文件用户的在服务器的私人的储物柜就是session中,添加对象或者操作其中的对象
<%
session.setAttribute("student", stu1);
%>
注意这里Http服务器也会自动给来访用户创建一个session
;就相当于使用的是空参的方法,有则使用,无则创建, 因为内置对象的时候就相当于给访问者创建一个session用来数据共享【所以使用的时候就不需要再创建session,直接使用就可以了】
可以访问一下就可以看到
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:8080/TestJsp/
Cookie: JSESSIONID=C0B155AE394C0EB1FA1921B14C8D9F06
可以看到并没有专门的java代码来创建session对象,jsp内置了session对象,所以用户就有相应的cookie; Cookie: JSESSIONID=C0B155AE394C0EB1FA1921B14C8D9F06
9大内置对象
JSP内置对象(9个内置对象):
PageContext javax.servlet.jsp.PageContext JSP的页面容器
response javax.servlet.http.HttpServletResponse 服务器向客户端的回应信息
application javax.servlet.ServletContext 表示所有用户的共享信息
config javax.servlet.ServletConfig 服务器配置信息,可以取得初始化参数
out javax.servlet.jsp.jspWriter 页面输出
page java.lang.object)
exception java.lang.Throwable
这些内置的对象都是可以直接使用的,但是最常用的就是request和session
JSP和servlet分工
JSP作为新的技术,目的就是解决servlet中输出的冗杂问题;所以两者的分工时明确的
- Servlet : 负责处理业务并得到处理的结果
- JSP : 不负责业务处理,主要任务将Servlet中的处理结果写入到响应体中
如果将一个程序当作一个菜,那么Servlet就是大厨,那么JSP就是传菜员,大厨是不可能自己将菜给用户的,JSP就专门负责将菜给用户;这里就进行了分包了,也就是JSP就是view,Servlet就是业务处理service
- 调用的关系:
一般是Servlet工作完毕后,一般通过请求转发的方式,向Tomcat请求调用JSP
- Servlet和JSP之间如何实现数据共享?
使用的是请求作用域对象,所以这里必须使用请求转发才能共享数据,因为HTTP是无状态协议,所以必须是同一次的请求
配合的案例,实现StudentListAction
首先写一个Servlet代表之前的StudentListAction
package cfeng.oa.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cfeng.oa.dao.StudentDao;
@WebServlet("/JspStuQuery")
public class JspStuQueryServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取访问信息?这里不需要
//调用dao的方法获取数据库中的所有的学生的信息
StudentDao stuDao = new StudentDao();
List allStu = stuDao.getAllStudent();
//将数据交给请求作用域的对象
request.setAttribute("stuList", allStu);
//请求转发给jsp,将req和resp交给jsp使用
request.getRequestDispatcher("/studentDetail.jsp").forward(request, response);
}
}
这里的代码量就很少,就是调取Dao层的对象进行数据库连接【这里使用的是常规连接,没有统一开放通道节省时间】注意这里的共享数据的方式是请求作用域对象,所以一定要使用请求转发,这里的是后台路径
<%@page import="cfeng.oa.entity.Student"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
//从请求作用域中获得添加的集合
List<Student> stuList = (List<Student>)request.getAttribute("stuList");
%>
<!-- 将结果输出到表格中 -->
<table border="1px" width="50%" align="center">
<tr>
<th>学生学号</th>
<th>学生姓名</th>
<th>学生班级</th>
</tr>
<%
for(Student s: stuList) {
%>
<tr>
<td><%=s.getStuno() %></td>
<td><%= s.getStuname() %></td>
<td><%= s.getStuclass() %></td>
</tr>
<%
}
%>
</table>
JSP文件调用步骤
- Http服务器将JSP文件内容【编辑】为一个Servlet接口的实现类(.java)
- Http服务器将Servlet接口实现类【编译】为class(.class)
- Http服务器负责创建class的实例对象,这个实例对象就是Serevlet实例对象
- Http服务器通过Servlet实例对象调用jsp_service方法,将jsp文件内容写入到响应体中
其实就是将jsp当作一个servlet来使用的
前面的两个可以在work文件夹下面观察到
D:\Java项目\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\TestJsp\org\apache\jsp
其实就是在eclipse或者其他的编译软件的Tomcat副本下面;这里的Server.core下面的temp0就是Tomcat的副本;在其下的几个主要文件夹中,这些应用的代码和编译后的字节码都存在work文件夹下
可以看看它生成的java代码
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/9.0.55
* Generated at: 2021-12-28 09:37:51 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.*;
import cfeng.oa.entity.Student;
import java.util.List;
public final class studentDetail_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 = new java.util.HashSet<>();
_jspx_imports_classes.add("java.util.List");
_jspx_imports_classes.add("cfeng.oa.entity.Student");
}
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("\r\n");
out.write("\r\n");
out.write("\r\n");
//从请求作用域中获得添加的集合
List<Student> stuList = (List<Student>)request.getAttribute("stuList");
out.write("\r\n");
out.write("\r\n");
out.write("<!-- 将结果输出到表格中 -->\r\n");
out.write("<table border=\"1px\" width=\"50%\" align=\"center\">\r\n");
out.write(" <tr>\r\n");
out.write(" <th>学生学号</th>\r\n");
out.write(" <th>学生姓名</th>\r\n");
out.write(" <th>学生班级</th>\r\n");
out.write(" </tr>\r\n");
out.write(" ");
for(Student s: stuList) {
out.write("\r\n");
out.write(" <tr>\r\n");
out.write(" <td>");
out.print(s.getStuno() );
out.write("</td>\r\n");
out.write(" <td>");
out.print( s.getStuname() );
out.write("</td>\r\n");
out.write(" <td>");
out.print( s.getStuclass() );
out.write("</td>\r\n");
out.write(" </tr>\r\n");
out.write(" ");
}
out.write("\r\n");
out.write("</table>");
} 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);
}
}
}
这些写的代码就封装在==_jspService==中,导入的包则直接通过的是静态代码块来实现的
这个jsp继承的类HttpJspBase继承自Servlet,所以这个类就是一个Servlet类
那么九大内置对象也全部给出
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
这里和之前设想的相同,真的就是给的参数
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;
可以看到九大内置对象就是这样子存在的;而上面介绍的session确实就是使用的getSession()无参的方法,就是如果没有session就会创建session
然后再来看一下我们的的HTML是如何写入浏览器
for(Student s: stuList) {
out.write("\r\n");
out.write(" <tr>\r\n");
out.write(" <td>");
out.print(s.getStuno() );
out.write("</td>\r\n");
out.write(" <td>");
out.print( s.getStuname() );
out.write("</td>\r\n");
out.write(" <td>");
out.print( s.getStuclass() );
out.write("</td>\r\n");
out.write(" </tr>\r\n");
out.write(" ");
}
out.write("\r\n");
这就发现其实就是一个输出流将HTML给输出,因为上面已经设置了response的输出的MIME类型为html了
所以说JSP其实就是一套封装好的工具【所作的工作就是给出一些命令,方便将HTML给剥离处理】,但是最基本的还是之前的Servlet;其实最终的本源还是servlet
所以说JSP就是一个工具,其主要作用就是HTML代码的剥离,可以看到所有的执行标记之外的部分(HTML代码)全部使用Out.write方法给输出到浏览器,其他地方的都是正常的顺序
因此审视一个JSP文件应该是以java代码为主,写HTML的部分就当成out.println;这样使用执行标记与其说是拆分java代码,不如说是插入HTML代码,执行标记之间插入HTML代码 【 这里的<%= %> 相当于是out.print() 】
JSP提供的手段让programmer不用写out.println;让HTML可以独立存在,方便检查正确性 ---- 但是缺点就是不够彻底,JSP的可读性太低了
jsp文件的作用就是嵌入HTML代码,代替响应对象,将Servlet中的doGet/Post中的执行结果输出到响应体
;所以jsp中不要有业务操作的代码
jsp的开发步骤:
- 从作用域中得到数据【全局,请求,会话后可】req.getServletContext() req.getSession() req请求域
- 将数据进行强制类型转换【因为request得到的是Object类型】
- 将数据写入到响应体中
JSP中都有这几个内置对象: application、session、request
EL技术 ${域Scope.变量名}
EL工具包是java技术开发的一个jar包, 其作用就是降低JSP文件中java命令的开发强度; 从作用域中取出数据并输出到响应体中,表达式命令格式;Tomcat服务器本身就携带了一个EL工具包,el-api.jar
其实EL技术就像java8中的lambda表达式,也是为了减小匿名内部类的开发强度;还有后面更加方便的方法引用---- 也就是开发中要尽可能简化,但是学习时就要从最基本的,最复杂的开始,直到后面使用最简化的
Tomcat中携带的jar包就可以让运行项目的时候来解析调用;所以为了不每次建立项目都要配置mysql的jar包,可以将mysql的包也放到lib中去 scope — 领域
${applicationScope.变量名}
${sessionScope.变量名}
${requestScope.变量名}
EL就是将上面JSP的开发三步变成一步即可
$在JQuery中也会使用到
这里可以看一下例子,就之前的取出所有的学生
//1、从请求作用域中获得添加的集合
//2、强制类型转换
List<Student> stuList = (List<Student>)request.getAttribute("stuList");
//将结果输出到响应体
<%=stuList%></td>
那么使用EL表达式就可以
${requestScope.stuList} //自动完成了取出强转并输出
这样之后输出的结果是
[cfeng.oa.entity.Student@255065de, cfeng.oa.entity.Student@67acc4d3, cfeng.oa.entity.Student@7ef1153c, cfeng.oa.entity.Student@13949c01, cfeng.oa.entity.Student@35cc7538, cfeng.oa.entity.Student@34c7a50e, cfeng.oa.entity.Student@42e91e5f, cfeng.oa.entity.Student@752ae697, cfeng.oa.entity.Student@231938bf]
可以看到输出的就是其中包含的9个对象;就相当于println(stuList);
作用域对象别名
Servlet规范中有三个作用域对象; JSP中有4个,除了Servlet的之外,还有一个当前页作用域对象,pageContext,用来与JSTL进行数据共享 JSTL—》pageContext–》JSP
在JSP文件中可以使用的作用域对象一共有4种,其中有session,request,application【内置对象】;类型分别为HttpSession,HttpServletRequest,ServletContext类型
但是还有一种只能在jsp中使用的作用域对象,【当前页作用域对象】 pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
在当前作用域对象中的数据只能在当前页JSP使用,不能共享给其他的JSP和Servlet; 真实开发过程中,主要使用JSTL标签与JSP文件之间的数据共享【实际开发中,JSTL中的数据自动写到pageContext中】
EL表达式提供的作用域对象别名
JSP EL表达式
application ${applicationScope.变量名}
session ${sessionScope.变量名}
request ${requestScope.变量名}
pageContext ${pageScope.变量名} ---------- 不用写Context
引用类型对象的属性EL
之前就实验过,将一个引用对象放到域中【Map】,使用EL表达式输出的就是这个对象的toString的类型;但是实际上真正要输出的是其的方法,所以其实就在变量名后再引用即可,引用外面的${}代表的是输出其中的内容
${pageScope.变量名.属性名} 读取变量对象的属性值并自动写入响应体
比如学生类Student,这里就不用使用其get方法,而是直接.引用属性即可 属性名要完全和类中一致【属性不需要管是否私有,因为这里使用的是反射机制,可以忽略是否私有】
但是一个缺陷就是
EL表达式没有提供遍历集合的方法,无发从作用域对象中读取集合的内容输出
;上面的LIST就是
EL表达式简化
作用域的别名可以直接省略,也就是说Http服务器会从JSP的四大作用域中去寻找这个属性Attribute;所以不能同名
${变量名}