Servlet与JSP

开发Web应用程序技术学习

1. HTTP协议

概念:超文本传输协议(HyperText Transfer Protocol)

作用:规范服务器和客户端数据交互格式

建立在TCP/IP基础上,HTTP协议使用可靠的TCP连接,端口80

特点:

  • 无连接,限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,断开连接,节省传输时间
  • 无状态,HTTP协议对于事务处理没有记忆功能,缺少状态意味着如果后续处理需要前面的信息,必须重传,可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答较快。
  • HTTP/1.1 支持可持续连接

交互流程:

  1. 客户端和服务端建立连接
  2. 客户端向服务端请求数据,发送http协议
  3. 服务器处理请求的http协议,发送响应数据给客户端
  4. 客户端接受响应数据,渲染到浏览器

1.1. 请求方式

HTTP/1.0 定义三种请求方法:

  • GET 请求指定页面信息,返回实体主体,请求数据以?key=value的方式拼接到URL中,不安全,请求数据不能太大(地址栏有限)

  • POST 提交表单等,请求数据包含在请求主体中

  • HEAD 返回的响应没有实体内容,用于获取报头

HTTP/1.1 新增

  • OPTIONS 允许客户端查看服务器性能

  • PUT 从客户端向服务端传送的数据取代指定文档的内容

  • DELETE 请求服务器删除指定页面

  • TRACE 回退服务器收到的请求,主要用于测试或诊断

  • CONNECT HTTP/1.1协议预留给能够将连接改为管道方式的代理服务器

1
2
3
4
5
6
7
8
9
10
11
12
方法_/统一资源标识符_/协议/版本\r\n
请求标头\r\n
\r\n
实体主体(post请求才有)

POST /examples/default.jsp HTTP/1.1
Accept: text/plain; test/html
Accept-Language: en-gb
Connection: Keep-alive
Host: localhost

user=wt&&pwd=123

Connection: Keep-alive 持久连接,使用同一个连接来下载所有资源

1.2. 响应方式

Response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
协议 状态码 描述\r\n
响应标头\r\n
\r\n
实体主体

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html
Content-Length:112

<html>
<head></head>
<body></body>
</html>

状态码

  • 200 OK 请求成功

  • 400 Bad Request 请求有语法错误

  • 404 Unauthorized 请求未经授权,和www-Authenticate一起使用

  • 403 Forbidden 服务器收到请求,拒绝提供服务

  • 404 Not Found 资源不存在

    • servlet别名书写错误
    • 虚拟项目名书写错误
  • 500 Internal Server Error 服务器错误,异常

    • servlet类class路径拼写错误
    • servlet类方法体代码有错误
  • 503 Server Unavailable 服务器当前不能处理客户端请求

e.g.

HTTP基于TCP/IP

TCP用ServerSocket类实现

接收客户端 accpet() –> Socket类

接收 getInputStream()

发送 getOutputStream()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Main {

private void service(){
try {
ServerSocket server = new ServerSocket(8888,1, InetAddress.getByName("localhost"));
Socket client = server.accept();
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"));

BufferedWriter bf = new BufferedWriter(new OutputStreamWriter(out,"UTF-8"));
StringBuilder string=new StringBuilder();
string.append("HTTP/1.1 200 OK\r\n");
string.append("Content-type: text/html; charset=UTF-8\r\n");
string.append("Connection: keep-alive\r\n");
string.append("\r\n");
string.append("<html><head></head><title>return</title>");
string.append("<body>Hello World"+br.readLine());
string.append("</body></html>");
bf.write(string.toString());
bf.flush();
} catch (IOException e) {
e.printStackTrace();
}

}
public static void main(String[] args) {
Main main = new Main();
main.service();

}
}

//客户端
Socket client = new Socket(6666);
client.getOutputStream(new File(""));

2. Tomcat

代码编写的可以根据用户请求实时调用执行对应逻辑代码的容器。

目录:

bin 可执行文件

conf 配置文件

lib 存放库文件

logs 日志

temp 临时文件

webapps web应用

work jsp转换后的servlet文件

运行

1
2
3
$ cd bin
$ sudo ./startup.sh
$ sudo ./shutdown.sh

