Servlet
核心概念
执行流程:http://localhost:8080/web-demo/demo1
这个url中,http://localhost:8080这是访问的web服务器的,web-demo是你部署在服务器上的项目名称,demo1是你这个servlet的请求地址,此时web服务器会创建你访问的这个对应的servlet对象并执行重写的service()
服务器怎么知道servlet中一定有service方法? 因为你是实现了Servlet的接口,实现后一定会重写service(),所以服务器知道一定会有service()
生命周期:servlet的生命周期由容器管理,分为4个阶段:
-
实例化:默认情况下,当servlet第一次被访问时,由容器创建servlet对象
-
初始化:实例化后会直接调用init()来初始化这个对象,只调用一次
-
处理请求:每次请求都会调用一次service()
-
销毁:调用destroy(),只调用一次
可以改变创建servlet对象:
@WebServlet(urlPatterns = "/demo", loadOnStartup = 1)
,小于0是第一次被访问时创建Servlet对象,大于等于0服务器启动时创建Servlet对象,数字越小优先级越高
使用
准备工作
把Tomcat先配置好,然后maven来构建项目,然后修改成web项目并添加依赖
<groupId>com.dx</groupId>
<artifactId>servlet-first</artifactId>
<version>1.0.0</version>
<packaging>war</packaging> <!-- 修改成web项目 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope> <!-- scope标签是一定要加的,表示只在项目操作的时候提供依赖,因为tomcat继承了servlet,写了provided后,后续项目打成jar包后不会出现依赖冲突 -->
</dependency>
然后在main文件夹里面创建一个webapp的目录,然后刷新Maven
编写Servlet
编写servlet有三种方式:
-
实现Servlet接口 【javax.servlet包下,它会实现5个方法,包含初始化init()、service()、销毁方法destroy()】
-
继承GenericServlet抽象类 【javax.servlet包下,它实现了实现Servlet,只有service()核心方法没实现,所以它是抽象类】
-
继承HttpServlet抽象类【我们的请求绝大部分都是 HTTP 请求,因此我们在真正开发中使用的是继承 HttpServlet 这个类】
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*我们一般还是会选择重写它的下面两个方法
*/
public class FirstServlet extends HttpServlet {
//对get请求的处理逻辑,通过浏览器直接敲url是get请求的方式
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("ok");
resp.getWriter().write("ok ok");
}
//对post请求的处理逻辑
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doGet(req, resp);
}
}
HttpServlet底层原理
它能够处理不同请求,正常情况下如果我们自己去编写的话
@Override public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException { //转成HTTP对象 HttpservletRequest request = (HttpServletRequest) req; HttpservletResponse response = (HttpServletRequest) res; //获取请求方式 String method = request.getMethod(); //判断,根据不同请求写不同逻辑 if("GET".equals(method)) { //get对应的处理逻辑 }else if("POST".equals(method) { //post对应的处理逻辑 } }
但后续我们每个类都要这么写很麻烦,所以现在HttpServlet底层已经封装了上面这个逻辑了,它里面有两个
service()
:其中一个是完成的是强转,剩下一个就是获取并判断的逻辑了,如果是GET就用doGet(),以此类推(类似于模板设计模式)
配置Servlet
xml文件形式
要运行需要配置Servlet,这个文件的路径应该是在 src/main/webapp/WEB-INF/web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置 Servlet -->
<servlet>
<servlet-name>servlet的名称,一般是类名首字母小写</servlet-name>
<servlet-class>servlet的全类名</servlet-class>
<!-- <load-on-startup>指定Servlet加载时机,一般在1之后,值越小越先加载</load-on-startup> -->
</servlet>
<!-- 配置Servlet映射 -->
<servlet-mapping>
<servlet-name>要和上面的名称对应</servlet-name>
<url-pattern>写的请求路径,必须是以/线开头</url-pattern>
</servlet-mapping>
</web-app>
注解形式
Servlet3.0开始支持注解开发,不用配置web.xml,就可以直接运行(推荐使用)
package com.dx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//name指定名称,alue 和 urlPatterns 都可以指定访问的路径
@WebServlet(name = "fourServlet", value = "/four")
public class FourServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("fourServlet");
}
}
然后运行Tomcat,输入路径即可访问
HttpServletRequest
常用方法
package com.dx.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
* 演示HttpServletRequest方法
*/
@WebServlet("/demo")
public class RequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 防止post请求中文解析乱码,Tomcat8开始get请求就不再乱码了
req.setCharacterEncoding("utf-8");
//路径演示:http://localhost:8080/javaweb-servlet/demo?username=zhangsan&password=123456
/* 获取请求行 */
System.out.println(req.getMethod()); // 获取请求方式:GET
System.out.println(req.getContextPath()); // 动态获取项目访问路径:/javaweb-servlet
System.out.println(req.getRequestURL().toString()); // 获取URL:完整路径
System.out.println(req.getRequestURI()); // 获取URI: /javaweb-servlet/demo
/* 获取请求头 */
String header = req.getHeader("user-agent");
System.out.println(header); // Mozilla/5.0......
/* 通用获取请求体(参数) */
//get和post都有专门获取参数的方式,但请求类型你也不确定,为了通用就用了getParamter
String name = req.getParamter("name");
System.out.println(name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp); //推荐这种写法,因为不需要分开写逻辑,判断完是post请求把参数给到doGet()也是可以的
}
}
转发
在服务器内部进行资源跳转的方式
1)FromServlet.java
package com.dx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/from")
public class FromServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 存入值
req.setAttribute("name", "zhang san");
// 转发到 ToServlet 中
// 转发不需要写项目路径
req.getRequestDispatcher("/to").forward(req, resp);
}
}
2)ToServlet.java
package com.dx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/to")
public class ToServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 HttpServletRequest 对象中的 name 属性值
String name = (String) req.getAttribute("name");
System.out.println("name = " + name);
// req.removeAttribute(String name); 删除数据
}
}
输入 /from 后发现在控制台中输出了 zhang san,说明资源可共享
HttpServletResponse
常用方法
package com.dx.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 常用方法演示
*/
@WebServlet("/demo1")
public class ResponseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* 获取响应行 */
//设置响应状态码
resp.setStatus(200);
/* 设置响应头 */
resp.setHeader("content-type", "application/json;charset=UTF-8");//这里是json,当然也可以: resp.setContentType("application/json; charset=UTF-8");
/* 设置响应体 */
//获取字符输出流,流自动关
PrintWriter writer = resp.getWriter();
writer.write("你好"); //会放到响应体里面,最终写到页面上,后续可以ajax接收后进行拼接展示
//获取字节输出流
FileInputStream fis = new FileInputStream("d://a.jpg");
ServletOutputStream os = resp.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
os.write(buf, 0, len);
}
fis.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
重定向
在任意位置实现资源跳转的方式
package com.dx.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/from")
public class FromServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("name", "zhang san");
/*
* 重定向原理:
* 1.首先要设置状态码:`302`(告诉浏览器我处理不了,找别人)
* resp.setStatus(302);
* 2.然后设置响应头:`location: 资源B的路径`(B资源可以解决,并给它路径让浏览器去找它)
* resp.setHeader("location","javaweb-servlet/demo2");
*/
//重定向必须要要写项目路径,因为重定向的地址是浏览器使用,而不是像转发那样服务器内部使用
//req.getContextPath()这样可以动态获取虚拟目录,写死的话不好改
resp.sendRedirect(req.getContextPath() + "/to");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
ToServlet和转发一样没变,程序运行后的结果发现在 ToServlet 中不能成功获取到 name 属性的值,说明资源不可共享
一般而言,页面发生了数据的变化就需要使用重定向来重新发送一次请求,保证数据的实时更新
转发和重定向的区别
-
转发的方法是 HttpServletRequest 对象中的方法,而重定向是 HttpServletReponse 对象中的方法。
-
转发发送一次请求(资源可以使用request的共享数据),而重定向会发出两次或多次请求(资源不可使用request的共享数据)【通过 setAttribute、getAttribute、removeAttribute实现资源共享】
-
转发浏览器地址栏不会发生变化,而重定向浏览器地址栏会发生变化。
-
转发只能转发到当前服务器的内部资源,重定向可以重定向到任意位置的资源(比如从FromServlet重定向到百度)
-
在一个转发链中可以使用 HttpServletRequest 对象中的属性,而重定向不能使用 HttpServletRequest 对象中的属性。
异步Ajax + 回显
异步
原生对象 + JQuery + Axios (网上一搜就知道怎么用了)
回显
//jsonData是相应的Json数据,现在开始拼接数据
let str = '';
jsonData.forEach(person => {
str += '<tr>'
str += '<td>' + person.id + '</td>'
str += '<td>' + person.name + '</td>'
str += '<td>' + person.age + '</td>'
str += '<td>' + person.gender + '</td>'
str += '</tr>'
})
//把数据放到tbody
document.querySelector('tbody').innerHTML = str;
//users是响应的json数据,接下来进行拼接操作
let usersData = "";
for (let i = 0; i < users.length; i++) {
let user = users[i];
usersData += "<tr>\n" +
"<td>"+user.id+"</td>\n" +
"<td>"+user.username+"</td>\n" +
"<td>"+user.password+"</td>\n" +
"</tr>";
}
document.getElementById("tbody").innerHTML = usersData; //设置在tbody上即可
//data就是请求返回的json格式的数据
let s = ''; //准备拼接
$(data).each(function (i, user) { //i表示索引,user其实就是拿到的一个个对象
s += '<tr>';
s += `<th scope="row">${user.id}</th>`;
s += `<td>${user.name}</td>`
s += `<td>${user.account}</td>`
s += `<td>${user.password}</td>`
s += `<td><img src="${user.avatar}" height="25"></td>`
s += `<td><button type="button" class="btn btn-sm btn-info">修改</button>` +
`<button type="button" class="btn btn-sm btn-danger">删除</button></td>`
s += '</tr>';
});
//拼接完之后回显到页面上
$('.table > tbody').append(s);