JSP
1.概述
在动态网页开发中,经常需要动态生成HTML内容,例如,一篇新闻报道的测览次数需要动态生成。这时,如果使用 Servlet 来实现 HTML 页面数据的改变,需要调用大量的输出语句,从而使静态内容和动态内容混合在一起,导致程序非常臃肿。为了克服 Servlet 的这些缺点,Oracle(Sun)公司推出了JSP技术。
1.1.什么是JSP
JSP全名是 Java Server Page,它是建立在 Servlet 规范之上的动态网页开发技术。
在JSP文件中,HTML代码与Java代码共同存在,其中,HTML代码用来实现网页中静态内容的显示,Java代码用来实现网页中动态内容的显示。为了与普通HTML有所区别JSP文件的扩展名为.jsp
2.JSP快速了解
2.1.Servlet 输出 HTML
public class HtmlServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<h1>Hello World</h1>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
2.2.创建JSP界面
创建新的项目,然后再项目的WebContent目录下新建simple.jsp文件,如下
eclipse 提供了新建jsp界面的功能,在WebContent目录右键,New-JSP File
新建好界面后,在 节点里面添加如下代码:
访问jsp界面,其中输出了时间信息,其中每次查看时间都不同,会显示当前时间,这是因为simple.jsp是一个动态网页,它的效果实际上是有服务器程序实现的。
2.3.分析JSP所生成的Servlet代码
当用户第一次访问JSP页面时,该页面都会被 JspServlet 翻译成一个 Servlet 源文件,然后将源文件编译为 .class 文件。 Servlet 源文件和 .class 文件都放在硬盘\Workspace\eclipse.metadata.plugins\org.eclipse.wst.server.core\tmp1\work\Catalina\localhost\Things\org\apache\jsp
每个人的可能在有点稍许不同,有的work目录是在tmp2,也可能有多个tmp0和tmp1,所以这个可以根据个人情况查找。
idea的web项目不是直接将webapp放在tomcat容器中。而是将生成的webapp与tomcat按照idea的“技术”形成连接
具体路径为:注意需要给项目设置项目名开头的访问路径
C:\Users\登录名.IntelliJIdea2017.2\system\tomcat\Tomcat-pure_工程名\work\Catalina\localhost\appcontext名称\org\apache\jsp
3.JSP基本语法(掌握)
JSP界面可以按照编写 HTML 的方式来编写其中可以包含 HTML 文件的所有静态内容,在静态的HTML内容之中可以嵌套JSP的其它各种元素来产生动态内容和执行业务逻辑,JSP页面中的静态HTML内容被称为JSP模板元素,JSP模板元素定义了网页的基本骨架,即定义了页面的结构和外观。
3.1.JSP表达式
JSP表达式用于将程序数据输出到客户端
语法
<%= %>
将=号后面的变量或者表达式计算结果转成一个字符串,然后插入JSP页面输出结果的相应位置处,如刚刚编写的代码
<%=new Date().toLocaleString() %>
3.2.JSP脚本片段
JSP脚本片段是值嵌套在<% 和 %>之中的一条或者多条Java程序代码,这些Java程序代码必须严格遵守Java语法规范,否则编译会报错。
简单的脚本片段
<%
int x = 10;
out.print(x);
%>
分开写,单个脚本中的语句可以是不完整的,但是多个脚本片段组合后的结果必须是完整的Java语句
<%
int y = 20;
%>
<%
out.print(y);
%>
3.3.JSP声明
JSP脚本片段编写的代码都被放到了一个叫_jspService()的方法里面,变量成为了局部变量,
有时候我们希望定义的变量是成员变量,这时就可以使用JSP声明。
JSP声明中定义的都是成员方法、成员变量、静态方法、静态属性、静态代码块
语法
<%!
java代码
%>
3.4.JSP注释
<%--这是注释内容--%>
需要注意的是:JSP页面中格式为<%-- 注释信息 --%> 内容不会发布到客户端
4.JSP指令(了解)
为了设置JSP页面中的一些信息,Sum公司提供了JSP指令
page、include、taglib三种指令
4.1.page指令
在JSP页面中,经常需要对页面的某些特性进行描述,例如,页面的编码方式,JSP页面采用的语言等,这时,可以通过page指令来实现
常用属性
格式:
<%@ page 属性名1= “属性值1” 属性名2= “属性值2” …%>
常用属性如下:
- language:jsp脚本中可以嵌入的语言种类
- pageEncoding:当前jsp文件的本身编码
- contentType:response.setContentType(text/html;charset=UTF-8)
- import:指定JSP页面翻译成Servlet资源文件中导入的包或者类
- session:是否jsp在翻译时自动创建session
- errorPage:当当前页面出错后跳转到哪个页面
- isErrorPage:当前页面是一个处理错误的页面
4.2.include指令
有时候,需要在JSP页面静态包含一个文件,例如HTML文件、文本文件等,这时,可以通过 include指令来实现
语法
<%@ include file="被包含的文件地址"%>
5.JSP隐式对象(掌握)
在JSP页面中,有一些对象需要频繁使用,如果每次创建这些对象则会非常麻烦,为此,JSP提供了9个隐式对象,它们是JSP默认创建的,可以直接在JSP页面使用,例如刚开始演示的out对象
名称 | 类型 | 描述 |
---|---|---|
out | javax.servlet.jsp.JspWriter | 用于页面输出 |
Request | javax.servlet.http.HttpServletRequest | 得到用户请求信息, |
Response | javax.servlet.http.HttpServletResponse | 服务器向客户端的回应信息 |
Config | javax.servlet.ServletConfig | 服务器配置,可以取得初始化参数 |
Session | javax.servlet.http.HttpSession | 用来保存用户的信息 |
Application | javax.servlet.ServletContext | 所有用户的共享信息 |
Page | java.lang.Object | 指当前页面转换后的Servlet类的实例 |
pageContext | javax.servlet.jsp.PageContext | JSP的页面容器 |
Exception | java.lang.Throwable | 表示JSP页面所发生的异常,在错误页中才起作用 |
5.1.1.out对象
在JSP页面中,经常需要向客户端发送文本内容,这时,可以使用out对象来实现
使用示例
<%
out.print(new java.util.Date().toLocaleString());
response.getWriter().println("使用response对象输出");
%>
5.1.2.pageContext对象
5.1.2.1.pageContext获取隐式对象
在JSP中,想要获取JSP隐式对象,可以使用pageContext对象,pageContext对象时Javax.servlet.jsp.PageContext类的实例对象,它代表当前JSP页面的运行环境,并提供了一些列用于获取其他隐式对象的方法。
方法 | 功能 |
---|---|
JspWriter getOut() | 获取out隐式对象 |
Object getPage() | 获取page隐式对象 |
ServletRequest getRequest() | 获取request隐式对象 |
ServletResponse getResponse( ) | 获取response隐式对象 |
HttpSession getSession( ) | 获取session隐式对象 |
ServletConfig getServletConfig( ) | 获取config |
ServletContext getServletContext( ) | 获取application隐式对象 |
Exception getException() | 获取exception隐式对象 |
示例,使用pageContext获取request对象
<%
HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();
String ip = request.getRemoteAddr();
out.print(ip);
%>
5.1.2.2.pageContext操作属性
方法名 | 功能 |
---|---|
void setAttribute(String name, Object value, int scope) | 设置pageContext对象的属性 |
Object getAttribute(String name, int scope) | 获取pageContext对象的属性 |
void removeAttribute(String name,int scope) | 删除指定范围内名称为name的属性 |
void removeAttribute(String name) | 删除所有范围内名称为name的属性 |
Object findAttribute(String name) | 从4个域对象中查找名称为name的属性 |
pageContext对象的作用范围有4个值
- pageContext.PAGE_SCOPE:表示页面范围
- pageContext.REQUEST_SCOPE:表示请求范围
- pageContext.SESSION_SCOPE:表示会话范围
- pageContext.APPLICATION_SCOPE:表示Web应用程序范围
使用findAttribute()方法来查找属性是,会依次从page、request、session、application的顺序进行查找,找到立刻返回实际的值,找不到返回null。
示例
<%
pageContext.setAttribute("company", "jf", pageContext.PAGE_SCOPE);
Object name = pageContext.getAttribute("company", pageContext.PAGE_SCOPE);
out.print("公司名称为:" + name);
%>
5.1.3.JSP四个域范围
PageContext常量名 | 描述 | 作用域名称 | 域对象类型 |
---|---|---|---|
PageScope | 当前页面中有效 | pageContext | PageContext |
RequestScope | 一次请求范围 | Request | HttpServletRequest |
Sessionscope | 一次会话范围 | Session | HttpSession |
Applicationscope | 应用范围 | Application | ServletContext |
- page:表示当前页,通常没用。jsp标签底层使用
- request:表示一次请求。通常一次请求就一个页面,但如果使用请求转发,可以涉及多个页面。
- session:表示一次会话。可以在多次请求之间共享数据
- application:表示一个web应用(项目)。可以整个web项目共享,多次会话共享数据
6.JSP标签(了解)
JSP标签元素用来控制JSP的行为,执行一些常用的JSP页面动作。通过标签元素可以实现使用多行Java代码能够实现的效果,如包含页面文件,实现请求转发等
6.1.jsp:include标签
在JSP页面中,为了把其他资源的输出内容插入到当前JSP页面的输出内容中,JSP技术提供了jsp:include标签
语法:
<jsp:include page="relativeURL" flush=”true|false”>
page属性用于指定被引入资源的相对路径, flush属性用于指定是否将当前页面的输出内容刷新到客户端,默认情况下,fush属性的值为 false
使用示例
在项目下新建两个jsp,分别是included.jsp和dynamicInclude.jsp,其中dynamicInclude.jsp引入了included.jsp
included.jsp
<body>
<% Thread.sleep(5000);%>
included.jsp 中的内容;
</body>
dynamicInclude.jsp
<body>
dynamicInclude.jsp 中的内容
<jsp:include page="included.jsp" flush="true" />
</body>
启动 Tomcat服务器,访问地址,发现浏览器首先会显示 dynamicInclude jsp页面中的输出内容,等待5秒后,才会显示 included jsp页面的输出内容。说明被引用的资源 included jsp在当前JSP页面输出内容后才被调用,显示结果如下
注意:虽然include指令和jsp:include标签都能够包含一个文件,但是它们4之间有很大的区别:
< jsp:include>
标签中要引入的资源和当前 JSP 页面是两个彼此独立的执行实体,即被动态引入的资源必须能够被Web容器独立执行。而 include 指令只能引入遵循JSP格式的文件,被引入文件与当前JSP文件需要共同合并才能翻译成一个 Servlet 源文件
<jsp:include>
标签中引入的资源是在运行时才包含的,而且只包含运行结果。而 include指令引入的资源是在编译时期包含的,包含的是源代码
<jsp:include>
标签运行原理与 RequestDispatcher.include()方法类似,即被包含的页面不能改变响应状态码或者设置响应头,而 include指令没有这方面的限制。
6.2.jsp:forward标签
在JSP页面中,经常需要将请求转发给另外一个资源,这时,除了 RequestDispatcher 接口的 forward() 方法可以实现外,还可以通过<jsp:forward>
标签来实现,<jsp:forward>
标签的具体语法格式如下所示:
<jsp:forward page="relativeURL" />
page属性用于指定请求转发到的资源的相对路径,该路径是相对于当前的JSP页面的URL
jspforward.jsp
<body>
<jsp:forward page="welcome.jsp" />
</body>
welcome.jsp
<body>
你好欢迎访问,当前的时间是
<% out.print(new java.util.Date()); %>>
</body>
显示结果
7.案例
7.1.显示列表
添加ListServlet,使用循环生成列表需要的数据,并添加到集合中
public class ListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ArrayList<Message> messages = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Message m = new Message();
m.setId(i);
m.setTitle("标题" + i);
m.setContent("内容" + i);
messages.add(m);
}
req.setAttribute("messages",messages);
req.getRequestDispatcher("/list.jsp").forward(req,resp);
}
}
添加list.jsp,获取到request域中的数据,使用循环进行显示
<table border="1" width="600px" align="center">
<%
ArrayList<Message> messages = (ArrayList<Message>) request.getAttribute("messages");
if (messages != null) {
for (int i = 0; i < messages.size(); i++) {
%>
<tr>
<td align="center"><%=messages.get(i).getTitle() %></td>
<td align="center"><%=messages.get(i).getContent()%></td>
</tr>
<%
}
}
%>
</table>
7.2.图片验证码
使用jsp完成图片验证码的功能
<%@page import="javax.imageio.ImageIO"%>
<%@page import="java.awt.Font"%>
<%@page import="java.awt.Graphics"%>
<%@page import="java.awt.image.BufferedImage"%>
<%@page import="java.util.Random"%>
<%@page import="java.awt.Color"%>
<%@ page language="java" contentType="image/JPEG; charset=UTF-8"
pageEncoding="UTF-8"%>
<%!
//获取随机颜色
Color getRandColor(int fc,int bc){
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r,g,b);
}
%>
<%
//设置页面不缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
//在内存中创建图像
int width = 60;
int height = 20;
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//获取图形上下文
Graphics g = image.getGraphics();
//随机类
Random random = new Random();
//设定背景
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
//设定字体
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
//随机产生干扰线
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 100; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
//随机产生4位验证码
String[] codes = {"2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z"};
String code = "";
for(int i=0;i<4;i++){
String str = codes[random.nextInt(codes.length)];
code += str;
// 将认证码显示到图象中
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.drawString(str, 13 * i + 6, 16);
}
// 将认证码存入SESSION
session.setAttribute("code", code);
// 图象生效
g.dispose();
// 输出图象到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
//加上下面代码,运行时才不会出现java.lang.IllegalStateException: getOutputStream() has already been called ..........等异常
response.getOutputStream().flush();
response.getOutputStream().close();
response.flushBuffer();
out.clear();
out = pageContext.pushBody();
%>