简单的说,JSP 就是 Java + Html,JSP 的出现是为了让 Java Web 应用生成动态页面更容易。
添加 JSP 依赖:
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2version>
<scope>providedscope>
dependency>
和 servlet-api 的依赖类似,jsp-api 依赖同样存在于 Tomcat 中,所以这里范围要设置为provided
。
在 /rwebapp 目录中添加一个简单的 JSP 文件 hello.jsp :
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
hello
<% System.out.println("hello"); %>
<% ... %>
标签中的是 Java 脚本。
访问 http://localhost:8080/login-demo/hello.jsp 就能看到h1
标签中的内容,同时服务端控制台也会输出信息。
JSP 本质上就是一个 Servlet,在项目运行后,/target/tomcat/work
目录下会生成一个hello_jsp.java
文件:
public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
// ...
}
这个文件实际上就是 Tomcat 在收到hello.jsp
的请求后,将hello.jsp
”翻译“为一个 Servlet 类。
查阅 Tomcat 的 API 文档 可以知道,这个类实际上就是HttpServlet
的子类:
public abstract class HttpJspBase
extends HttpServlet
implements HttpJspPage{
// ...
}
它有一个_jspService()
方法,负责处理 HTTP 请求,其主要内容是输出 JSP 中的 HTML 内容以及执行其中的 Java 脚本:
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
// ...
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write(" Title \r\n");
out.write(" hello
\r\n");
out.write(" ");
System.out.println("hello");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
// ...
}
当然,Tomcat 生成的 Java 文件同样要编译为字节码(.class)才能执行。
整个过程可以表示为:
JSP 脚本有三种类型:
<% ... %>
,脚本中的 Java 代码将在_jspService
方法中执行。<%= ... %>
,脚本中的 java 表达式的运行结果将作为out.print()
方法的参数输出到 Html 页面中。<%! ... %>
,脚本中的 Java 代码将作为 Tomcat 生成的 Servlet 的成员变量或成员方法。第一种之前已经看到过,这里看后两种。
在 JSP 中添加:
当前时间:
<%= LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) %>
请求 JSP 页面就能看到显示的时间,这个时间是服务端的 JSP 脚本生成并写入 Html 中的。
在 /target/Tomcat/.../hello_jsp.java
的_jspService
方法中可以看到:
out.print( LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) );
还可以用<% ... %>
定义的局部变量进行输出,比如:
<% LocalDateTime now = LocalDateTime.now(); %>
当前时间:
<%= now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) %>
可以用<%! ... %>
定义成员方法,并由<% ... %>
或<%= ... %>
中的 Java 代码调用:
<%!
String getNowTimeStr(){
return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
%>
当前时间:
<%= this.getNowTimeStr() %>
在下面的案例中,用 JSP 实现一个简单的品牌列表展示页面。
先导入表结构和测试数据 tb_brand.sql。
用 MyBatis 实现持久层和服务层。
实现用于展示品牌列表的 JSP 页面 brand.jsp:
<%@ page import="cn.icexmoon.logindemo.service.BrandService" %>
<%@ page import="cn.icexmoon.logindemo.service.impl.BrandServiceImpl" %>
<%@ page import="cn.icexmoon.logindemo.entity.Brand" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%
BrandService brandService = new BrandServiceImpl();
List brands = brandService.getAllBrands();
%>
序号
品牌名称
企业名称
排序
品牌介绍
状态
操作
<% for(Brand b : brands){ %>
<%= b.getId() %>
<%= b.getBrandName() %>
<%= b.getCompanyName() %>
<%= b.getOrdered() %>
<%= b.getDescription() %>
<% if(b.getStatus()==0){ %>
启用
<% }else{ %>
禁用
<% } %>
修改 删除
<% } %>
在 JSP 中调用了服务层的代码,需要使用<%@ page import="..." %>
导入到 Tomcat 生成的 Servlet 类中。
此外获取到品牌列表数据后,这里通过循环遍历的方式输出表格内容。
将 Java 代码和 HTML 混合起来的 JSP 并不是一个好的解决方案,可读性差。MVC 思想中,将视图(页面展示)和数据准备分离是很重要的一条原则。当然使用 JSP 也可以实现视图和数据的分离,比如将数据准备放在 Servlet 中,在 JSP 中加载数据和页面渲染。
但是加载数据和页面渲染同样要使用 JSP 脚本,可以使用 EL 和 JSTL 两种技术来进行简化。
EL(Expression Language,表达式语言)用于简化在 JSP 中获取数据的操作。
EL 中,可以用${name}
表示一个名称为name
的域属性值。这里所谓的”域“分为以下几种:
我们之前使用的HttpServletRequest.setAttribute
就是设置request
域的属性,request
域的属性对当前 HTTP 请求都有效,所以可以用于多个 Servlet 流转时传递信息。
一个简单示例:
@WebServlet("/brand/list")
public class ListController extends HttpServlet {
private BrandService brandService = new BrandServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Brand> brands = brandService.getAllBrands();
request.setAttribute("brands", brands);
request.getRequestDispatcher("/demo.jsp").forward(request, response);
}
// ...
}
demo.jsp 的内容:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
Title
${brands}
这里<%@page isELIgnored="false" %>
是为了开启 EL 表达式解析功能。
此时请求 /brand/list
就能在页面上直接打印我们传递的List
对象了。
查看 Tomcat 生成的 demo_jsp.java:
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${brands}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null, false));
可以看到是将${brands}
作为 EL表达式解析,以获取属性值。
如果 EL 表达式不起作用,可以检查 web.xml 中的
web-app
版本,在低版本中默认不开启 EL 表达式。具体相关内容可以阅读这篇文章。
这里获取属性值的规则是按照域的大小从小到大获取:
即先检查 Page 中有没有该名称的属性,再检查 Request,以此类推,直到找到为止。
JSTL(Jsp Standard Tag Library,JSP 标准标签库)是用于取代 JSP 中常见 Java 代码的一组标签库。
要使用 JSTL 需要引用依赖:
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>taglibsgroupId>
<artifactId>standardartifactId>
<version>1.1.2version>
dependency>
使用 JSTL 标签替换 JSP 脚本:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
序号
品牌名称
企业名称
排序
品牌介绍
状态
操作
${b.id}
${b.brandName}
${b.companyName}
${b.ordered}
${b.description}
启用
禁用
修改 删除
JSP 的内容简略了很多,并且可读性更强,更接近于 HTML 的风格。
更多的 JSTL 标签介绍可以阅读这里。
用上边介绍的内容可以在之前示例的基础上进一步实现对品牌的增加、删除和修改。
实现并不复杂,可以查看视频我参考我实现的完整示例。
The End,谢谢阅读。
本文的完整示例可以从这里获取。