目录
④servlet的继承关系 —— 重点查看的是服务方法(service())
2.新建一个Servlet类ViewBaseServlet (有两个方法)
3.在web.xml文件中进行配置 (配置两个context-param)
⚪什么是 Servlet
- Servlet 是 JavaEE 规范之一。规范就是接口
- Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。
- Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。
一、Tomcat的安装和配置
1.解压
不能存在中文和空格
2.目录结构说明
- bin:可执行文件目录
- conf:配置文件目录
- logs:日志文件目录
- webapps:项目部署的目录
- work:工作目录
- temp:临时目录
- lib:存放lib的目录
3.配置环境变量,使Tomcat可以运行
因为tomcat也是用Java和C来写的,因此需要JRE,所以需要配置JAVA_HOME
4.启动tomcat,然后访问主页面
二、新建web项目,并在tomcat中部署
1.用户发送请求:action=add
2.项目(Tomcat)中,web.xml中找到 url-pattern = /add
3.servlet-name = AddServlet
4.找与servlet-mapping中servlet-name一致的servlet
5.寻找servlet-class -> com.atxupt.servlets.AddServlet
6.用户发送的是post请求(method=post),因此tomcat会执行AddServlet中的doPost方法
🐟运行时出现的错误
①404:找不到对应的资源
②405:方法不允许
解决方案(参考该文):https://blog.youkuaiyun.com/Dale_zero/article/details/88324077
原因:调用父类的doGet或doPost等方法。父类HttpServlet的doGet或doPost等方法覆盖了重写的doGet或doPost等方法
代码示例(向水鬼库中添加数据)
①web.xml
XML是eXtensible Markup Language的缩写,翻译过来就是可扩展标记语言。所以很明显,XML和HTML一样都是标记语言,也就是说它们的基本语法都是标签。
<?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-name>AddServlet</servlet-name>
<servlet-class>com.atguigu.servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>
</web-app>
②Java代码
package com.atguigu.servlets;
import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
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 AddServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
FruitDAO fruitDAO = new FruitDAOImpl();
boolean flag = fruitDAO.addFruit(new Fruit(0,fname,price,fcount,remark));
request.setCharacterEncoding("utf-8");
System.out.println(flag ? "添加成功!" : "添加失败!");
}
}
③发送请求
④发送的数据
三、注意点
①lib—artifact
先有artifact,后来才添加的MySQL.jar。此时,这个jar包并没有添加到部署包中。那么在projectSettings中有一个Problems中会有提示的,我们点击fix选择add to......另外,我们也可以直接把lib文件夹直接新建在WEB-INF下
②部署时的问题
在部署时,修改application Context,然后再回到server选项卡,检查URL的值。
URL的值指的是tomcat启动完成后自动打开指定的浏览器,默认访问的网址
③设置编码
post方式下,设置编码,防止中文乱码
request.setCharacterEncoding("utf-8");
需要注意的是,设置编码这一句代码必须在所有的获取参数的动作之前
④servlet的继承关系 —— 重点查看的是服务方法(service())
Ⅰ.继承关系
javax.servlet.Servlet接口
—— javax.servlet.GenericServlet抽象类
———— javax.servlet.http.HttpServlet抽象子类
Ⅱ.相关方法
javax.servlet.Servlet接口:
- void init(config)—— 初始化方法
- void service(request,response)—— 服务方法
- void destroy()—— 销毁方法
javax.servlet.GenericServlet抽象类:
- void service(request,response)—— 仍然是抽象的
javax.servlet.http.HttpServlet抽象子类:
void service(request,response)—— 不是抽象的
- String method = req.getMethod();获取请求的方式
- 各种if判断,根据请求方式的不同,决定去调用不同的do方法
- 在HttpServlet这个抽象类中,do方法基本都差不多
Ⅲ.小结
-
继承关系:HttpServlet -> GenericServlet -> Servlet
-
Servlet中的核心方法:init() , service() , destroy()
-
服务方法:当有请求过来时,service方法会自动响应(其实是tomcat容器调用的),在 HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete等等,然后再决定调用的是哪个do开头的方法,HttpServlet中,这些do方法默认都是405的实现风格——要我们子类去实现对应的方法,否则默认会报405错误。
-
我们在新建servlet时,才会去考虑请求方法,从而决定重写哪个do方法
四、servlet的生命周期
Ⅰ.生命周期
从出生到死亡的过程就是生命周期。对应servlet中的三个方法:init() , service() , destroy()
//演示servlet的生命周期
public class Demo02Servlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("正在初始化......");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("正在服务......");
}
@Override
public void destroy() {
System.out.println("正在销毁......");
}
}
Ⅱ.默认情况:
- 第一次接收请求时,这个servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
- 从第二次请求开始,每一次都是服务
- 当容器关闭时,其中的所有servlet实例会被销毁,调用销毁方法
🐟结论
通过案例发现:
- servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应
- 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务(提高系统的启动速度,但是第一次请求时,耗时较长)
- 如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置servlet的初始化时机。
-
servlet的初始化时机:
- 默认是第一次接收请求时,实例化,初始化
- 我们可以通过<load-on-startup>来设置servlet启动的先后顺序,数值越小,启动越靠前,最小值为0
- servlet在容器中,是单例的(所有的请求都是同一个实例去响应),线程不安全的->
- 线程不安全:一个线程需要根据这个实例中的某个成员变量去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
- 启发:尽量不要在servlet中定义成员变量 ,如果不得不定义成员变量,那么:①不要去修改成员变量的值②根据成员变量的值做一些逻辑判断
五、HTTP协议
Ⅰ.介绍
- HTTP:Hyper Text Transfer Protocol超文本传输协议
- HTTP最大的作用就是确定了请求和响应数据的格式。浏览器发送给服务器的数据:响应报文
- HTTP是无状态的
- 由Request和Response组成
Ⅱ.HTTP请求响应包含两个部分:请求和响应
①请求
请求包含三个部分:请求行、请求消息头、请求主体
请求行:展示当前请求的最基本信息
- 请求方式
- 请求的URL
- HTTP协议的版本 (一般都是HTTP1.1)
请求消息头
- 作用:通过具体的参数对本次请求进行详细的说明
- 格式:键值对,键和值之间使用冒号隔开
- 相对比较重要的请求消息头:
请求主体:
- 作用:作为请求的主体,发送数据给服务器。具体来说其实就是POST请求方式下的请求参数。
- 格式:
- 请求体分为三种方式:(1)get方式,没有请求体,但是有一个queryString(查询字符串) (2)post方式:有请求体,form data (3)json格式:有请求体,request payload
② 响应
响应包含三个部分:响应行、响应头、响应体
响应行:
- HTTP协议版本
- 响应状态码
- 响应状态的说明文字
响应消息头:
- 响应体的说明书。
- 服务器端对浏览器端设置数据,例如:服务器端返回Cookie信息。
响应体: 服务器返回的数据主体,有可能是各种数据类型。
- HTML页面
- 图片
- 视频
- 以下载形式返回的文件
- CSS文件
- JavaScript文件
响应状态码
作用:以编码的形式告诉浏览器当前请求处理的结果
404产生的具体原因:
- 访问地址写错了,确实是没有这个资源
- 访问了WEB-INF目录下的资源
- Web应用启动的时候,控制台已经抛出异常,导致整个Web应用不可用,访问任何资源都是404
- 服务器端缓存
六、会话
HTTPSession:表示会话
使用会话的原因是因为:HTTP是无状态的
1.什么是无状态?
2.通过会话跟踪技术解决无状态问题
- 客户端第一次发请求给服务器,服务器获取session,获取不到则创建新的,然后响应给客户端
- 下次客户端给服务器发请求时,会把session ID带给服务器,那么服务器就能获取到了,那么服务器就判断这次请求和上次某次请求是同一个客户端,从而能够区分开客户端
发送请求,获取session ID
第二次发送请求
3.常用的API
- request.getSession() ---> 获取当前的会话,没有则创建一个新的会话
- request.getSession(true) ---> 效果和不带参数相同
- request.getSession(false) ---> 获取当前会话,没有则返回null,不会创建新的
- session.getId() ---> 获取sessionID
- session.isNew() ---> 判断当前session是否是新的
- session.getMaxInactiveInterval() ---> session的非激活间隔时长,默认1800秒
- session.invalidate() ---> 强制性使会话立即失效
4.session保存作用域:一次会话范围都有效
session保存作用域是和具体的某一个session对应的
⚪常用的API
- void session.setAttribute(k,v)
- Object session.getAttribute(k)
- void removeAttribute(k)
package com.atguigu.servlets;
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 Demo04Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getSession().setAttribute("uname","lina");
}
}
package com.atguigu.servlets;
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 Demo05Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object unameObj = request.getSession().getAttribute("uname");
System.out.println(unameObj);
}
}
七、服务器内部转发以及客户端重定向
①服务器端内部转发
request.getRequestDispatcher("...").forward(request,response);
- 是一次请求响应的过程,对于客户端而言,内部经过了多少次转发客户端是不知道的
- 地址栏没有变化
②客户端重定向
response.sendRedirect("...")
- 两次请求响应的过程。客户端肯定知道请求URL有变化
- 地址栏有变化
package com.atguigu.servlets;
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 Demo06Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo06......");
//服务器端内部转发
request.getRequestDispatcher("demo07").forward(request,response);
}
}
package com.atguigu.servlets;
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 Demo07Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo07......");
}
}
八、Thymeleaf(视图模板技术)
1.在服务器端引入Thymeleaf环境
①加入jar包
2.新建一个Servlet类ViewBaseServlet (有两个方法)
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
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 ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
3.在web.xml文件中进行配置 (配置两个context-param)
<!-- 配置上下文参数 -->
<!-- prefix:前缀 suffix:后缀 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
4.使Servlet继承ViewBaseServlet
package com.atguigu.fruit.servlets;
import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.mespringmvc.ViewBaseServlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
//Servlet从3.0版本开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList();
//保存到session作用域
HttpSession session = req.getSession();
session.setAttribute("fruitList",fruitList);
//此处的视图名称是 index
//那么thymeleaf会将这个逻辑视图名称对应到物理视图名称上去
//逻辑视图名称:index
//物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是:/ index .html
super.processTemplate("index",req,resp); //处理模板
}
}
5.根据逻辑视图名称得到物理视图名称
物理视图=视图前缀+逻辑视图+视图后缀
//此处的视图名称是 index //那么thymeleaf会将这个逻辑视图名称对应到物理视图名称上去 //逻辑视图名称:index //物理视图名称:view-prefix + 逻辑视图名称 + view-suffix //所以真实的视图名称是:/ index .html
index.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" href="css\index.css">
<meta charset="UTF-8">
</head>
<body>
<div id="div-container">
<div id="div_fruit_list">
<p class="center f30">欢迎使用水果库存后台管理系统</p>
<table id="tbl_fruit">
<tr>
<th class="w20">名称</th>
<th class="w20">单价</th>
<th class="w20">库存</th>
<th>操作</th>
</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}">20</td>
<td th:text="${fruit.fcount}">5</td>
<td><img src="imgs/th.jpg" class="delImg" width="20px" height="20px"></td>
</tr>
</table>
</div>
</div>
</body>
</html>
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="add" method="post">
名称:<input type="text" name="fname"/><br/>
价格:<input type="text" name="price"/><br/>
库存:<input type="text" name="fcount"/><br/>
备注:<input type="text" name="remark"/><br/>
<input type="submit" value="添加"/>
</form>
</body>
</html>
index.css
*{
color:threeddarkshadow;
}
body{
margin:0;
padding:0;
background-color:black;
}
div{
position:relative;
float:left;
}
#div_fruit_list{
width: 100%;
border: 0px sienna;
}
#div-container{
width:80%;
height:100%;
border:0px solid #0000ff;
margin-left:10%;
float:left;
background-color:honeydew;
border-radius: 8px;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:120px;
margin-left: 20%;
}
#tbl_fruit,#tbl_fruit tr,#tbl_fruit th,#tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.center{
text-align: center;
}
.f30{
font-size: 30px;
}
运行结果
⚪内置对象
1.概念
内置对象其实就是在表达式中可以直接使用的对象
2.基本内置对象
3.公共内置对象
⚪ 部分标签
<th:if> | 判断 |
<th:unless> | 否则 |
<th:each> | 迭代 |
<th:tsxt> | 修改标签文本值 |
if配合not关键词和unless配合原表达式效果是一样的
九、servlet保存作用域
原始情况下,保存作用域我们可以认为有4个:page(页面级别,现在几乎不用)、request(一次请求响应范围)、session(一次会话范围)、application(整个应用程序范围)
1.request:一次请求响应范围
package com.atguigu.demo;
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("/demo01")
public class Demo01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.向request保存作用域保存数据
request.setAttribute("uname","lili");
//2.客户端重定向
// response.sendRedirect("demo02");
//3.服务器端转发
request.getRequestDispatcher("demo02").forward(request,response);
}
}
package com.atguigu.demo;
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("/demo02")
public class Demo02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取request保存作用域保存的数据,key为uname
Object unameObj = request.getAttribute("uname");
System.out.println(unameObj);
}
}
2.session(一次会话范围有效)
package com.atguigu.demo2;
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;
//演示session保存作用域
@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.向session保存作用域保存数据
request.getSession().setAttribute("uname","lili");
//2.客户端重定向
response.sendRedirect("demo02");
//3.服务器端转发
// request.getRequestDispatcher("demo02").forward(request,response);
}
}
package com.atguigu.demo2;
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("/demo02")
public class Demo02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取session保存作用域保存的数据,key为uname
Object unameObj = request.getSession().getAttribute("uname");
System.out.println(unameObj);
}
}
打印结果为:null
此时直接访问demo04不可以获取到 lili
3.application(整个应用程序范围)
⚪ServletContext
Servlet上下文(Tomcat停止,上下文结束)
package com.atguigu.demo;
import javax.servlet.ServletContext;
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;
//演示application保存作用域
@WebServlet("/demo05")
public class Demo05Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.向application保存作用域保存数据
//ServletContext:servlet上下文
ServletContext application = request.getServletContext();
application.setAttribute("uname","lili");
//2.客户端重定向
response.sendRedirect("demo06");
//3.服务器端转发
// request.getRequestDispatcher("demo06").forward(request,response);
}
}
package com.atguigu.demo;
import javax.servlet.ServletContext;
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("/demo06")
public class Demo06Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取application保存作用域保存的数据,key为uname
ServletContext application = request.getServletContext();
Object unameObj = application.getAttribute("uname");
System.out.println(unameObj);
}
}
此时直接访问demo06也可以获取到 lili
十、servlet路径问题