3. Servlet

狭义的servlet指java语言实现的一个接口,广义的servlet指任何实现了这个servlet接口的类。servlet运行于支持java的应用服务器中,用来扩展基于http协议的web服务器

3.1. 第一个servlet服务

继承HttpServlet,重写service方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String username=request.getParameter("username");
String password=request.getParameter("pwd");

response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.write(username);
out.write(password);
out.flush();
}

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
}

映射

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>log</servlet-name>
<servlet-class>com.runaccpeted.servlet.LoginServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>log</servlet-name>
<url-pattern>/log</url-pattern>
</servlet-mapping>

简单界面

1
2
3
4
5
<form action="./log" method="get">
用户名:<input type="text" name="username" id="username"/><br/>
密码:<input type="password" name="pwd" id="pwd"/><br/>
<input type="submit" value="登陆"/>
</form>

流程:

url: http://localhost:8080/tomcat/log?username=wt&pwd=123

http://服务器地址:端口号/webapps下文件名/?请求数据/web.xml中配置的servlet的url-pattern?请求数据

3.2. URL,URI

URI:Uniform Resource Indentifier 统一资源标识符,/tomcat/log?username=wt&pwd=123

URL:Uniform Resource Location 统一资源定位器 一种具体的URI

http://localhost:8080/tomcat/log?username=wt&pwd=123

URL.openStream() == InputStream 可用于抓取网络信息

3.3. 生命周期

Serlvet在整个tomcat中只有一个实例,第一次被调用,加载进内存init方法执行,到tomcat关闭destory方法执行。

1
2
3
4
5
<servlet>
<servlet-name>log</servlet-name>
<servlet-class>com.runaccpeted.servlet.LoginServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

在web.xml中配置load-on-startup使servlet在tomcat启动时就被初始化,数字1表示加载顺序

3.4. doGet(),doPost(),service()

doGet处理get请求

doPost处理post请求

service处理所有请求,存在service(),优先调用service

1
2
3
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
super.service(request,response);
}

调用父类的service方法时,它处理完本service后调用相应的doGet,doPost方法执行,不存在doGet,doPost方法时则出现405错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//父类service方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}

if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}

}

父类doGet方法

直接响应405

1
2
3
4
5
6
7
8
9
10
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

父类doPost方法

1
2
3
4
5
6
7
8
9
10
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

接口中包括

void init(ServletConfig) 仅第一个请求Servlet时会调用

void service(ServletRequest request, ServletResponse response)

Void destory()

文件结构

webapps

——FirstProject

————WEB-INF

——————classes java文件的class文件放在这里

——————lib

3.5. 乱码

String 转换字符集

1
String str = new String(uname.getByte("iso8859-1"),"utf-8");

request编码格式 –POST请求方式下有效

1
request.setCharacterEncoding("utf-8");

tomcat文件下conf/server.xml

useBodyEncodingForURI=”true”

1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="utf-8" useBodyEncodingForURI="true"/>

3.6. 操作流程

数据流转流程:

  1. 浏览器发起请求到服务器
  2. 服务器接受浏览器请求进行解析,创建request对象存储请求数据
  3. 服务器调用对应的servlet进行请求处理,将response对象作为实参传递给servlet。

Servlet使用流程:

  1. 设置请求编码格式
  2. 设置响应编码格式
  3. 获取请求信息
  4. 处理请求信息
  5. 响应处理结果

3.7. 请求转发

实现多个servlet联动操作处理请求,让servlet职责更加明确

1
request.getRequestDispatcher("相对地址").forward(request, response);

一次请求,地址栏信息不变

3.8. 重定向

解决表单重复提交问题,当前servlet无法处理的请求问题

1
response.sendRedirect("");

两次请求对象,地址栏信息改变

请求中有表单数据,数据不适合重复提交;

数据无法处理,需要重定向到特定资源处理

3.9. ServletRequest

