参考资料 尚硅谷javaweb2022版教程
1.CS和BS的异同点
CS:客户端服务器架构模式 优点:一部分安全要求不改的计算任务和存储任务放在客户端进行,不需要把所有的计算和存储都在服务器执行,从而能够减轻服务器的压力,充分利用客户端机器的资源,也能减轻网络负担 缺点:需要安装,升级维护成本较高 BS:浏览器服务器架构模式 优点:客户端不需要安装,维护成本较低 缺点:所有的计算和存储任务都是放在服务器端的,服务器的负荷较重,在服务端计算完成之后把结果再传输给客户端,所以服务端和客户端会进行频繁的通信,从而网络负荷较重。
2.Tomcat
2.1新建项目-部署-运行-访问(底层原理)
下载地址 tomcat下载,解压时路径不要出现中文!
目录结构说明 bin 可执行文件目录 conf 配置文件目录 lib 存放依赖的目录 losg 存放日志的目录 webapps 项目部署的目录 work 工作目录 temp 临时目录 配置环境变量 在系统变量下新建JAVA_HOME 访问localhost 浏览器输入localhost:8080
新建项目 在webapps中新建一个文件夹(名字随意 ) 在该文件夹中新建一个WEB-INF文件夹(名字不能错!) 访问项目 浏览器输入: http://localhost:8080/新建的文件名/html文件
2.2在IDEA下新建javaWeb项目
注:社区版不支持web项目,在校学生可以申请学生免费,详情见这位大佬的文章 idea申请学生免费方法
首先,先新建一个项目,右键添加新的框架支持 添加如图文件 在web中即可新建html文件 配置Tomcat,点击Add configuration, 选择Tomcat的本地路径,后apply 部署项目,同样的步骤点击deployment点击加号点击第一个 点第一个serve,如图设置,url后面是点击运行后默认打开的网址
3.Servlet
3.1Servlet获取参数
首先,我们需要先添加一个依赖 点击:File->Project Structure->Moudles->选择要添加的模块->添加 编写一个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>
编写一个AddServelt用于获取参数
import Dao.FruitDao;
import Dao.FruitDaoImpl;
import bean.fruit;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.dbutils.DbUtils;
import utils.JDBCutils;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
public class AddServelt extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
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");
Connection conn = null;
FruitDao fruitDao = new FruitDaoImpl();
try {
conn = JDBCutils.getConnection();
fruitDao.addFruit(conn,new fruit(0,fname,price,fcount,remark));
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
DbUtils.close(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
在web.xml中添加刚刚写的Servlet
<servlet>
<servlet-name>AddServlet</servlet-name>
<servlet-class>servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>
以上流程的图示 小小的回顾一下
- 新建项目-新建模块
- 在项目中添加web
- 现有artifact,后来才添加的jar包,此时这个jar包并没有添加到部署中,那么在projectSettings中有一个Problems中会有提示的,我们点击fix添加即可,
3.2Servlet继承关系以及生命周期
继承关系
javax.servelt.Servelt接口 javax.servelt.GenericServelt抽象类 javax.servelt.http.HttpServelt抽象子类
相关方法-重点为服务方法 javax.servlet.Servlet接口
void init(config) - 初始化方法 void servlet(request,response) - 服务方法(自动响应请求) void destroy() - 销毁方法
javax.servelt.GenericServelt抽象类
void servlet(request,response) - 仍为抽象方法
javax.servlet.http.HttpServlet抽象子类
void servlet(request,response) - 不是抽象方法 1.String method = req.getMethod() - 获取请求的方式 2.各种if判断,根据请求方式的不同,决定去调用不同的do方法 3.需要根据请求的方式,重写相应的do方法,不然会报405
servlet的生命周期
生命周期:从创建到销毁,对应init(),service(),destroy()
测试代码
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会实例化,初始化,然后服务 从第二次请求开始,每一次都是服务 关闭容器后,其中所有的servlet都会销毁 servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应
3.3HTTP协议
介绍
HTTP:超文本传输协议,最大的作用是确定了请求和响应数据的格式。浏览器发个服务器的数据:请求报文(request)。服务器给浏览器的:响应请求(response)。
请求的三个部分 请求头
展现当前请求的基本信息(请求的方式,请求的URL,请求的协议)
请求消息头
包含了很多客户端需要告诉服务器的消息
名称 | 功能 |
---|
Host | 服务器的主机地址 | Accept | 声明当前能够接受的媒体类型 | Referer | 当前请求来源页面的地址 | Contene-Length | 请求体内容的长度 | Content-type | 请求体的内容类型,这一项的具体值是媒体类型中的某一种 | Cookie | 浏览器访问服务器时携带的Cookie数据 |
请求主体
三种情况: get方式,没有请求体 post方式,有请求体,form data json格式,request payload
响应的三个部分
响应行:包含:协议,响应状态码,响应状态 响应头:包含了服务器的信息,服务器发给浏览器的信息(内容的媒体类型,编码,内容长度等) 响应体:响应的实际内容
3.4会话(session)
HTTP的无状态
HTTP是无状态的(服务器无法判断不同的请求是否来自于同一个客户端) -通过会话跟踪技术来解决无状态的问题
图解会话
会话跟踪技术
- 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
- 下次客户端给服务器法请求时,会把sessionID带给服务器,那么服务器就能企分开客户端了
常用的API
- request.getSession()->获取当前的会话,没有就创建一个新的
- request.getSeeeion(true)->效果和不带参数相同
- request.getSeeeion(false)->获取当前会话,没有返回null,不会创建新的
- session.getID()->获取sessionID
- session.isNew()->判断当前session是否为新的
- session.getMaxInactiveInterval()->session的非激活间隔时长,默认为半小时
- session.invalidate()->让会话立即失效
session的保存作用域
session保存作用域是和具体的某一个session对应的 同一个客户端可以访问其保存的数据 不同的客户端不能访问其他客户端的数据
上图中第二个空白黑框代表另一个服务器,其无法访问第一个服务器保存的数据。
常用API
-session.setAttribute(k,v) -Object session.getAttribute(k) -void removeAttribute(k)
服务器内部转发以及客户端重定向
-服务器内部转发:request.getRequestDispatcher(“…”).forward(request,response); -客户端重定向: response.sendRedirect(“…”);
内部转发: 一次响应的过程,客户端是不知道内部经过多少次转发的 客户端重定向: 两次响应的过程,客户端直到请求URL有变化
3.5thymeleaf(视图模板技术)
1.添加thymeleaf的jar包 2.在web.xml中添加配置 3.新建一个ViewBaseServlet(复制黏贴即可)
首先新建一个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 {
ServletContext servletContext = this.getServletContext();
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
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");
templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
}
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
WebContext webContext = new WebContext(req, resp, getServletContext());
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
并在web.xml中加入如下配置文件
<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>
实现页面的跳转 这段代码的功能是运行后会跳转到/index.html页面
import Dao.FruitDao;
import Dao.FruitDaoImpl;
import bean.fruit;
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;
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
FruitDao fruitDao = new FruitDaoImpl();
List<fruit> list= fruitDao.getFruitList();
HttpSession session = req.getSession();
session.setAttribute("fruitList",list);
super.processTemplate("index",req,resp);
}
}
渲染页面 thymeleaf标签
th:if:判断 th:unless:除非 th:each:相当于foreach th:text:文本
html代码:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" href="CSS/Demo.css">
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">欢迎使用水果库存后台管理系统</p>
<table id="tb1_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}">5</td>
<td th:text="${fruit.fcount}">20</td>
<td><img src="imgs/del.jpg" width=20px height=20px/></td>
</tr>
</table>
</div>
</div>
</body>
</html>
效果:数据库中的两条数据都被加载到页面上了
3.6servlet保存作用域
原始情况下:保存作用域可认为有四个:page(页面级别,现在几乎不用),request(一次请求响应范围),session(一次会话范围),application(整个会话范围)
request
若使用客户端重定向,则demo02无法读出lili(相当于超出了一次响应范围) 若使用内部转发,则可以读出lili
session
与上图类似,在session作用域保存获取数据的代码: req.getSession().setAttribute(“uname”,“lili”); Object obj = req.getSession().getAttribute(“uname”);
session作用域通过重定向与转发均可获取,只要不创建新会话
application
在application作用域保存数据: ServletContext application = req.getServletContext(); application.setAttribute(“uname”,“lili”); 在application作用域中读取数据 ServletContext application = req.getServletContext(); Object obj = application.getAttribute(“uname”);
在application中保存的数据,只要tomcat服务没用停止,数据就是公共的,所有客户端均可访问。
3.7servlet路径问题
一张关于路径的图:
红色为相对路径 蓝色为绝对路径
3.8Servlet Api
初始化 如果我们想要在Servlet初始化时做一些准备工作,那么我们可以重写init方法
下面是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-name>Demo01Servlet</servlet-name>
<servlet-class>servlet.Demo01Servlet</servlet-class>
<init-param>
<param-name>hello</param-name>
<param-value>world</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Demo01Servlet</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
</web-app>
public class Demo01Servlet extends HttpServlet {
@Override
public void init() throws ServletException {
ServletConfig config = getServletConfig();
String initValue = config.getInitParameter("hello");
System.out.println(initValue);
}
}
也可以通过注解的方式
@WebServlet(urlPatterns = {"/demo01"},
initParams = {
@WebInitParam(name="hello",value="world")
}
)
Servlet中的ServletContext和<context-param>
- 获取ServletContext,有很多方法
在初始化方法中: ServletContxt servletContext = getServletContext(); 在服务方法中也可以通过request对象获取,也可以通过session获取: request.getServletContext(); session.getServletContext() 2) 获取初始化值: servletContext.getInitParameter();
业务层
MVC : Model(模型)、View(视图)、Controller(控制器)
视图层:用于做数据展示以及和用户交互的一个界面
控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成
模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件
1) pojo/vo : 值对象
2) DAO : 数据访问对象
3) BO : 业务对象
过滤器filter
- Filter也属于Servlet规范
- Filter开发步骤:新建类实现Filter接口,然后实现其中的三个方法:init、doFilter、destroy
配置Filter,可以用注解@WebFilter,也可以使用xml文件 - Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter(“*.do”)表示拦截所有以.do结尾的请求
- 过滤器链
1)如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的(字母顺序) 2)如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序
事物 之前在DAO层里处理事物的缺点: 通过过滤器来解决上述问题 Listener(了解) 1) ServletContextListener - 监听ServletContext对象的创建和销毁的过程 2) HttpSessionListener - 监听HttpSession对象的创建和销毁的过程 3) ServletRequestListener - 监听ServletRequest对象的创建和销毁的过程
4) ServletContextAttributeListener - 监听ServletContext的保存作用域的改动(add,remove,replace) 5) HttpSessionAttributeListener - 监听HttpSession的保存作用域的改动(add,remove,replace) 6) ServletRequestAttributeListener - 监听ServletRequest的保存作用域的改动(add,remove,replace)
7) HttpSessionBindingListener - 监听某个对象在Session域中的创建与移除 8) HttpSessionActivationListener - 监听某个对象在Session域中的序列化和反序列化
|