代码地址:https://github.com/Autunomy/webstudy
1.HttpServlet对象
在编写web程序的时候,我们都会让自己的servlet继承于Tomcat的HttpServlet类,重写其中的service方法。我们自己编写的Servlet的对象,在全局只创建一次,也就是说Servlet对象是单例的,Servlet对象的创建是第一次访问此Servlet时创建;
2.HttpServlet中,两种service方法的区别
在tomcat的HttpServlet对象中有两个service方法,他们的权限修饰符不同
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
}
两种方法并没有本质的区别,查看源码
这里的this就是HttpServlet的protected的service方法,也就是说如果我们调用public的service方法,那么本质上还是调用的是protected的service方法
3.HttpServletRequest对象
HttpServletRequest对象是请求对象,这个对象不需要我们创建,是Tomcat创建并传递给我们的一个对象,这个对象里面封装了请求的所有数据,比如,请求头,请求体,请求方法等的内容
常用api
方法 | 参数 | 含义 |
---|
getHeader() | String 请求头参数的key | 获取请求头 | getContextPath() | 无 | 获取上下文路径 | getServletPath() | 无 | 获取servlet中的映射路径 | getMethod() | 无 | 获取请求方式 | getRequestURL() | 无 | 获取url | getRequestURI() | 无 | 获取uri | getParameter() | String 请求参数的key | 获取请求的数据 |
案例
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String host = req.getHeader("host");
System.out.println("请求头="+host);
String contextPath = req.getContextPath();
System.out.println("上下文路径="+contextPath);
String servletPath = req.getServletPath();
System.out.println("servlet中的映射路径="+servletPath);
String method = req.getMethod();
System.out.println("请求方式="+method);
StringBuffer url = req.getRequestURL();
String uri = req.getRequestURI();
System.out.println("url="+url);
System.out.println("uri="+uri);
String name = req.getParameter("name");
String age = req.getParameter("age");
System.out.println("name="+name+" "+"age="+age);
}
}
4.HttpServletResponse对象
HttpServletRequest对象是响应对象,这个对象不需要我们创建,是Tomcat创建并传递给我们的一个对象,这个对象里面封装了响应的所有数据,包括响应体,响应头等等
常用api
方法 | 参数 | 含义 |
---|
setHeader() | String Object 是一个key-value键值对 | 给响应数据包设置响应头 | setContentType() | String 数据的格式 | 设置数据类型 | setCharacterEncoding() | String 编码的类型 | 设置响应实体编码 | setStatus() | 无 | 给响应数据包设置状态码 | getWriter() | 无 | 获取页面输出对象PrintWriter | write() | Object 需要写入页面的数据 | 向页面写数据 |
案例
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("mydata","data");
resp.setContentType("application/json");
resp.setCharacterEncoding("utf-8");
resp.setStatus(200);
resp.getWriter().write("servlet02你好");
}
}
5.Servlet的生命周期
Servlet是全局单例的,也就是说这个实例在第一次访问的时候被创建,之后就不再进行创建了
整个生命周期
- 第一次访问被创建,执行对象的创建回调构造方法
- 回调init()初始化方法,执行初始化操作
- 每次请求到达都会执行service()方法
- 程序停止的时候会自动回调destroy()函数来销毁servlet
案例
public class Servlet03 extends HttpServlet {
public Servlet03() {
System.out.println("servlet03被创建了");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("servlet03");
}
@Override
public void destroy() {
System.out.println("servlet03被销毁了");
}
@Override
public void init() throws ServletException {
System.out.println("servlet03被初始化了");
}
}
5.HttpServletConfig对象
在HttpServlet对象中,有两个init() 方法其中一个方法是无参的一个方法是有参的,有参的方法的参数是HttpServletConfig ,这个对象可以将我们在web.xml 文件中提前配置的内容取出来
案例
<servlet>
<servlet-name>servlet03</servlet-name>
<servlet-class>com.hty.web01.Servlet03</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>zhangsan</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>18</param-value>
</init-param>
</servlet>
public void init(ServletConfig config) throws ServletException {
String name = config.getInitParameter("name");
String age = config.getInitParameter("age");
System.out.println("name="+name+" "+"age="+age);
}
问题:如果我们将两个init方法都重写了,那么调用哪一个呢?
查看源码
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
在有参的init中调用了无参的init,说明了,如果我们重写之后没有调用父类的init方法,那么就只会执行有参的init方法,但是如果我们在有参的init方法中调用了父类的init方法,那么就会在调用有参的init方法之前先调用一次无参的init方法
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
String name = config.getInitParameter("name");
String age = config.getInitParameter("age");
System.out.println("name="+name+" "+"age="+age);
}
输出结果
servlet03被创建了 servlet03被初始化了 name=zhangsan age=18
6.请求转发
其实请求转发是服务器内部的请求转发,与浏览器没有任何关系,对于浏览器而言页面不会出现出现刷新,就是一个请求
案例
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/servlet01").forward(req,resp);
}
}
这个案例转发到了servlet01中,ide的控制台就会展示出上面servlet01中的内容
7.请求重定向
浏览器发送一次请求,服务器返回302状态码+Location响应头,浏览器因为有一个机制(碰到响应码是302的就会去检查Location响应头,然后跳转访问Location响应头中携带的地址)
请求重定向之后,浏览器地址栏就会发生变化
案例
public class Servlet05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/servlet01");
}
}
8.ServletContext对象
Tomcat在加载我们的应用程序的时候,会给我们的应用程序创建一个ServletContext对象,代表我们这个应用程序对象,我么可以通过Servlet中的api来获取此对象。这个对象相当于一个变量的保存区,可以将我们每一个servlet设置key-value键值对保存在里面,在任何一个servlet中都可以使用这些key-value键值对
案例
public class Servlet06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("name","zhangsan");
servletContext.setAttribute("age","18");
}
}
public class Servlet07 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String name = (String) servletContext.getAttribute("name");
String age = (String) servletContext.getAttribute("age");
System.out.println(name + " " + age);
}
}
9.service()方法详解
public class Servlet08 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
}
在上面的程序中,我们直接调用了父类的service()方法,最后的执行结果为
原因:
阅读源码我们可以发现,service方法调用了HttpServlet中的doGet(),doPost()等一系列的方法,他是根据请求的方法来进行选择的,又由于浏览器只能发送get请求,所以service方法还是调用了doGet()方法,进入到doGet()方法就会发现,405错误时从这里来的。根据这个例子来看,我们重写service方法的时候,一定不能调用父类方法。
10.json与对象之间的转换
前后端交互的数据格式就是json格式
一种json字符串 {"author":"吴承恩","id":1,"name":"西游记"}
要拼接为这种字符串那么我们就要重写toString()方法,并利用反射机制获取到属性的值
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("{");
Class<? extends Book> bookClass = this.getClass();
Field[] fileds = bookClass.getDeclaredFields();
for (int i = 0; i < fileds.length; i++) {
String name = fileds[i].getName();
sb.append("\"" + name + "\": ");
try {
sb.append("\"" + fileds[i].get(this) + "\",");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
sb.replace(sb.length()-1,sb.length(),"");
sb.append("}");
return sb.toString();
}
通过拼接我们发现,这种方式非常的麻烦,那么我们就需要借用第三方的工具包来实现json的转换
11.fastjson
由于自行拼接json字符串很麻烦,于是我们就可以使用第三方的工具包来帮助我们拼接json字符串,fastjson就是阿里巴巴的一个工具包,专门用来处理json字符串的
public class json01 {
public static void main(String[] args) {
Book book = new Book();
book.setId(1);
book.setName("西游记");
book.setAuthor("吴承恩");
book.setDate(new Date(System.currentTimeMillis()));
String s = JSON.toJSONStringWithDateFormat(book,"yyyy-MM-dd HH:mm:ss", SerializerFeature.BrowserCompatible);
String s1 = JSON.toJSONStringWithDateFormat(book,"yyyy-MM-dd HH:mm:ss", SerializerFeature.PrettyFormat);
System.out.println(s);
Book book1 = JSON.parseObject(s, Book.class);
System.out.println(book1);
}
}
集合的转换
public class json02 {
public static void main(String[] args) {
List<Book> bookList = new ArrayList<>();
Book book1 = new Book();
book1.setId(1);
book1.setName("西游记");
book1.setAuthor("吴承恩");
book1.setDate(new Date(System.currentTimeMillis()));
Book book2 = new Book();
book2.setId(2);
book2.setName("三国演义");
book2.setAuthor("罗贯中");
book2.setDate(new Date(System.currentTimeMillis()));
bookList.add(book1);
bookList.add(book2);
String s = JSON.toJSONStringWithDateFormat(bookList, "yyyy-MM-dd HH:mm:ss");
System.out.println(s);
}
}
详情请见:http://t.csdn.cn/8bPs1
12.Servlet返回json字符串给客户端
由于现在的开发都是前后端分离,所以前后端的数据交互主要都是靠json。
public class Servlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Book book = new Book();
book.setId(1);
book.setName("西游记");
book.setAuthor("吴承恩");
book.setDate(new Date());
String bookJson = JSON.toJSONString(book);
resp.getWriter().write(bookJson);
}
}
13.乱码的问题
只有在地址栏中携带数据才不会出现乱码,也就是说get请求永远不会出现乱码,post,put,delete等请求在请求体中携带的参数数会出现乱码问题
解决方法是
req.setCharacterEncoding("urf-8");
在响应实体中出现乱码的解决方式
resp.setHeader("Content-Type","application/json;charset=utf-8");
14.MIME类型(Type)
在网络中传输数据,需要告诉客户端或者告诉服务器我们传输的数据的类型是什么,这些个类型是有一定规范的,我们常用的类型有以下这么一些:
text/plain(纯文本)
text/html (网页)
text/javascript(js的脚本文件)
text/css(css文件)
image/jpeg(JPEG图像)
image/png(PNG图像)
application/pdf(PDF文档)
application/json(json类型)
application/x-www-form-urlencoded(简单表单数据类型)
multipart/form-data(复合表单类型)
....
这些就是可以设置在Content-Type中的值
如果不指定MIME-TYPE类型,如果客户端是浏览器,那么浏览器就会尝试着去解析,所以在实际开发中,都会显示的指定MIME-TYPE;
15.服务器获取浏览器提交的表单数据
后端编写
public class Servlet01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
Integer age = Integer.valueOf(req.getParameter("age"));
System.out.println(name);
System.out.println(age);
}
}
前端编写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/web03/servlet01" method="POST" enctype="application/x-www-form-urlencoded">
<input type="text" name="name" placeholder="请输入姓名" autocomplete="off">
<input type="text" name="age" placeholder="请输入年龄" autocomplete="off">
<button>提交</button>
</form>
</body>
</html>
总结: 以上的这种方式提交表单数据的方式是同步的,在网络环境较差的情况下用户体验不好,所以在实际的开发过程中,尽量应该使用异步的方式提交表单数据;
16.异步请求
16.1.原生js的异步请求实现
异步请求的第一步就是先要将表单阻止提交,阻止的方式就是在form中加入onsubmit属性并return false;
<form onsubmit="return false;"></form>
编写前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>提交表单数据</h1>
<form onsubmit="return false;" id="form">
<input type="text" autocomplete="off" name="name" placeholder="请输入姓名"/>
<br>
<input type="text" autocomplete="off" name="age" placeholder="请输入年龄"/>
<br>
男: <input type="radio" name="sex" value="男"> 女:<input type="radio" name="sex" value="女">
<br>
java: <input type="checkbox" value="java" name="loves">
python:<input type="checkbox" value="python" name="loves">
大数据: <input type="checkbox" value="大数据" name="loves">
<br>
<select name="city">
<option>西安市</option>
<option>北京市</option>
<option>宝鸡市</option>
</select>
<br>
<button onclick="doSubmit()">提交数据给服务器</button>
</form>
<script type="text/javascript">
function doSubmit() {
let name = document.querySelector("input[name='name']");
let age = document.querySelector("input[name='age']");
let sex = document.querySelectorAll("input[name='sex']");
let sexVal = null;
for (let sexKey in sex) {
if (sex[sexKey].checked) {
sexVal = sex[sexKey].value;
}
}
let lovesInputs = document.querySelectorAll("input[name='loves']");
let loves = [];
lovesInputs.forEach(lovesInput=>{
if(lovesInput.checked){
loves.push(lovesInput.value);
}
})
let city = document.querySelector("select[name='city']").value;
const userObj={
name,
age,
sex,
loves,
city
}
let xhr = new XMLHttpRequest();
xhr.open("POST","/web03/servlet01");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
xhr.send("name=zhangsan&age=18&sex=男&loves=java&loves=大数据&city=西安市");
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
const responseJsonText = xhr.responseText;
const jsonObj = JSON.parse(responseJsonText);
console.log(JSON.stringify(jsonObj));
}
}
}
</script>
</body>
</html>
编写后端
public class Servlet01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
Integer age = Integer.valueOf(req.getParameter("age"));
String sex = req.getParameter("sex");
String[] loves = req.getParameterValues("loves");
String city = req.getParameter("city");
User user = new User(name,age,sex,loves,city);
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(JSON.toJSONString(user));
}
}
这个Uers实体类
package com.hty.web03.pojo;
public class User {
private String name;
private Integer age;
private String sex;
private String[] loves;
private String city;
public User() {
}
public User(String name, Integer age, String sex, String[] loves, String city) {
this.name = name;
this.age = age;
this.sex = sex;
this.loves = loves;
this.city = city;
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String[] getLoves() {
return loves;
}
public void setLoves(String[] loves) {
this.loves = loves;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
16.2.基于jQuery的异步请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基于jQuery的异步请求实现</title>
<script src="jquery-1.9.1.js"></script>
</head>
<body>
<form onsubmit="return false;" id="form">
<input type="text" autocomplete="off" name="name" placeholder="请输入姓名"/>
<br>
<input type="text" autocomplete="off" name="age" placeholder="请输入年龄"/>
<br>
男: <input type="radio" name="sex" value="男"> 女:<input type="radio" name="sex" value="女">
<br>
java: <input type="checkbox" value="java" name="loves">
python:<input type="checkbox" value="python" name="loves">
大数据: <input type="checkbox" value="大数据" name="loves">
<br>
<select name="city">
<option>西安市</option>
<option>北京市</option>
<option>宝鸡市</option>
</select>
<br>
<button onclick="doSubmit()">提交数据给服务器</button>
</form>
<script>
function doSubmit(){
let dataform = $("#form").serialize();
$.ajax({
url:"/web03/servlet01",
type:"POST",
data:dataform,
success:function (...args){
console.log("成功响应",args);
},
fail:function (){
console.log("失败响应");
}
})
}
</script>
</body>
</html>
17.Servlet中的域对象
域就是作用域(范围)
- ServletContext(全局域对象)
- ServletRequest(请求域对象)
- HttpSession
18.ServletContext对象
ServletContext域对象是在全局作用域都是同一个对象
要测试这个对象,我们需要先创建两个servlet,一个用来存,一个用来获取
public class Servlet03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("name","zhangsan");
System.out.println("servlet03");
}
}
public class Servlet04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String name = servletContext.getAttribute("name").toString();
System.out.println(name);
System.out.println("servlet04");
}
}
请求重定向和请求转发都可以获取到ServletContext对象中的数据
19.ServletRequest对象
ServletRequest对象只在一次请求中有效;
我们仍然需要两个类来进行测试,用一个servlet请求转发到另一个servlet中,就可以测试请求作用域对象
public class Servlet03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("name","张三");
System.out.println("servlet03");
req.getRequestDispatcher("/web03/servlet04").forward(req,resp);
}
}
public class Servlet04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getAttribute("name").toString();
System.out.println(name);
System.out.println("servlet04");
}
}
在请求重定向中不能访问到请求作用域对象
20.Filter过滤器
过滤器的本质还是一个Servlet,对Servlet的访问的前后做一些处理
- 过滤器就是Servlet规范中,对servlet前后做处理的一个组件
- 过滤器是对多个请求的前后都可以进行处理
- 过滤器可以有多个,多个过滤器组成了一条过滤链
- 过滤器编写完成后需要注册
- 过滤器中需要配置过滤器到底过滤哪些请求
执行步骤
21.Filter过滤器的生命周期
首先在web.xml中对filter进行注册
<filter>
<filter-name>myfilter01</filter-name>
<filter-class>com.hty.web03.filter.MyFilter01</filter-class>
</filter>
<filter-mapping>
<filter-name>myfilter01</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
public class MyFilter01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器MyFilter01被初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行MyFilter01过滤器------------");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("过滤器MyFilter01被销毁");
}
}
22.过滤链
当有多个过滤器的时候,那么这些过滤器就会组成一个过滤链,过滤链中,配置在前面的过滤器会先执行,回调的时候,顺序是相反的
23.数据库连接池
- dbcp
- c3p0
- druid(阿里巴巴开源数据连接池)
- hikari
24.servlet访问数据库
首先创建数据库和数据
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`name` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图书名称',
`author` char(4) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图书作者',
`release_date` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `book` VALUES ('1', '西游记', '吴承恩', '2022-05-17');
INSERT INTO `book` VALUES ('2', '三国演义', '罗贯中', '2022-05-30');
INSERT INTO `book` VALUES ('3', '水浒传', '施耐庵', '2022-05-30');
INSERT INTO `book` VALUES ('4', '红楼梦', '曹雪芹', '2022-05-30');
在项目根目录下创建一个resources的目录,并标记为resources目录,创建一个db.properties的文件
# 写入自己的配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://10.10.10.134:3306/java21?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=root
password=123456
使用数据库连接池
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.fastjson.JSON;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class Servlet07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Integer id = Integer.valueOf(req.getParameter("id"));
Properties prop = new Properties();
prop.load(this.getClass().getClassLoader().getResourceAsStream("db.properties"));
DataSource druidDataSource = DruidDataSourceFactory.createDataSource(prop);
QueryRunner queryRunner = new QueryRunner(druidDataSource);
Map result = (Map)queryRunner.query("select * from book where id = ?", id, new MapHandler());
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(JSON.toJSONString(result));
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
25.案例-下拉菜单二级联动
前端页面的编写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>homework</title>
<script src="jquery-1.9.1.js"></script>
</head>
<body>
<button onclick="getCity()">获取城市信息</button>
<br>
<select name="city" id="city" class="city" onchange="changeCity(this)">
</select>
<select name="school" id="school">
</select>
<script>
function getCity(){
let city = [];
$.ajax({
url:"/web04/homeworkservlet01",
method:"POST",
success:function (...args){
for(let i=0;i<args[0].length;++i){
city.push(args[0][i][0]);
}
const citySelecter = document.querySelector("#city");
citySelecter.length = 0;
for(let i = 0;i<city.length;++i){
let option = document.createElement("option");
let text = document.createTextNode(city[i]);
option.append(text);
citySelecter.append(option);
}
},
fail:function(){
}
})
}
function changeCity(city){
let dataMsg = city.value;
console.log(dataMsg);
let school = [];
$.ajax({
url:"/web04/homeworkservlet02",
method:"POST",
data:{"city":dataMsg},
success:function(...args){
console.log(args);
for(let i=0;i<args[0].length;++i){
school.push(args[0][i][0]);
}
const schoolSelecter = document.querySelector("#school");
schoolSelecter.length = 0;
for(let i = 0;i<city.length;++i){
let option = document.createElement("option");
let text = document.createTextNode(school[i]);
option.append(text);
schoolSelecter.append(option);
}
},
fail:function(){
}
})
}
</script>
</body>
</html>
两个Servlet
HomeWorkServlet01
package com.hty.web04;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.fastjson.JSON;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
public class HomeWorkServlet01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
req.setCharacterEncoding("utf-8");
Properties pro = new Properties();
pro.load(this.getClass().getClassLoader().getResourceAsStream("db.properties"));
DataSource druidDataSource = DruidDataSourceFactory.createDataSource(pro);
QueryRunner queryRunner = new QueryRunner(druidDataSource);
List<String> result = (List<String>)queryRunner.query("select name from city",new ArrayListHandler());
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(JSON.toJSONString(result));
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
HomeWorkServlet02
package com.hty.web04;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.fastjson.JSON;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
public class HomeWorkServlet02 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
req.setCharacterEncoding("utf-8");
String city = req.getParameter("city");
System.out.println(city);
Properties pro = new Properties();
pro.load(this.getClass().getClassLoader().getResourceAsStream("db.properties"));
DataSource druidDataSource = DruidDataSourceFactory.createDataSource(pro);
QueryRunner queryRunner = new QueryRunner(druidDataSource);
List<String> result = (List<String>)queryRunner.query("select name from school where city = ?",city,new ArrayListHandler());
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(JSON.toJSONString(result));
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
26.Servlet中的会话技术
26.1.Cookie技术
Cookie是浏览器存储数据的一种机制,也就是说Cookie技术其实是浏览器的一个功能,浏览器可以在本地存储数据;
测试
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("name","admin");
resp.addCookie(cookie);
}
response中的内容
HTTP/1.1 200
//设置了一个cookie
Set-Cookie: name=admin
Content-Length: 0
Date: Wed, 08 Jun 2022 13:36:25 GMT
cookie中的api
Cookie cookie = new Cookie("name", "admin");
cookie.setComment("我是Comment注释");
cookie.setDomain("localhost");
cookie.setPath("/");
cookie.setMaxAge(60 * 60 * 24 * 7);
resp.addCookie(cookie);
总结:
Cookie是浏览器的一个机制:
- 存储数据(检查到响应头中有
Set-Cookie: xxx ,则会把数据进行存储) - 携带数据:每次发送请求的时候都会进行检查,如果有符合条件的Cookie数据,则会在请求头中进行携带(
Cookie: xxx )
26.2.Session技术
创建两个Servlet:
public class Servlet03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
System.out.println("servlet03:" + session);
resp.getWriter().write("hello");
}
}
public class Servlet04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
System.out.println("servlet04:" + session);
}
}
首先访问servlet03:
发现请求头中没有携带任何数据,那么servlet03中就会创建一个新的HttpSession对象,并且生成一个ID,把id称为SessionID ,并且响应的时候通过响应头: Set-Cookie: JSESSIONID=XXXX ,浏览器收到响应之后,发现触发了Cookie的机制(功能),把Sessionid进行了存储(Cookie的形式);有效期为-1;
再次访问servlet04(浏览器没有关闭): 浏览器会把JSESSIONID通过请求头 Cookie:xxx 携带给服务器,服务器收到请求之后,根据JSESSIONID找到Session对象;
总结:
HttpSession浏览器只要不关闭就是一次会话,使用的就是同一个HttpSession对象;
27.监听器
监听对象的数据变化
需要在web.xml中进行注册
27.1.ServletContextListener
可以监听ServletContext对象的创建与销毁
初始化: 在Tomcat启动时(当程序加载到Tomcat中)就会初始化 销毁: 在Tomcat关闭时(从容器中卸载程序)就会销毁
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("servletcontext初始化");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("servletcontext销毁");
}
}
27.2.ServletContextAttributeListener
可以监听ServletContext对象的数据的添加,修改和删除
给ServletContext域对象添加数据—>attributeAdded 修改ServletContext域对象中的数据—>attributeReplaced 移除ServletContext域对象中的数据—>attributeRemoved
public class MyListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
}
@Override
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
}
@Override
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
}
}
27.3.HttpSessionListener
监听session的创建与销毁
初始化: 会话建立的时候初始化 销毁: session对象被销毁时或者手动触发 session.invalidate();
public class MyListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
}
}
27.4.HttpSessionAttributeListener
监听session中数据的添加,删除和修改
给HttpSession域对象添加数据—>attributeAdded 修改HttpSession域对象中的数据—>attributeReplaced 移除HttpSession域对象中的数据—>attributeRemoved
public class MyListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
}
}
27.5.ServletRequestListener
监听请求作用域对象的创建和销毁
初始化: 请求到达服务器的时候创建(初始化) 销毁: 请求响应结束会进行销毁
public class MyListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
}
}
27.6.ServletRequestAttributeListener
监听请求作用域对象的数据的添加,修改和删除
给ServletRequest域对象添加数据—>attributeAdded 修改ServletRequest域对象中的数据—>attributeReplaced 移除ServletRequest域对象中的数据—>attributeRemoved
public class MyListener implements ServletRequestAttributeListener {
@Override
public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
}
}
28.使用注解开发
在Servlet3.x的版本中已经添加了对注解的支持,我们可以不使用web.xml ,只使用注解开发;
@WebServlet(urlPatterns = "/servlet04")
@WebFilter(urlPatterns = "/*")
@WebListener
29.文件上传
//接收GET请求的普通参数
String name = req.getParameter("name");
但是: 我们在实际的开发过程中有可能会使用到文件上传,而我们在前面学过的表单提交其实都是简单的表单提交 ,但是如果一个表单中包含了文件上传的组件,那么就不能使用简单的表单提交了;
简单的表单提交既可以使用GET请求也可以使用POST请求,但是在一般情况下,我们提交表单数据都是使用POST请求;
<form action="/servlet05" method="post" enctype="application/x-www-form-urlencoded" >
POST /servlet05 HTTP/1.1
Host: 192.168.3.136:8080
Connection: keep-alive
Content-Length: 32
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.3.136:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.3.136:8080/ops1.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
username=admin&password=admin123
我们接下来看复合表单的数据提交:
复合表单绝对不能使用GET请求进行提交的,一定要使用POST请求;
复合表单的提交enctype就不能使用application/x-www-form-urlencoded ,我们就得指定支持复合表单的编码格式enctype="multipart/form-data"
POST /servlet05 HTTP/1.1
Host: 192.168.3.136:8080
Connection: keep-alive
Content-Length: 391
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.3.136:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0U1mMgBO6VA5fYQk
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.3.136:8080/ops1.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
------WebKitFormBoundary0U1mMgBO6VA5fYQk
Content-Disposition: form-data; name="username"
admin
------WebKitFormBoundary0U1mMgBO6VA5fYQk
Content-Disposition: form-data; name="password"
admin123
------WebKitFormBoundary0U1mMgBO6VA5fYQk
Content-Disposition: form-data; name="mPic"; filename=""
Content-Type: image/png
xxxx文件的二进制数据xxxx
------WebKitFormBoundary0U1mMgBO6VA5fYQk--
在老版本的Servlet中并没有提供对文件上传的支持,需要我们自己进行解析,但是Servlet3.x之后官方也发现很多人有这样的需求,官方替我们解析了,给我们直接提供了使用方式;我们直接获取复合表单数据即可,只需要注意在页面提交表单数据时的两个属性: method="post" enctype="multipart/form-data"
案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/web06/servlet03" method="post" enctype="multipart/form-data">
<input type="text" name="username" placeholder="用户名" autocomplete="none">
<input type="password" name="password" placeholder="密码" autocomplete="none">
<input type="file" name="pic">
<button>提交</button>
</form>
</body>
</html>
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
@WebServlet(urlPatterns = "/web06/servlet03")
@MultipartConfig
public class Servlet03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
Part part = req.getPart("pic");
System.out.println(part);
System.out.println(part.getName());
System.out.println(part.getSize());
System.out.println(part.getSubmittedFileName());
System.out.println(part.getContentType());
part.write("D:\\pic.jpg");
}
}
ead> Title 提交 ```
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
@WebServlet(urlPatterns = "/web06/servlet03")
@MultipartConfig
public class Servlet03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
Part part = req.getPart("pic");
System.out.println(part);
System.out.println(part.getName());
System.out.println(part.getSize());
System.out.println(part.getSubmittedFileName());
System.out.println(part.getContentType());
part.write("D:\\pic.jpg");
}
}
|