request对象封存当前请求的所有请求信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//请求编码格式
request.setCharacterEncoding("utf-8");
//字节数
int length=request.getContentLength();
//请求主体类型
String mime=request.getContentType();
//数据key不存在返回null
//参数值 一对一
String parameter=request.getContentParameter(name);
//参数值 一对多
String[] values= request.getParameterValues("hobbies");
for(String str:values){
System.out.println(str);
}
//协议
String protocol=request.getContentProtocol();
//URL
String url=request.getRequestURL()
//URI
String uri=request.getRequestURI();
//请求方式
String method=request.getMethod();
//请求头
Enumeration<String> heads=request.getHeaderNames();
while (heads.hasMoreElements()) {
String key=heads.nextElement();
String value=request.getHeader(key);
System.out.println(key+" = "+value);
}

3.10. ServletResponse

response对象用来响应客户端

1
2
3
4
5
6
7
8
9
10
11
//响应头
//同键覆盖
response.setHeader("Content-type","text/html");
//同键不覆盖
response.addHeader("Content-type","text/html");
//编码集
response.setContentType("text/html;charset=utf-8");
//响应状态码
response.sendError(404,"Resource Not Found");
//java.io.PrintWriter
PrintWriter out = response.getWriter(); //传输文本给客户端

3.11. 登录练习

login.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="log" method="post">
用户名<input type="text" name="uname"/>
密码<input type="password" name="pwd"/>
<input type="submit" value="提交">
</form>
</body>

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.runaccpeted.pojo;

public class User {

private String username;
private String password;
private int 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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", id=" + id + "]";
}
}

LoginServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.runaccpeted.servlet;

import java.io.IOException;
import java.io.Writer;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.mysql.cj.protocol.Resultset;
import com.runaccpeted.pojo.User;

