JSP(全称 Java Server Pages,Java 的服务器页面)是由 Sun 公司专门为了解决动态生成 HTML 文档的技术。
jsp 的主要作用是代替 Servlet 程序回传 html 页面的数据。
如果我们要往客户端输出一个页面,我们可以使用 Servlet 程序来实现,但是十分不方便和繁琐。 并且有些复杂的页面上千上万行,那就更加的困难,开发成本和维护成本都极高。 所以 sun 公司推出一种叫做 jsp 的动态页面技术帮助我们实现对页面的输出繁锁工作。
选中 Web目录,右键创建一个 jsp 文件
输入 jsp 页面的文件名
在 body 标签中添加你想要显示的文本内容
然后启动Tomcat,在浏览器中输入 jsp 页面的访问地址
jsp 页面的访问地址和 html 页面的访问路径一样 http://ip地址:端口号/工程名/文件名
注意:jsp 页面的访问千万不能像 HTML 页面一样,直接手拖到浏览器中或是直接用地址名访问。只能通过启动 Tomcat 服务器再用浏览器访问 jsp 页面
jsp 页面本质上是一个 Servlet 程序。
当我们第一次访问 jsp 页面的时候,Tomcat 服务器会帮我们把 jsp 页面翻译成为一个 java 源文件。并且把它进行编译成为.class 字节码程序。我们可以通过启动Tomcat时IDEA的输出,找到Using CATALINA_BASE这一项,复制后面的地址到我的电脑的地址栏,再逐步点进work/Catalina/localhost/项目名org/apache/jsp
,就可以找到我们的jsp页面翻译后的java源文件。我们打开 java 源文件不难发现其里面的内容是:
我们跟踪原代码发现,这个jsp翻译出来的类继承了HttpJspBase 类,查看HttpJspBase的源码发现,它直接地继承了 HttpServlet 类。也就是说:jsp 翻译出来的 java 类,它间接了继承了 HttpServlet 类。也就是说,jsp页面翻译出来的是一个 Servlet 程序。
总结:jsp 就是 Servlet 程序。
当我们访问 一个 xxx.jsp
文件后,翻译成 java 文件的全名是 xxx_jsp.java
文件
大家也可以去观察翻译出来的 Servlet 程序的源代码,不难发现,其底层实现,也是通过输出流,把 html 页面数据回传给客户端:
jsp 的 page 指令可以修改 jsp 页面中一些重要的属性,或者行为。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
page指令中有如下属性:
/
斜杠打头,它表示请求地址为 http://ip:port/工程路径/
,映射到代码的 Web 目录格式:
<%! java 代码 %>
作用:可以给 jsp 翻译出来的 java 类定义全局变量、方法、静态代码块和内部类等。 (几乎可以写在类的内部写的代码,都可以通过声明脚本来实现)
格式: <%=表达式 %>
作用:表达式脚本用于向页面输出内容。
案例:
<%=12 %> <br> <%=12.12 %> <br> <%="我是字符串" %> <br> <%=map%> <br> <%=request.getParameter("username")%>
和翻译出来的java源代码对照:
格式:
<% java 代码 %>
作用:代码脚本里可以书写任意的 java 语句。
案例:
和翻译出来的java源代码对照:
练习1:在浏览器中输出九九乘法口诀表
练习2:jsp 输出一个表格,里面有 10 个学生信息
Student类:
public class Student { private Integer id; private String name; private Integer age; private String phone; public Student() { } public Student(Integer id, String name, Integer age, String phone) { this.id = id; this.name = name; this.age = age; this.phone = phone; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", phone='" + phone + '\'' + '}'; } }
Servlet:
import com.fox.bean.Student; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class SearchStudentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 使用for循环生成查询到的数据做模拟 List<Student> studentList = new ArrayList<Student>(); for (int i = 0; i < 10; i++) { int t = i + 1; studentList.add(new Student(t,"name"+t, 18+t,"phone"+t)); } // 保存查询到的结果(学生信息)到request域中 req.setAttribute("stuList", studentList); // 请求转发到showStudent.jsp页面 req.getRequestDispatcher("/showStudent.jsp").forward(req,resp); } }
web.xml:
<servlet> <servlet-name>SearchStudentServlet</servlet-name> <servlet-class>com.fox.servlet.SearchStudentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SearchStudentServlet</servlet-name> <url-pattern>/searchStudentServlet</url-pattern> </servlet-mapping>
showStudent.jsp:
<!-- 这是 html 注释 -->
html 注释会被翻译到 java 源代码中,在_jspService() 方法里,以 out.writer 输出到客户端。
用于给脚本中的java代码注释
<% // 单行 java 注释 /* 多行 java 注释 */ %>
java 注释会被翻译到 java 源代码中。
<%-- 这是 jsp 注释 --%>
jsp 注释可以注释掉jsp 页面中所有代码
我们打开翻译后的 java 文件,查看_jspService() 方法:
jsp 中九大内置对象分别是:
九大内置对象,都是我们可以在【代码脚本】或【表达式脚本】中直接使用的对象
四大域对象经常用来保存数据信息。
四个域对象分别是:
域对象就是可以像 Map 一样存取数据的对象。四个域对象功能一样,不同的是它们对数据的存取范围。 虽然四个域对象都可以存取数据。在使用上它们是有优先顺序的。 四个域在使用的时候,优先顺序是他们有效范围从小到大的顺序:
pageContext ====>>> request ====>>> session ====>>> application
案例:测试四个域对象的作用域(有效范围)
context1.jsp:
context2.jsp:
测试 pageContext 作用域:
测试 request 作用域:
测试 session 作用域:
测试 application 作用域:
a.jsp:
浏览器访问a.jsp:
图解 out 流和 writer 流的两个缓冲区如何工作:
当jsp页面所有代码执行完成后会做以下两个操作:
由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下,我们在 jsp 页面中统一使用 out 输出流来进行输出,避免打乱页面输出内容的顺序。
out.write()
输出字符串没有问题,但是输出别的类型会得不到想要的结果out.print()
输出任意数据类型都没有问题(都会转换成为字符串后调用out.write()
输出)结论:在 jsp 页面中,可以统一使用 out.print()来进行输出
开发中,我们可能会写很多网页,但是假设每个网页的页脚信息都是一样的,而我们需要修改页脚信息就要把每个网页都修改一遍很麻烦,于是想到将页脚信息提取出来成为单独一份jsp,再将它包含到每个网页即可。(静态包含或动态包含)
静态包含是把包含的页面内容原封不动地输出到包含的位置。
格式:
<%@ include file="/被包含的 jsp 页面的路径" %>
file 属性指定你要包含的jsp页面的路径
地址中第一个斜杠 /
表示为http://ip:port/工程路径/
映射到代码的web目录
案例:
main.jsp:
footer.jsp:
我们可以在main.jsp翻译出来的java源代码中看到被包含的jsp的内容:
静态包含的特点:
动态包含会把包含的 jsp 页面单独翻译成 servlet 文件,然后在执行到的时候再调用翻译的 servlet 程序,并把计算的结果返回。 动态包含是在执行的时候,才会加载,所以叫动态包含。
格式:
<jsp:include page="/被包含的 jsp 页面的路径"></jsp:include>
案例:
main.jsp:
footer.jsp:
动态包含的特点:
JspRuntimeLibrary.include(request, response, "/footer.jsp", out, false);
动态包含底层原理:
静态包含 | 动态包含 | |
---|---|---|
是否生成 java 文件 | 不生成 | 生成 |
_JspService() 方法中的区别 | 把被包含的内容原封拷贝到 _JspService() 中 | _JspService() 方法中调用JspRuntimeLibrary.include 方法 |
是否可以传递参数 | 不能 | 可以 |
编译次数 | 1 | 被包含的文件数 + 1 |
适用范围 | 适用包含纯静态内容(CSS,HTML,JS),或没有非常耗时操作、或大量 java 代码的 jsp | 包含需要传递参数,或含有大量 java代码的jsp |
在这里需要补充说明一点:我们在工作中,几乎都是使用静态包含。理由很简单,因为 jsp 页面虽然可以写 java 代码,做其他的功能操作,但是由于 jsp 在开发过程中被定位为专门用来展示页面的技术。也就是说,jsp 页面中,基本上只有 html,css,js,还有一些简单的 EL,表达式脚本等输出语句,所以我们都使用静态包含。
格式:
<jsp:forward page="/转发的路径"></jsp:forward>
相当于 request.getRequestDispatcher("/xxxx.jsp").forward(request, response);
的功能。
public interface ServletContextListener extends EventListener { // 在 ServletContext 对象创建之后马上调用,做初始化 public void contextInitialized(ServletContextEvent sce); // 在 ServletContext 对象销毁之后调用 public void contextDestroyed(ServletContextEvent sce); }
如何使用 ServletContextListener 监听器监听 ServletContext 对象?
使用步骤如下:
案例:
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象被创建了"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); } }
web.xml:
<listener> <listener-class>com.fox.servlet.MyServletContextListener</listener-class> </listener>
启动web工程:
停止web工程: