前面讲解了Servlet,了解了Servlet的继承结构,生命周期等有了一些比较详细的了解,但是我们会发现在Servlet中编写一些HTML代码,需要使用out.println(HTML)逐行打印,如果html代码十分繁多那么编写代码就会十分繁琐; 因此就出现了JSP,来解决这样的问题,JSP中的内容就是html,但是能够嵌套java语言,现在来详细了解一下JSP。
什么是JSP
JSP全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计,它使用JSP标签在HTML网页中插入Java代码。tomcat获得JSP文件后,先将JSP转成servlet,变成xxx.java(servlet源码),然后编译成class文件最后运行class文件将结果输出到浏览器端。
特点:
- Servlet特点:在Java源码中嵌入html源码
- JSP特点:在html源码中嵌入java代码
通过一个简单的例子进行讲解
新建一个method.jsp
<%--
Created by IntelliJ IDEA.
User: chen
Date: 2019/4/8
Time: 10:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>this is title</title>
</head>
<body>
<h3>今天天气很好适合学习</h3>
</body>
</html>
运行后可以在文件夹如下
看到转换后的java代码
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/9.0.17
* Generated at: 2019-04-08 06:42:31 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.*;
public final class method_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 = null;
}
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("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>this is title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<h3>今天天气很好适合学习</h3>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
} 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);
}
}
}
看116行到126行就应该理解,jsp就是一个可以嵌套java的html页面,tomcat服务器通过将jsp页面转换为java代码后编译运行,对于我们而言,只需要书写html代码即可。
另外可以注意到method_jsp继承自org.apache.jasper.runtime.HttpJspBase,通过查看HttpJspBase源码可以看到
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage
即HttpJspBase继承自HttpServlet,看到了一个熟悉的类,HttpServlet,我们编写Servlet时就是继承自该类,这里也是继承HttpServlet,并且HttpJspBase的源码会发现,生命周期也是有init()方法,service()方法,destory()方法,相当于_jspService()方法就是servlet的service()方法的执行,所以说JSP也是一个servlet。
注意:
jsp 生成java源码,默认第一次生成,之后直接执行,除非内容修改,具体点说,由于JSP只会在客户端第一次请求的时候被编译,因此第一次请求JSP时会感觉比较慢,而之后的请求因为不会编译JSP,所以速度就快多了,如果将Tomcat保存的JSP编译后的class文件删除,Tomcat也会重新编译JSP。在开发Web程序的时候经常需要修改JSP,Tomcat能够自动检测到JSP程序的改动,如果检测到JSP源代码发生了改动,Tomcat会在下次客户端请求JSP时重新编译JSP,而不需要重启Tomcat,这种自动检测功能默认是开启的,检测改动会消耗少量的时间,在部署web应用程序的时候可以在web.xml中将它关掉。这也就是为什么我们能够在jsp页面直接修改内容,而不用重新启动服务器的原因。
因为JSP就是servlet,那么生命周期也就是跟serlvet一样。
JSP和servlet有一点区别就在于:jsp是先部署后编译,而servlet是先编译后部署。
JSP语法
JSP语法就两个东西,模板数据和元素。其中
模板数据就是JSP中的HTML代码,它的内容给是固定的,无论程序如何运行模版数据输出到客户端浏览器时都不会发生改变,当我们创建一个JSP时,模版就已经固定了。
元素又包括脚本,指令,标签。接下来我们重点学习jsp中的元素。
jsp脚本
使用<% 编写java代码 %>,中间java代码必须遵循Java语法
<%
int a = 1;
int b = 2;
out.println(a+b); //打印到浏览器端
System.out.println(a+b); //打印到后台
%>
其中out.println和九大内置对象中的out对象有关接下来讲
使用<%=xxx %>来输出结果
对于上面的java代码也可以使用
<%=a+b%>
注意不要在后面加分号;
其实两种方式在转译后的servlet中都变为out.println(a+b)来完成。
jsp注释
- 前端代码注释:<!-- --> 会被转译,也会被发送到浏览器端,但不会被浏览器执行
- java代码注释:// /* */ 会被转译,但不会发送到浏览器端,也不会被浏览器执行
- jsp注释:<%-- --%> 不会被转译,更不会被发送到浏览器和被浏览器执行
jsp中声明方法和属性(全局变量)
注意,之前我们写的所有html代码和java脚本代码最终转译成servlet时都会是在_jspService方法中,但是如果我们想要使用全局属性或者其他方法时需要使用
<%!属性或方法%>
jsp指令
JSP指令格式:<%@ directive {attribute=value}* %>
解释:
directive:指令名称,例如page指令
attribute=value:紧跟指令名称后面的就是各种属性,以键值对的形式书写
*:代表后面能跟0个或多个属性。
page指令 用来进行jsp的页面设置和转译配置
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
上面是我们创建jsp时自动生成的,可以看到page指令后面有两个属性,但这其实并不全面,page指令允许的属性如下表所示
include指令
<%@ include file="relativeURL"%>
relativeURL:本应用程序内另一个JSP文件或者HTML文件的相对路径,
例如,网址内所有页面均有一个统一风格的导航栏和页脚版权,那么就可以使用该指令将其包含进来
特点:include指令会将引入的jsp文件和当前jsp文件转译成一个java文件(servlet)使用,在网页中也就显示了合并后的结果。
注意:因为是合并两个jsp文件,所以java代码块不能使用同名变量。
而等下会讲到的一个JSP标签
<jsp:include page="relativeURL">
作用跟include指令一样,但是不同的是,这种方式是将引入的jsp文件单独编译,然后在当前转译好的jsp文件中调用引入的jsp的编译文件,因为是单独编译,所以两个jsp文件中的java代码块可以使用同名变量。
taglib指令
JSP支持标签技术,后面会讲到标签的用法,jstl标签库的使用等,
作用:用来指明JSP页面内使用的JSP标签库,taglib指令有两个属性,uri为类库的地址,prefix为标签的前缀
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
jsp标签
标签分为JSP自带内置的标签、通过taglib指令来使用JSP标签库或者自定义标签。
JSP内置的标签就被称为JSP行为(JSP Actions)。只要书写很少的标记代码就能使用JSP提供的丰富功能,JSP行为其实是对常用的JSP功能的抽象与封装,可以取代jsp脚本,让JSP中就少一些嵌入java代码的地方。
格式:<jsp:elements {attribute="value"}* />
jsp:标签的前缀,说明是jsp内置的标签,
elements:行为的名称,
attribute=value:使用键值对来编写属性
*:能指定0个或多个属性对
<jsp:include />行为
include行为用于运行时包含某个文件,如果被包含的文件为JSP程序,则先会执行JSP程序,然后在把执行的结果包含进来。 作用是跟include指令一样的,唯一的区别就在于,include指令是将被包含的文件的源码加入到了本JSP程序中,然后在进行编译,属于静态包含,而include行为只是将被包含的文件的运行结果包含进自己。属于动态包含
<jsp:forward />行为
实现请求转发功能,Servlet中通过request.getRequestDispatcher("someServlet").forward(request,response);而在JSP中也能够实现相同的功能,只不过用的是<jsp:forward />行为,实际上forward行为就是对其进行了封装。
<jsp:forward page="someServlet">
<jsp:param name="param1" value="value1"/>
<jsp:param name="param2" value="value2"/>
</jsp:forward>
page:需要跳转到的页面或者servlet、 <jsp:param/>参数行为,带一些参数过去,name、value是以键值对的形式带过去的
Java bean行为
是一组与Java Bean 相关的行为,包括useBean行为、setProperty行为、getProperty行为等
Java Bean就是普通的Java类,也被称为POJO,只有私有的属性与对应的getter方法和setter方法,注意其中当私有的属性为boolean类型时,习惯上一般把getter方法写成isXxx();而不是getXxx();
useBean行为
<jsp:useBean id="beanObject" class="className" scope="Value">
作用:在jsp中定义一个java bean对象,
id:指明Java Bean对象的名称,JSP中可以使用该名称引用该Java Bean对象,相当于给new出来的对象取一个变量名,
class:Java Bean类的全名
scope:该java bean对象的作用范围,可以写的就四个,也就是JSP的四大作用域,page、request、session、application
- page:只能在当前JSP页面使用,如果不在JSP页面,那么就会失效
- request:这个前面学过,A页面请求转发到B页面,那么使用的是同一个request,那么A,B页面都算是request的作用域,也就是通过请求转发的页面都是其作 用域
- session:该作用域在一个web项目下任何位置应该读访问的到,只要cookie不关闭,并且cookie设置 的访问路径为"/",
- application:其实就是Servlet中的servletContext,服务器下的所有项目都能访问到。
setProperty行为
<jsp:setProperty name="beanName" property="propertyName" value="">
对Java Bean对象进行属性的设置
name:java bean对象的名称,也就是在useBean行为中的id
property:对象中的属性名,
value:要对其属性进行赋值的值
getProperty行为
<jsp:getProperty name="beanName" property="propertyName" />
获取JavaBean对象的某个属性值
name:java bean 对象的名称,也就是在useBean行为中的id
property:对象的属性名
使用demo:
package a;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
<body>
<!-- 创建一个新的javabean对象user,会先判断在page作用域内是否有叫user对象的javabean,如果有则取它,如果没有则创建新的javabean对象 -->
<jsp:useBean id="user" class="a.User" scope="page"></jsp:useBean>
<!-- 对javabean对象的username进行赋值 -->
<jsp:setProperty property="username" name="user" value="wu"/>
<!-- 获取javabean对象的username属性 -->
<jsp:getProperty property="username" name="user"/>
</body>
<jsp:directive/>行为
directive行为,就相当于JSP指令,比如<jsp:directive.page/>相当于<%@ page %>指令,等等其它指令是一样的书写格式。
jsp九大内置对象和四大作用域
九大内置对象
内置对象:jsp文件在转译成其对应的servlet文件时自动生成并声明的对象。我们在jsp页面中直接使用即可。
pageContext:页面上下文对象,封装了其他内置对象,封存了当前jsp运行信息。 注意:每个jsp文件有一个pageContext对象
request:封存当前请求数据的对象,由tomcat服务器创建 一次请求
session:此对象用来存储用户的不同请求的共享数据。 一次会话
application:就是servletContext,一个项目只有一个,存储所有用户共享数据的对象。 项目内
response:响应对象,用来响应处理结果给浏览器。 设置响应头、重定向
out:响应对象,jsp内部使用。带有缓冲区的响应对象,效率高于response对象
page:代表当前jsp对象,相当于java中的this
exception:异常对象,存储了当前的异常信息。注意:使用此对象需要在page指定中使用属性isErrorPage=“true”开启
config:也就是ServletConfig,主要用来获取web.xml中的配置数据,完成一些初始化数据的读取。
四大作用域对象
其实就是九大内置对象中的四个,之所以称之为作用域对象,是因为他们能够进行数据的流转
pageContext:当前页面,解决了在当前页面内的数据共享问题。获取其他内置对象。
request:一次请求。一次请求的servlet共享数据。通过请求转发可以将数据流转给下一个servlet
session:一次会话。一个用户的不同请求的数据共享。将数据从一次请求流转给下一次请求。
application:项目内。不同用户的数据共享问题。将数据从一个用户流转给其他用户。
jsp路径问题总结
相对路径完成跳转但是:
1.资源的位置不可随意更改
2.使用../进行上级文件夹的跳出,使用比较麻烦。
绝对路径完成跳转(推荐):
/虚拟项目名/项目资源路径
其中第一个/表示服务器根目录,相当于localhost:8080
jsp中自带的全局路径声明:
myeclipse创建jsp文件时会自动生成如下代码:
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"
%>
使用<base href="<%=basePath%>">后相当于http:127.0.0.1:8080/虚拟项目名/ 之后引用文件只需要直接书写文件位置即可
但是在idea中创建jsp文件并不会自动生成,而且只写文件位置容易不清楚,所以还是锐减第二种方式使用绝对路径。
JSP中还有其他两大块内容
一个是EL表达式,很重要,另一个是jstl标签库的使用,也很重要,下一篇文章中讲到。