public class LoginServlet extends HttpServlet {
Connection conn;
PreparedStatement statement;

@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

request.setCharacterEncoding("utf-8");

response.setContentType("text/html;charset=utf-8");
String uname=request.getParameter("uname");
String pwd=request.getParameter("pwd");
Writer out = response.getWriter();

try {
statement = conn.prepareStatement("select * from user where username=? and password=?");
statement.setString(1, uname);
statement.setString(2, pwd);

ResultSet rs=statement.executeQuery();
User user=null;
while(rs.next()) {
user =new User();
user.setId(rs.getInt(1));
user.setUsername(rs.getString(2));
user.setPassword(rs.getString(3));
}
System.out.println(user);
if (user!=null) {
out.write("登录成功");
}else {
response.sendRedirect("login.jsp");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

@Override
public void destroy() {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

@Override
public void init() throws ServletException {

try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/login?useSSL=false", "root", "mysql123//");
} catch (Exception e) {
e.printStackTrace();
}
}
}

3.12. 资源映射

  1. web.xml
1
2
3
4
5
6
7
8
<servlet>
<servlet-name>LoginSerlvet</servlet-name>
<servlet-class>com.bjsxt.serlvet.LoginSerlvet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginSerlvet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
  1. 注解
1
2
@WebServlet(name="myServlet",urlPatterns={"/login"})
public class myServlet extends HttpServlet{ }

4. Cookie

javax.servlet.http.Cookie;

实现跨多个页面信息的传递,数据共享

对象声明在服务端,存储在客户端

临时存储:存储在客户端内存中,浏览器关闭则cookie失效

定时存储:存储在客户端硬盘中,有效期内符合路径要求的都存在cookie中 cookie.setMaxAge(时间秒);

默认请求中都会携带cookie信息。

cookie.setPath(“相对路径”);设置cookie有效路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Cookie cookie = new Cookie("username",request.getParameter("username"));
//有效期限 10s
cookie.setMaxAge(10);
//发送给浏览器
response.addCookie(cookie);

//取回数据
//key - getName();
//value - getValue();
Cookie[] cookies=request.getCookies();
String recoder = null;
if(cookies!=null){
for(Cookie cookie:cookies){
if(cookie.getName().equals("username")){
recoder = cookie.getValue();
}
}
}

请求头中存有cookie请求头

1
Cookie: JSESSIONID=5071A23663A7EA118B8251498FD21944; uname=abc; Hm_lvt_59cd4bc54e9d484dff9357727e454c80=1578376259,1579197274

5. Session

一个用户的不同请求处理数据共享

用户第一次访问服务器,服务器会创建一个session给此用户,并将session对象的JSESSIONID技术存储到浏览器中,保证用户的其他请求能够获取到同一个session对象,保证不同请求能够获取到共享的数据。

存储在服务器端

有效范围为一次会话,在JSESSIONID和session对象不失效情况下存在于整个项目

依赖cookie技术实现,存储在cookie的临时存储空间中,浏览器关闭则失效,默认失效时间30分钟

5.1. 创建Session

1
HttpSession session=request.getSession();
  • 请求中拥有session的标识符也就是JSESSIONID,则返回其对应的session队形
  • 请求中没有session的标识符也就是JSESSIONID,则创建一个新的session对象,并将其JSESSIONID作为cooike数据存储在浏览器内存中
  • session失效则重新创建一个session对象,并将其JSESSIONID存储在浏览器内存中
1
2
3
4
5
//false,没有则返回null
HttpSession session=request.getSession(false);

//Cookie: JSESSIONID=23F98ACE9F815A5191E2E40044D898F9;
System.out.println(session.getId());

5.2. Session失效

设置失效时间 5s

1
session.setMaxInactiveInterval(5);

强制session过期

1
2
HttpSession session = request.getSession();
session.invalidate();

所有项目 conf/web.xml

1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>

5.3. Session使用

存值, value可以是任意实现了Serializable的Object

1
session.setAttribute(String name,Object value);

可用于存储登录的用户对象,解决登录重定向后的信息关联,不同请求的数据共享

所有key

1
java.util.Enumeration<String> getAttributeNames();

servlet容器会为每个HttpSession生成一个唯一标识符,并将标识符作为token JSESSIONID发送给浏览器,在后续请求中浏览器将token传回服务器使得服务器知道是哪个用户在发出请求。

6. ServletContext

实现不同用户的数据共享

ServletContext对象由服务器创建,一个项目只有一个对象

可用于统计网站访问量

6.1. 创建对象

1
2
3
4
5
6
//方式1
ServletContext c1=request.getServletContext();
//方式2
ServletContext c2 =this.getServletConfig().getServletContext();
//方式3
ServletContext c3 = request.getSession().getServletContext();

6.2. 存储数据

1
2
3
4
ServletContext c1=request.getServletContext();

c1.setAttribute(String key,Object value);
Object = c1.getAttribute(value);

6.3. 应用场景

配置全局数据

1
2
3
4
<context-param>
<param-name>key</param-name>
<param-value>value</param-value>
</context-param>

取值

1
2
3
4
5
6
7
ServletContext c1=request.getServletContext();

//单个键
c1.getInitParameter("key");

//所有键枚举
Enumeration<String> str =c1.getInitParameterNames();

获取项目根目录下资源的绝对路径

/Users/Learning/Resource/workspace/myEclipse/.metadata/.me_tcat85/webapps/tomcat/index.jsp

1
2
3
ServletContext c1=request.getServletContext();

c1.getRealPath("index.jsp");

获取流对象

1
2
3
ServletContext c1=request.getServletContext();

InputStream in=c1.getResourceAsStream(String path);

7. ServletConfig

作为servlet的专属配置对象,每个servlet拥有一个ServletConfig对象,用来获取web.xml中的配置信息

1
2
3
4
5
6
<servlet>
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
</servlet>

1
2
ServletConfig config = this.getServletConfig();
String value = getInitParameter("key");

8. server.xml

Web容器按ServletContext->context-param-> listener-> filter-> servlet顺序加载组件,这些元素在web.xml文件中为任意位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<Server port="8005" shutdown="SHUTDOWN">
<Listener
className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener
className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="on" />
<Listener
className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener
className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<Service name="Catalina">
<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="utf-8"
useBodyEncodingForURI="true"/>
<!-- 服务器集群 -->
<Connector port="8009"
protocol="AJP/1.3"
redirectPort="8443" />
<!-- 默认网站地址为localhost-->
<Engine name="Catalina"
defaultHost="localhost">
<!-- 设置到webapps文件夹下寻找项目 -->
<Host name="localhost"
appBase="webapps"
unpackWARs="true"
autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>

9. 热部署

写在Host标签中

在集成开发环境下修改项目代码,tomact自动再次加载项目

1
2
3
4
5
<Engine>
<Host>
<context path="/项目别名" reloadable="true" docBase="项目绝对路径到项目名"/>
</Host>
</Engine>

10. JSP

java服务器页面 Java Server page。 它本质还是一个servlet

配置项在tomcat的web.xml中

1
2
3
4
5
6
7
8
9
10
11
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>

编写index.jsp

在work/Catalina/localhost/project/org/apache/jsp下有

login_jsp.java

login_jsp.class

index_jsp.java源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class login_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 {

final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
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("\n");
out.write("<!DOCTYPE html>\n");
out.write("<html>\n");
out.write("<head>\n");
out.write("<meta charset=\"UTF-8\">\n");
out.write("<title>登录</title>\n");
out.write("</head>\n");
out.write("<body>\n");
out.write("<form action=\"log\" method=\"post\">\n");
out.write("用户名<input type=\"text\" name=\"uname\"/>\n");
out.write("密码<input type=\"password\" name=\"pwd\"/>\n");
out.write("<input type=\"submit\" value=\"提交\">\n");
out.write("</form>\n");
out.write("</body>\n");
out.write("</html>");
} 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);
}
}
}

