Web开发
参考资料
尚硅谷javaweb2022版教程
1.CS和BS的异同点
CS:客户端服务器架构模式
优点:一部分安全要求不改的计算任务和存储任务放在客户端进行,不需要把所有的计算和存储都在服务器执行,从而能够减轻服务器的压力,充分利用客户端机器的资源,也能减轻网络负担
缺点:需要安装,升级维护成本较高
BS:浏览器服务器架构模式
优点:客户端不需要安装,维护成本较低
缺点:所有的计算和存储任务都是放在服务器端的,服务器的负荷较重,在服务端计算完成之后把结果再传输给客户端,所以服务端和客户端会进行频繁的通信,从而网络负荷较重。
2.Tomcat
2.1新建项目-部署-运行-访问(底层原理)
目录结构说明
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 {
//相应post请求
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//post方式下,设置编码,防止中文乱码
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");
//添加至数据库中的操作,代码书写方式见JDBC笔记
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>
<!--
1.用户发请求,action=add
2.项目中,找到url-pattern =/add
3.找servlet-name=AddServlet
4.找和ervlet-mapping中servlet-name一致的Servlet
5.最终找到servlets.AddServlet
6.用户发送的是post,所以会执行AddServlet中的doPost
-->
以上流程的图示
小小的回顾一下
- 新建项目-新建模块
- 在项目中添加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()
测试代码
//演示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会实例化,初始化,然后服务
从第二次请求开始,每一次都是服务
关闭容器后,其中所有的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 {
// 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());
}
}
并在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;
//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> list= fruitDao.getFruitList();
//保存到session作用域
HttpSession session = req.getSession();
session.setAttribute("fruitList",list);
//此处的视图名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是: / index .html
//所以最终跳转的页面是 /index.html
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 {
/* 我们可以通过如下步骤去获取初始化设置的数据
- 获取config对象:ServletConfig config = getServletConfig();
- 获取初始化参数值: config.getInitParameter(key);*/
@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域中的序列化和反序列化