6 Servlet
6.1 Servlet技术
6.1.1 Servlet简介
- Servlet是JavaEE规范之一。接口
- Servlet是JavaWeb三大组件之一。三大组件:Servlet程序、Filter过滤器、Listener监听器
- Servlet是运行在服务器上的一个Java小程序,可以接收并响应2客户端的请求
6.1.2 手动创建Servlet
- Servlet接口的service方法
- 在web.xml中配置相应访问地址
- 根据http://localhost:8080/05_web/hello访问Servlet程序
public class HelloServlet implements Servlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet 被访问了");
}
}
<!--servlet标签给Tomcat配置Servlet程序-->
<servlet>
<!--servlet-name给Servlet程序起一个别名-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class是Servlet程序的全类名-->
<servlet-class>com.ct.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet-mapping给Servlet程序配置访问地址-->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<!--url-pattern配置访问地址-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HgRte2Gj-1645450757606)(C:\Users\Tao\AppData\Roaming\Typora\typora-user-images\image-20220120161058744.png)]
6.1.3 Servlet生命周期
-
执行Servlet构造器方法
-
执行init初始化方法
以上两种方法只在第一次访问时创建Servlet程序使用
-
执行service方法:每次访问都会调用
-
执行destroy销毁方法:在web工程停止时执行
6.1.4 GET和POST请求的分发处理
Service方法本身无法对请求类型GET和POST分别处理,所以我们手动编写方法来处理请求
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//获取请求的类型
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
//执行相应的操作
if("GET".equals(method)){
System.out.println("get请求");
}else if("POST".equals(method)){
System.out.println("post请求");
}
}
优化后
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//获取请求的类型
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
if("GET".equals(method)){
doGet();
}else if("POST".equals(method)){
doPost();
}
}
public void doGet(){
System.out.println("get请求");
}
public void doPost(){
System.out.println("post请求");
}
6.1.5 通过继承HttpServlet实现Servlet程序
实际项目开发中,都是使用继承HttpServlet类的方式实现Servlet程序
- 编写一个类继承HttpServlet类
- 根据业务需要重写doGet和doPost方法
- 到web.xml中配置Servlet程序的访问地址
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doGet方法");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doPost方法");
}
}
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>com.ct.servlet.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
6.1.6 使用IDEA帮助创建Servlet程序
右键包->new->Servlet
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0xY1v7Nl-1645450757608)(C:\Users\Tao\AppData\Roaming\Typora\typora-user-images\image-20220120170229673.png)]
6.1.7 Servlet程序的体系
interface Servlet:定义Servlet程序访问规范
- class GenericServlet:实现了Servlet接口,做了很多空的实现。并有一个ServletConfig类的对象
- abstract class HttpServlet:实现了service()方法,封装了请求的分发处理doGet()和doPost()
- class 自定义子类:只需要重写doGet()和doPost()实现我们的需求
- abstract class HttpServlet:实现了service()方法,封装了请求的分发处理doGet()和doPost()
6.2 ServletConfig类
ServletConfig是Servlet程序的配置信息类
Servlet程序默认是在第一次访问时创建,ServletConfig是每个Servlet创建时,就创建一个对应的ServletConfig对象
6.2.1 ServletConfig三个作用
- 获取Servlet程序的别名servlet-name的值
- 获取初始化参数init-param
- 获取ServletContext对象
三大作用实现
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("别名是"+servletConfig.getServletName());
String username = servletConfig.getInitParameter("username");
String url = servletConfig.getInitParameter("url");
System.out.println("username:"+username+" url:"+url);
ServletContext servletContext = servletConfig.getServletContext();
System.out.println("servletContext对象:"+servletContext);
}
init-param如何配置,在web.xml中
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.ct.servlet.HelloServlet</servlet-class>
<!--init-param是初始化参数-->
<init-param>
<param-name>username</param-name>
<param-value>CT</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</init-param>
</servlet>
tips:
- ServletConfig只能获取自己Servlet程序的init-param参数
- 重写init方法,一定要调用父类的init方法:super.init(config);
6.3 ServletContext类
- ServletContext是一个接口,它表示Servlet上下文对象
- 一个web工程,只有一个ServletContext对象实例
- ServletContext对象是一个域对象
- ServletContext在web工程部署启动时创建,在web工程停止时销毁
域对象:可以像Map一样存取数据的对象。域是指存取数据的操作范围,是整个web工程。域中的数据是全局的
存 | 取 | 删除 | |
---|---|---|---|
Map | put() | get() | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribute() |
6.3.1 ServletContext类的四个作用
- 获取web.xml配置的上下文参数context-param
- 获取当前工程路径,格式:/工程路径
- 获取工程部署后在服务器硬盘上的绝对路径
- 像Map一样存取数据
6.4 HTTP协议
6.4.1 HTTP协议简介
6.4.2 请求
常用请求头
Accept:表示客户端可以接收的数据类型
Accept-Language:表示客户端可以接收的语言类型
User-Agent:表示客户端浏览器的信息
Host:表示请求时的服务器ip和端口号
GET和POST请求举例
GET:
- form标签 method=get
- a标签
- link标签引入css
- Script标签引入js文件
- img标签引入图片
- iframe引入html页面
- 在浏览器地址栏输入地址后敲Enter
POST:
- form标签 method=post
6.4.3 响应
常见的响应码
200:请求成功
302:请求重定向
404:表示服务器收到请求,但是你要的数据不存在
500:表示服务器收到请求,但是服务器代码错误
MIME类型说明
MIME是HTTP协议中数据类型
6.5 HttpServletRequest类
每次有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议解析好封装到Request对象中,然后传递到service方法中给我们使用。我们可以通过HttpServletRequest对象,获取所有请求的信息
6.5.1 HttpServletRequest类的常用方法
- getRequestURI():获取请求的资源路径
- getRequestURL():获取请求的绝对路径
- getRemoteHost():获取客户端ip
- getHeader(String s):获取请求头的内容
- getMethod():获取请求方式
public class RequestAPIServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("URI=>"+req.getRequestURI());
System.out.println("URL=>"+req.getRequestURL());
System.out.println("客户端 ip=>"+req.getRemoteHost());
System.out.println("请求头User-Agent=>"+req.getHeader("User-Agent"));
System.out.println("请求的方式"+req.getMethod());
}
}
6.5.2 如何获取请求参数
提交页面form.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/07_Servlet/parameterServlet" method="post">
用户名:<input type="text" name="username" ><br/>
密码:<input type="password" name="password"><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="js">JavaScript<br/>
<input type="submit">
</form>
</body>
</html>
如何获取请求参数
public class ParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-----------doGet-------------");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobby");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.asList(hobbies));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求体的字符集为UTF-8,从而解决post中文乱码问题,要在获取参数之前调用才有效
req.setCharacterEncoding("UTF-8");
System.out.println("-----------doPost-------------");
}
}
6.5.3 请求流程
- 用户发请求,action=add
- 项目中,web.xml找到url-pattern = /add
- 然后找到servlet-name
- 再通过servlet-name定位到servlet-class
- 根据用户发送的GET或POST请求,执行相应的doGet()或doPost()
* 一次问题
说明,数据库驱动正确,但是连接不上
//获取连接
protected Connection getConn(){
try {
Class.forName(DRIVER);
return DriverManager.getConnection(URL,USER,PASSWORD);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}
解决方法
-
maven配置
-
删除out文件夹里artifacts和production下的内容
文件->项目结构->工件,删除再重新添加。
-
将mysql-connector的jar包放在Tomcat安装目录的lib目录下
6.6 HttpServletResponse类
HttpServletResponse类和HttpServletRequest类一样,每次请求Tomcat服务器都会创建一个Response对象传递给Servlet程序。
HttpServletRequest表示请求过来的信息,HttpServletResponse表示响应的信息
我们需要设置返回给客户端的信息,都可以使用HttpServletResponse对象来设置
6.6.1 两种响应流的介绍
HttpServletResponse可以通过以下方法获取字节流和字符流
字节流:getOutputStream()
字符流:getWriter()
两个方法不能同时使用
6.6.2 如何往客户端回传数据
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置服务器的字符集
resp.setCharacterEncoding("UTF-8");
//通过响应头设置浏览器的字符集
resp.setHeader("Content-Type","text/html;charset=UTF-8");
//服务器回传数据
PrintWriter writer = resp.getWriter();
writer.write("如何评价");
}
}
修改字符集方式二(推荐)
//方式二:同时设置服务器和客户端的字符集
//但是要在获取流之前调用才有效
resp.setContentType("text/html;charset=UTF-8");
6.7 session会话追踪技术
Http是无状态的:服务器无法判断这两次请求是同一个客户端发过来的,还是不同客户端发的
通过会话跟踪技术来解决无状态问题
- 客户端发请求时,服务器获取session
- 第一次:服务器获取不到session,则会创建新的sessionID,然后响应给客户端
- 之后的请求:客户端的请求会带上sessionID,服务器就能获取到session,从而判断并区分客户端
//演示session
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session,如果没有创建一个新的
HttpSession session = req.getSession();
System.out.println("session ID: "+session.getId());
}
}
6.7.1 常用API
- Request.getSession()
- 空参:获取当前会话,没有则创建一个新的会话
- true:同空参
- false:获取当前会话,没有则返回null,不会创建新的会话
- session.getId():获取sessionID
- session.isNew():判断会话是否是新创建的
- session.getMaxInactiveInterval():会话非激活间隔时长,1800ms=半小时,可以set
- session.invalidate():强制让会话失效
6.7.2 session保存作用域
session中带有作用域,持续时间与会话相同,作用域为当前会话。
同样是Attribute,数据以Key-Value保存。
- session.setAttribute(k,v)
- session.getAttribute(k)
- session.removeAttribute(k)
6.8 请求转发
特点:
- 浏览器地址栏没有变化
- 他们是一次请求
- 他们共享Request域中的数据
- 可以转发到WEB-INF目录下
- 不允许访问工程以外的资源
Servlet1
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的资料
String username = req.getParameter("username");
System.out.println("在Servlet1查看参数:"+username);
//
req.setAttribute("key1","柜台1的章");
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
requestDispatcher.forward(req,resp);
}
}
Servlet2
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的资料
String username = req.getParameter("username");
System.out.println("在Servlet2查看参数:"+username);
//查看柜台1是否有章
Object key1 = req.getAttribute("key1");
System.out.println("柜台1是否有章:"+key1);
//处理自己的业务
System.out.println("Servlet2处理自己的业务");
}
}
6.9 请求重定向
服务器端在响应第一次请求的时候,让客户端再向另外一个URL发出请求,从而达到转发的目的。它本质上是两次HTTP请求,对应两个request对象。(因为之前的地址可能已经被废弃)
特点:
- 浏览器地址栏会发生变化
- 实质是两次请求
- 不会共享域数据
- 无法访问WEB-INF目录下的资源
- 可以访问工程外的资源
方式一
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("曾到此一游 Response1");
//设置状态码302,表示重定向
resp.setStatus(302);
//设置响应头,说明新的地址
resp.setHeader("Location","http://localhost:8080/07_Servlet/response2");
}
}
方式二(推荐)
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("http://localhost:8080/07_Servlet/response2");
}
}
6.10 Thymeleaf
视图模板技术,类似jsp。
在html页面上加载java内存中的数据,称为渲染(render)。
6.10.1 使用流程
- 导入thymeleaf依赖
- 新建一个Servlet类ViewBaseServlet
- 在web.xml文件中配置
- 配置前缀:view-prefix
- 配置后缀:view-suffix
- 使Servlet类继承ViewBaseServlet
- 使用thymeleaf标签
</tr>
<tr th:if="${#lists.isEmpty(session.fruitList)}">
<td colspan="4">对不起,库存为空</td>
</tr>
<tr th:unless="${#lists.isEmpty(session.fruitList)}" th:each="fruit : ${session.fruitList}">
<td th:text="${fruit.fname}"></td>
<td th:text="${fruit.price}">5</td>
<td th:text="${fruit.fcount}">20</td>
<td><img src="images/delete.png" class="delete_images"/></td>
</tr>
/*thymeleaf会将逻辑视图名称对应到物理视图名称上去
* 逻辑:index
* 物理:prefix+"index"+suffix
* 举例: / index .html
* */
6.11 作用域
Servlet程序中域数据的存取范围分为:page、request、session、application
- page:页面级别,几乎不用
- request:一次请求范围有效->HttpServletRequest类
- session:一次会话范围有效->HttpSession类
- application:一次应用程序范围有效->ServletContext类
* 资源不生效问题
IDEA项目中的js,css文件不生效。
**解决方法:**build->Build Artifact->clean,然后再build->build Artifact->rebuild
* base标签
由于相对路径会参照当前浏览器地址栏中的地址来进行跳转,所以在普通使用a标签进行跳转时,会导致404返回错误的地址
使用base标签可以设置当前页面所有的相对路径工作时的参照路径,给所有相对路径提供一个基准
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base href="http://localhost:8080/07_Servlet/a/b/">
</head>
<body>
这是a下的b下的c.html页面<br/>
<a href="../../index.html">跳回首页</a>
</body>
</html>
* web中/的含义
/
- 浏览器解析,得到:http://ip:port/
- 服务器解析,得到:http://ip:port/工程路径
- /servlet1
- servletContext.getRealPath("/")
- request.getRequestDispatcher("/")