10.1. 注释

前端注释 会被转译,也会被发送,但不会被浏览器执行

java注释 会被转译,不会被servlet执行

jsp注释 不会被转译

10.2. jsp存在的隐式参数

对象 类型
request javax.servlet.HttpServletRequest
response javax.servlet.HttpServletResponse
out javax.servlet.jsp.JspWriter
session javax.servlet.http.httpSession
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
pageContext javax.servlet.jsp.PageContext 页面上下文,封存了其他内置对象。封存了当前jsp的运行信息
page javax.servlet.jsp.HttpJspPage
exception java.lang.Throwable – <%@page isErrorPage=true %>

PageContext可以设定存取范围

page 当前页

request 当前ServletRequest

session 当前HttpSession

application ServletContext

e.g.

1
2
3
<%
pageContext.setAttribute("username",user,PageContext.REQUEST_SCOPE);
%>

10.3. page指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
	<!-- jsp要被转译的语言 -->
<%@page language="java"%>
<!-- 转译的java文件要导入的包 -->
<%@page import="java.util.Date,java.util.List" %>
<!-- 则该servlet无session隐式对象,默认开启 -->
<%@page session=false %>
<!-- out缓冲区大小,默认为8kb -->
<%@page buffer=8 %>
<!-- 缓冲区满时自动刷新 -->
<%@page autoFlush=true %>
<!-- getServletInfo方法返回值 -->
<%@page info=“ %>
<!-- 错误跳转网页 -->
<%@page errorPage="error.html" %>
<!-- 页面是否负责处理错误 -->
<%@page isErrorPage=true %>
<!--response内容类型 -->
<%@page contentType="text/html; charset=UTF-8" %>
<!--页面字符编码-->
<%@page pageEncoding="UTF-8" %>
<!-- 忽略EL表达式 -->
<%@page isELIgnored=true %>
<!-- 页面脚本语言 -->
<%@page language="java" %>

10.4. 脚本

10.4.1. 局部代码块

<% %>

局部代码块中的java代码会被原样转译到对应的servlet文件的_JspService方法中,进行逻辑判断时书写麻烦,阅读困难。

1
<% System.out.println("index"); %>

10.4.2. 表达式

<%= %>

结果会嵌入out.write() 中

不需要使用分号

1
<%=Hello World %>

10.4.3. 全局代码块

<%! %>

1
2
3
4
5
6
7
<%! 
public Date getDate(){
return new Date();
}
%>

<%=getDate()%>

10.4.4. 动作

10.4.4.1. 创建对象

1
2
<jsp:useBean id = "today" class="java.util.Date">
<%=today%>

10.4.4.2. 创建属性

1
2
<jsp:setProperty name="username" property="name" value="abc"/>
<jsp:getProperty name="username" property="name"/>

10.5. 引入

降低代码的冗余,便于代码维护

10.5.1. 静态引入

被引入的jsp不会被编译

1
<%@include file="/copyrigth.jsp" %>

合并转译成一个java文件访问,故文件中不能同时存在同名变量。

10.5.2. 动态引入

会单独生成jsp对应的java文件,引用java文件中的方法

1
2
3
<jsp:include page="menu.jsp">
<jsp:param name="text" value="How are you?"/>
</jsp:include>

include指令发生在页面转换时,include动作发生在请求时

include指令处理的可以是静态页面,include动作处理的是jsp页面

10.6. 跳转

一次请求,地址栏网址改变

1
2
3
<jsp:forward page="/login.jsp">
<jsp:param name="aaa" value="str"/>
</jsp:forward>

Servlet中对应代码

1
2
3
4
if(true){
_jspx_page_context.forward("/login.jsp"+"?"+org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("aaa",request.getCharacterEncoding())+"="+org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("str",request.getCharacterEncoding()));
return;
}

10.7. 资源路径

1
2
3
4
5
6
7
8
<% String path = request.getContextPath(); %>
<% String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath %>>">
</head>
</html>

11. ajax

实现异步访问

ajax.onreadystatechange 会调用4次

  • 未调用open方法时

  • 调用open方法时

  • 调用send方法时

  • 返回结果时

ajax.readyState返回第几次

ajax.status 返回响应状态码 200 404 500

send() 用于post请求中,输入参数

还要添加 setRequestHeader 以键值对的方式传递数据

1
2
3
ajax.open("post","ajax",true);
ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajax.send("name=w&&age=3");

ajax.open(“post”,”ajax”,true);

ajax.open(method,urlPattern,async);

async - true 异步; - false 同步

应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<head>
<style type="text/css">
#showdiv{
width:500px;
height:100px;
border:1px #000 solid;
}
</style>
<script type="text/javascript">
function getData(){
var ajax;
if(window.XMLHttpRequest){
ajax=new XMLHttpRequest();
}else if(window.ActiveXObject){
ajax=new ActiveXObject("Msxml2.XMLHTTP");
}

ajax.onreadystatechange = function(){
if(ajax.readyState==4){
var result=ajax.responseText;
//alert(result);
var showdiv = document.getElementById("showdiv");
showdiv.innerHTML=result;
}
}
ajax.open("get", "ajax", true);
ajax.send(null);
}
</script>
</head>
<input type="button" value="搜索" onclick="getData()"/>
<div id="showdiv"></div>

11.1. JSON

获取json对象

gson-2.8.0.jar

1
2
3
import com.google.gson.Gson;
//响应为json数据
response.getWriter().write(new Gson().toJson(user));

aja响应数据

1
2
var result = ajax.responseText();
eval("var u="+result);

12. EL

Experssion Language

${ } 运算结果强制为String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<% (User)request.getAttribute("user").getName();%>
${user[name]}
<!-- 两者表达式相同 -->
${user.name}

<% request.getParameter("uname")%>
${param.uname}

<!-- String数组-->
<% request.getParameterValues("hobbies");%>
${paramValues.hobbies}

${header}
${header["键名"]}
${headerValues["accpet-language"][0]}

<!-- 所有cookie对象的map集合 -->
${cookie}
<!-- 指定cookie对象 -->
${cookie.JSESSIONID}
<!-- 指定cookie对象存储的数据的键名 -->
${cookie.JSESSIONID.name}
<!-- 指定cookie对象存储的数据的值 -->
${cookie.JSESSIONID.value}

12.1. 隐式对象

对象 描述
pageContext 当前页面javax.servlet.jsp.PageContext (9大隐式对象)${pageContext.session.id}
initParam 所有context初始化参数并以参数名称为键的map
param 所有请求参数并以参数名称为键的map ${param.user}
paramValues 所有请求参数并以参数名称为键的map 返回的是数组
header 所有请求标头并以标头名称为键的map ${header[“accept-language”]}
cookie 当前请求对象中所有Cookie对象的map
applicationScope ServletContext对象中所有属性并以属性名称为键的map
sessionScope httpSession对象中所有属性并以属性名称为键的map

12.2. el查找顺序

作用域从小到大

pageContext –> request –> session –> application

指定作用域

pageScope –> requestScope –> sessionScope –> applicationScope

12.3. 逻辑运算

1
2
3
4
${1+2} 				==3
${sex==1?'男':'女'}
${1+"2"} ==3
${4==4} true

12.4. empty判空

1
${empty x}  true/false

13. JSTL

JavaServer Pages Standard Tag Library标准标签类库

对EL表达式的扩展

提升jsp页面编码效率

类别 URI 前缀
core http://java.sun.com/jsp/jstl/core c
xml http://java.sun.com/jsp/jstl/xml x
118n http://java.sun.com/jsp/jstl/fmt fmt
数据库 http://java.sun.com/jsp/jstl/sql sql
功能 http://java.sun.com/jsp/jstl/functions fn

13.1. 声明

1
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

Can not find the tag library descriptor for “http://java.sun.com/jsp/jstl/ core”

web工程中导入

jstl.jar

standard.jar

13.2. 通用标签

1
2
3
4
5
6
7
8
<c:out value="${}" default="">
</c:out>

<c:set var="变量" value="值" scope="page/request/session/application"/>
<c:set target="${address}" property="city" value="Tokyo"/>
//Tokyo 赋给address的city属性

<c:remove var="删除变量" scope="page/request/session/application">

13.3. 条件

1
2
3
4
5
6
7
8
9
10
11
12
13
<c:if test="${测试语句}" scope="page/request/session/application">
成功时输出
</c:if>

<c:if test="!测试语句" scope="page/request/session/application">
条件取反时输出
</c:if>

<c:choose>
<c:when test="${测试语句}"> </c:when>
<c:when test="${测试语句}"> </c:when>
<c:otherwise> </c:otherwise>
</c:choose>

13.4. 迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1 2 3 4 5
<c:forEach var="x" begin="1" end="5">
<c:out value="${x}"/>
</c:forEach>

<c:forEach var="x" items="${param.info}" step="2" varStatus="status">
<c:if test="${status.count%2==0}">
javax.servlet.jsp.jstl.LoopTagStatus接口,返回当前迭代次数 123...
</c:if>
<c:out value="${x}"/>
</c:forEach>

//a b c delims 分隔符
<c:forTokens var="x" items="a,b,c" delims="," varStatus="status">
${x}
</c:forTokens>

14. 监听器

监听作用域对象request,session,application的创建,销毁和内容的改变

监听器级别:ServletContext,HttpSession,ServletRequest

监听器的注册

1 注解配置

1
2
@WebListener
public class AppListener implements ServletContextListener

2 添加到web.xml

1
2
3
<listener>
<listener-class></listener-class>
</listener>

14.1. ServletContextListener

javax.servlet.*;

ServletContextListener对ServletContext的初始化和解构作出响应

1
2
3
4
5
6
7
8
9
10
11
12
public class AppListener implements ServletContextListener {
//ServletContext初始化时,所有已注册的ServletContextListener中调用
public void contextInitialized(ServletContextEvent e) {

ServletContext application=e.getServletContext();
application.setAttribute("test","1");
}

//ServletContext销毁时,所有已注册的ServletContextListener中调用
public void contextDestroyed(ServletContextEvent e) {
}
}

ServletContextAttributeListener

ServletContext添加,删除,替换属性时会收到通知

1
2
3
4
5
6
7
8
9
public class AppListener implements ServletContextAttributeListener {
public void attributeAdded(ServletContextAttributeEvent e) {
e.getName(); //属性名
e.getValue();//属性值
}
public void attributeRemoved(ServletContextAttributeEvent e) {}

public void attributeReplaced(ServletContextAttributeEvent e) {}
}

14.2. HttpSessionListener

javax.servlet.http.*;

HttpSessionListener

HttpSession创建或销毁,会调用所有注册的HttpSessionListener。

1
2
3
4
5
6
7
8
public class AppListener implements HttpSessionListener {

public void sessionCreated(HttpSessionEvent e) {
HttpSession session = e.getSession();
}

public void sessionDestroyed(HttpSessionEvent e) {}
}

HttpSessionAttributeListener

1
2
3
4
5
6
7
8
public class AppListener implements HttpSessionAttributeListener {

public void attributeAdded(HttpSessionBindingEvent e) {}

public void attributeRemoved(HttpSessionBindingEvent e) {}

public void attributeReplaced(HttpSessionBindingEvent e) {}
}

HttpSessionActivationListener

为了节省内存将访问量少的session序列化迁移到存储设备中

1
2
3
4
5
6
7
public class AppListener implements HttpSessionActivationListener {

public void sessionDidActivate(HttpSessionEvent e) {
e.getSession();
}
public void sessionWillPassivate(HttpSessionEvent e) {}
}

HttpSessionBindingListener

绑定HttpSession或者取消绑定时触发

类实现HttpSessionBindingListener接口,保存HttpSession时,对象值会自动更新

1
2
3
4
5
6
7
8
9
10
11
12
public class User implements HttpSessionBindingListener {
private String name;
public String getName()
{
return name;
}
public void valueBound(HttpSessionBindingEvent e) {
String name = e.getName();
}

public void valueUnbound(HttpSessionBindingEvent e) {}
}

14.3. ServletRequestListener

ServletRequestListener

对ServletRequest的创建和销毁作出响应

1
2
3
4
5
6
7
8
9
public class AppListener implements ServletRequestListener {

public void requestDestroyed(ServletRequestEvent e) {}

public void requestInitialized(ServletRequestEvent e) {
ServletRequest request=e.getServletRequest();
ServletContext context=e.getServletContext();
}
}

ServletRequestAttributeListener

1
2
3
4
5
6
7
8
public class AppListener implements ServletRequestAttributeListener {

public void attributeAdded(ServletRequestAttributeEvent e) {}

public void attributeRemoved(ServletRequestAttributeEvent e) {}

public void attributeReplaced(ServletRequestAttributeEvent e) {}
}

AsyncListener

1
2
3
4
5
6
7
8
9
10
11
public class AppListener implements AsyncListener {

public void onComplete(AsyncEvent e) throws IOException {}

public void onError(AsyncEvent e) throws IOException {}

public void onStartAsync(AsyncEvent e) throws IOException {}

public void onTimeout(AsyncEvent e) throws IOException {}

}

15. 过滤器

权限管理,session管理,统一编码

15.1. 注册

/* 全拦截

/log 特定拦截

注解方式

1
@WebFilter(filterName="",urlPatterns={"/*"})

xml方式

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>log</filter-name>
<filter-class>com.runaccpeted.filter.LoginFilter</filter-class>
</filter>

<filter-mappring>
<filter-name>log</filter-name>
<url-pattern>/*</url-pattern>
</filter-mappring>

使用

init 服务器启动则启动

doFilter 核心方法 需要手动放行 chain.doFilter(request, response);

destroy 服务器关闭则关闭

1
2
3
4
5
6
7
8
9
10
11
public class LoginFilter implements Filter {
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)ServletRequest;
HttpServletResponse response = (HttpServletResponse)ServletResponse;
chain.doFilter(request, response);

}
public void init(FilterConfig config) throws ServletException {}
}

16. 文件上传下载

1
2
3
4
<form action="fileUp" enctype="multipart/form-data" method="post">
<input type="file" name="filename"/>
<input type="submit">
</form>

Servlet

MultipartConfig限制:

maxFileSize文件容量

maxRequestSize 许多部分HTTP请求最大容量

location文件保存到磁盘指定位置

fileSizeThreshold溢出则写入磁盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@WebServlet(urlPatterns="/file")
@MultipartConfig
public class file extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

Part part = request.getPart("filename");
//form-data; name="filename"; filename="annotations-api.jar"
String file = part.getHeader("content-disposition");
String[] element=file.split(";");
for (String str:element) {
if(str.trim().startsWith("filename")){
String temp=str.substring(str.indexOf("=")+1);
part.write(System.getProperty("user.dir")+"/WebRoot/"+temp);
}
}
}
}

多文件

1
2
3
4
5
6
7
8
9
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

Collection<Part> parts = request.getParts();
for (Part path:paths) {
if(path.getContentType()!=null){

}
}
}
本文结束  感谢您的阅读
  • 本文作者: Wang Ting
  • 本文链接: /zh-CN/2019/09/20/Servlet与JSP/
  • 发布时间: 2019-09-20 19:00
  • 更新时间: 2021-10-29 14:18
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!