该笔记来自B站SGG-JavaWeb学习纪录,文章末贴出链接
2022.05.01
关于JavaBean
https://www.delftstack.com/zh/howto/java/java-bean/
关于数据库连接池
https://www.cnblogs.com/wenxuehai/p/15058811.html
关于读取配置文件
https://juejin.cn/post/6844904193489109006
关于java2mysql数据库驱动jdbc
因为我用的是Mysql 8.x,因此驱动包版本变化了( mysql-connector-java-8.0.16.jar)
关于数据库配置文件
# 老版本:
username=root
password=root
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
# 可能报错信息
# 1. Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'.
# 2. 时区异常
# 3. init datasource error, url: jdbc:mysql:/
# 新版本:
username=root
password=123456
url=jdbc:mysql://localhost:3306/book?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
# driverClassName=com.mysql.jdbc.Driver
driverClassName=com.mysql.cj.jdbc.Driver
initialSize=5
maxActive=10
# 参考链接https://blog.youkuaiyun.com/qq_26545503/article/details/104879286
2022.05.02
JavaEE项目三层架构
分层 包名 含义
1. web层/视图展现层 com.xxx.web(.servlet/.controller)
2. service层 com.xxx.service Service接口包
com.xxx.service.impl Service接口实现类
3. dao持久层 com.xxx.dao Dao接口包
com.xxx.dao.impl Dao接口实现类
4. 实体bean对象 com.xxx.pojo(.entity/.domain/.bean) JavaBean类
5. 测试包(单测) com.xxx.test(.junit) 测试类,单测
6. 工具类 com.xxx.utils jdbc驱动连接
// 以注册业务为例 大致梳理下
1. 创建数据库表
drop database if exists xxx;
create database xxx;
use xxx;
create table t_xxx(
'id' int primary key auto_increment,
'username' varchar(20) not null unique,
'password' varchar(32) not null,
'email' varchar(200)
)
2. 编写数据库表对应的JavaBean对象 // 在pojo包下新建User类
public class User{
private Integer id;
private String username;
private String password;
private String email;
// 利用idea Generator自动生成getter/setter、toString、构造函数
// ...
}
3. 编写连接数据库jdbc工具类
3.1 先导入需要的jar包(数据库和连接池)
druid-1.1.9.jar // 连接池
mysql-connector-java-8.0.16.jar // 数据库
3.2 在源码src文件夹下新建jdbc.properties数据库属性配置文件
// 上面提到过
3.3 在utils包下新建"JdbcUtils"工具类
// 读取jdbc.properties配置文件
// 创建数据库链接池
// 总的来说 jdbc这个工具类就是为了创建连接池跟mysql数据库进行连接和关闭连接
4. 编写"Dao"层
// 在上面那张图中 我们可以基本看到在Dao层其实就是作数据库的CURD
4.1 先导入需要的jar包
commons-dbutils-1.3.jar // 对数据库操作的封装的jar包
4.2 编写抽象类BaseDao // 目前我理解就是将可能会遇到的sql场景 利用DbUtils进行封装
4.3 编写UserDao接口
4.4 编写UserDao的实现UserDaoImpl // 继承至BaseDao实现UserDao
5. 编写"Service"层
// 从上面那张图中 我们可以看到在service层 主要是处理实际业务逻辑、调用dao层保存数据
5.1 编写UserService接口
5.2 编写UserService接口实现类 UserServiceImpl
6. 编写"web"层
// 从上面那张图中 看出web层就是接收客户端消息的第一层
6.1 编写Servlet类 处理请求
Idea中Debug调试
1. 如何调试
// 断点 + Debug启动服务器
2. 调试栏依次从左往右
2.1 让代码往下执行一行
2.2 可以进入当前方法体内(自己写的代码,非框架源码)
2.3 强制进入当前方法体内
2.4 跳出当前方法体外
2.5
2.6 停在光标所在行(相当于临时断点)
3. 变量窗口
可以查看当前方法范围内所有有效的变量
4. 方法调用栈窗口
方法调用栈可以查看当前线程有哪些方法调用信息;
下面的调用上一行的方法。
2022.05.07
补充关于Dao层或Service层生成单元测试
2022.05.03
什么是jsp
jsp全称是java server pages Java的服务器页面
jsp的主要作用就是代替Servlet程序回传html页面的数据
jsp的访问
jsp页面和html页面一样,都是存放在web目录下,访问也跟访问html页面一样
jsp本质
jsp页面本质上是一个Servlet程序;
当第一次访问jsp页面时,Tomcat服务器会帮我们把jsp页面翻译成一个java源文件,并且还有个被编译为.class的字节码程序。
// 如a.jsp
打开源文件:
// a_jsp.java
public final class a_jsp extends org.apache.jasper.runtime.HttpJspBase{}
// 其中HttpJspBase直接继承了HttpServlet类,本质上就是Servlet
// HttpJspBase.java
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage{}
jsp头部page指令相关
jsp的page指令可以修改jsp页面中的一些重要的属性,或者行为。
<%@ page
language="java" // jsp翻译后是什么语言,暂时支持java
contentType="text/html;charset=utf-8" // jsp返回的数据类型是什么,等同于resp.setContentType()参数值
pageEncoding="utf-8" // 当前jsp页面文件本身的字符集
import="java.utils.Map" // 等同于java中导包,导类
// 以下两个属性是给out输出流使用
autoFlush="true" // 设置当out输出流缓冲区满了后,是否自动刷新冲级区,默认true
buffer="8kb" // 设置out缓冲区大小,默认8kb
// 若设置autoFlush="false";buffer="1kb"则当jsp内容超过1kb时则报错显示 如:Java.io.IOException: Error JSP Buffer overflow
errorPage="/errorPage.jsp" // 设置当jsp页面运行时出错,自动跳转去的错误页面路径
// 路径一般以斜杠打头,表示请求地址为http://ip:port/工程路径/映射到代码的web目录下
isErrorPage="false" // 设置当前jsp页面是否是错误信息页面,默认false,若是true则可以从源码中看到获取了一个Exception
session="true" // 设置访问当前jsp页面,是否会创建HttpSession对象,默认true
extends="" // 设置jsp翻译出来的java类默认继承谁
%>
jsp中的声明脚本
声明脚本格式:<%! 声明java代码 %>
作用:可以给jsp翻译出来的java类定义属性、方法、静态代码块、内部类
// 属性
<%!
private String username;
private String password;
%>
// 方法
<%!
public int test(){
return 123;
}
%>
// 静态代码块
<%!
static {
System.out.println("static")
}
%>
// 内部类
<%!
public static class A{
private String email = "1@qq.com";
}
%>
jsp中的表达式脚本
表达式脚本格式:<%= 表达式 %>
作用:给jsp页面上输出数据
// 输出整型
<%=
33
%>
// 输出字符串
<%=
"i am string"
%>
...
特点:
所有表达式脚本会被翻译到_jspService()方法中;
所有表达式脚本会被翻译成out.print()输出到页面上;
可以使用_jspService()方法中的所有对象,如:<%= request.getParameter("test") %>;
所有表达式脚本不能以分号结束
jsp中的代码脚本
代码脚本格式:<% java语句 %>
作用:在jsp页面中,编写自己需要的功能(java语法)
特点:
翻译之后都在_jspService方法中;
_jspService()方法中的对象都可以直接使用;
可以由多个代码脚本块组合完成一个完整的java语句;
可以和表达式脚本一起组合使用,在jsp页面上输出数据。
jsp九大内置对象及四大域对象
// 从编译后的源码中可以看出
request // 请求对象
response // 响应对象
pageContext // jsp的上下文对象
session // 会话对象
application // ServletContext对象
config // ServletConfig对象
out // jsp输出流对象
page // 指向当前jsp的对象
exception // 异常对象
// 4大域对象 所属类 scope范围
pageContext (PageContextImpl类) 当前jsp页面范围内有效
request (HttpServletRequest类) 一次请求内有效
session (HttpSession类) 一个会话范围(打开浏览器直到关闭)
application (ServletContext类) 整个web工程范围内(直到web工程停止)
以上4者可以像Map一样存取数据setAttribute(key,value)&getAttribute(key); 但存取范围有限及优先顺序,从下到大如下排列:
pageContext -> request -> session -> application
jsp中out输出和response.getWriter.write()输出区别及out.write()和out.print()区别
首先:
out也可以再jsp编译后的源码中看见,均使用out进行输出;
reponse响应对象中,也可以设置返回给客户端的输出内容。
区别:
两者均会先输出到各自对应的缓冲区,比如out缓冲区,response缓冲区;
当jsp页面所有代码执行完成后会做两个操作:
1. 执行out.flush()操作,会把out缓冲区中的数据追加写入到reponse缓冲区末尾
2. 执行response刷新操作,把response缓冲区所有数据写回到客户端。
因此,默认情况下两者均存在时,会先输出reponse内容再输出out内容(除非手动的执行out.flush()方法),即在jsp页面时均使用"out"来统一输出。
其中:
out.write() 输出字符串没问题,但输出整型,会输出Ascal码(底层代码 cb[nextChar++] = (char) c 做了char类型的强制类型转换)
out.print() 输出任意类型没问题(底层代码均做了所有类型转换为字符串后再调用out.write()方法输出)
因此:
在jsp页面中,统一使用out.print()来进行输出。
jsp常用标签
1. 静态包含
<%@ include file="" %>
2. 动态包含
<jsp:include page="">
<jsp:param name="test" value="test" />
</jsp:include>
区别:
2.1 使用动态包含时也会把动态部分编译为java代码和字节码
2.2 JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false); 底层是使用这样的方法去动态加载,并把父页面的request\response\out对象传递给了子页面,因此父子共用一个out输出流
2.3 可以传递参数,如上所示,在子页面使用request.getParameter(key)获取
3. 转发标签
<jsp:forward page=""></jsp:forward>
三大组件之二:Listener监听器
1. JavaWeb三大组件
1.1 Servlet程序
1.2 Filter过滤器
1.3 Listener监听器
2. 什么是监听器
它是JavaEE的规范,就是接口
3. 作用
监听某种事物变化,然后通过回调函数让程序做一些相应的处理
4. ServletContextListner监听器
4.1 监听ServletContext对象的创建和销毁
4.2 涉及实现的两个方法
contextInitialized()方法: web工程启动时候回调
contextDestroyed()方法: web工程停止的时候回调
5. 如何使用
5.1 编写一个类去实现ServletContextListener接口
5.2 实现其两个回调方法
5.3 在web.xml中配置监听器
<listener>
<listener-class>classpath</listener-class>
</listener>
2022.05.04
EL表达式
1. 含义
Express Language(表达式语言)
2. 作用
主要是代替jsp页面中的表达式脚本在jsp页面中进行数据的输出
3. 格式
${ 表达式 }
EL表达式获取域数据的顺序
pageContext.setAttribute("key","pageContext");
request.setAttribute("key","request");
session.setAttribute("key","session");
application.setAttribute("key","application");
当四个域中都有相同的key的数据时,EL表达式会按照四个域的从小到大的顺序,找到就输出
${ key } // pageContext
EL中的".“点运算和”[]"中括号运算
.点运算,可以输出Bean对象中某个属性的值 pojo.username // 其中特别注意在EL中.点操作符后面默认找的是其getXxx() 如上面就是找的getUsername() getter方法,因此在EL中使用时注意可以不要get前缀,直接后面的内容
[]中括号运算,可以输出有序集合中某个元素的值 pojo.cities[0]
也可以输出map集合中key含特殊字符的值 pojo.map['x+x+x'] || pojo.map["y.y.y"]
EL中11个隐含对象
// 该11个隐含对象是EL自定义的,因此只能在El表达式中使用,注意与jsp9大内置对象区别
变量 类型 作用
// 注意在jsp内置对象中也有pageContext是当前jsp上下文对象 这里是在EL中使用来获取jsp9大对象
pageContext PageContextImpl 获取jsp中九大内置对象
1. 协议
jsp: <% request.getScheme() %>
EL: ${ pageContext.request.scheme }
2. 服务器IP
jsp: <% request.getServerName() %>
EL: ${ pageContext.request.serverName }
3. 服务器端口
jsp: <% request.getServerPort() %>
EL: ${ pageContext.request.serverPort }
4. 工程路径
jsp: <% request.getContextPath() %>
EL: ${ pageContext.request.contextPath }
5. 请求方法
jsp: <% request.getMethod() %>
EL: ${ pageContext.request.method }
6. 客户端IP
jsp: <% request.getRemoteHost() %>
EL: ${ pageContext.request.remoteHost }
7. 会话id
jsp: <% session.getId() %>
EL: ${ pageContext.session.id }
// 这四个对标jsp中的4大域对象
pageScope Map<String,Object> 获取pageContext域中数据
requestScope Map<String,Object> 获取request域中数据
sessionScope Map<String,Object> 获取session域中数据applicationScope Map<String,Object> 获取ServletContext域中数据
param Map<String,String> 获取请求参数的值
paramValues Map<String,String[]> 获取多个值的参数值
header Map<String,String> 获取请求头的信息
headerValues Map<String,String[]> 获取多个值的请求头值
cookie Map<String,Cookie> 获取当前请求的Cookie信息
// 这里需要注意,我们在servlet配置web.xml时 使用<init-param>来配置的"servletConfig"值 而这里的initParam对标的是我们在servlet配置web.xml中<context-param>这个"servletContext"值
initParam Map<String,String> 获取web.xml中配置的<context-param>上下文参数
cookie Map<String,Cookie> 获取当前请求的Cookie信息
// 这里需要注意,我们在servlet配置web.xml时 使用<init-param>来配置的"servletConfig"值 而这里的initParam对标的是我们在servlet配置web.xml中<context-param>这个"servletContext"值
initParam Map<String,String> 获取web.xml中配置的<context-param>上下文参数
2022.05.05
文件下载-Servlet版
1. 基于Apache的commons.io包
2. 步骤
1.1 获取指定文件的MIME类型
1.2 设置响应头告诉客户端(浏览器)Content-Type为MIME
// 这步是否编写的基础在于若想要浏览器下载 则需要设置,若不需要下载 比如图片可以只返回流直接打开,若是excel,word等则需要设置
1.3 设置响应头中内容处理Content-Disposition 其中
attachment表示"附件"
filename表示"附件名称"
其中在文件名称这里需要注意若是中文,则浏览器响应头中会出现乱码,因此需要对中文名称进行编码
// ie/chrome(URLEncoder解决)
URLEncoder.encode("中文下载名称.xls", StandardCharsets.UTF_8))
// firefox(需要以base64编码及固定格式解决)
"=?utf-8?B?"
+ new BASE64Encoder().encode("中文下载名称.xls".getBytes("utf-8")) + "?=";
=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明。
=? 表示编码内容的开始
charset 表示字符集
B 表示 BASE64 编码
xxxx 表示文件名 BASE64 编码后的内容
?= 表示编码内容的结束
1.4 指定文件输入流读入
1.5 输入流赋值给输出流
3. 源码
// 1. 获取要下载的文件名及文件磁盘路径
String downloadFileName = "中文下载名称.xls";
String downloadFilePath = "/WEB-INF/upload/" + downloadFileName;
// 2. 获取文件Mime及设置响应头
ServletContext servletContext = getServletContext();
// 2.1 获取要下载的文件类型 (通过servletContext读取)
String mimeType = servletContext.getMimeType(downloadFilePath);
// 2.2 需要设置响应头中的Content-Type告诉客户端返回的数据类型
resp.setContentType(mimeType + "charset=utf-8");
// 2.3 设置内容描述:这步比较重要:就是为了区分到底是"直接浏览器打开显示" 还是说需要下载
// 若是下载则需要设置Content-Disposition,其中attachment表示"附件"的意思,后面跟filename"文件名称"
resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downloadFileName, StandardCharsets.UTF_8));
// 3. 读取文件输入流
InputStream resourceAsStream = servletContext.getResourceAsStream(downloadFilePath);
ServletOutputStream outputStream = resp.getOutputStream(); // 输出流
// 使用Apache上传jar包中的方法来复制输入流到输出流
IOUtils.copy(resourceAsStream, outputStream);
2022.05.09
lib包
1. Tomcat
Edit Configurations -> 导入本地tomcat目录下的所有lib包进来 -> 这样再new 文件的时候就可以看见Servlet
2. Servlet
servlet-api.jar
3. 单元测试
junit-4.12.jar
hamcrest-core-1.3.jar
4. xml解析
dom4j-1.6.1.jar
5. JSP
jsp-api.jar // from tomcat
6. EL表达式
el-api.jar // from tomcat
7. JSTL
taglibs-standard-impl-1.2.1.jar
taglibs-standard-spec-1.2.1.jar
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
8. MySQL
mysql-connector-java-8.0.16.jar
9. JDBC连接池
// 主要使用来读取数据库配置文件使用连接池进行连接
druid-1.1.9.jar // utils工具类使用
10. 将Map对象转换为Bean对象
commons-beanutils-1.8.0.jar // utils工具类使用
commons-logging-1.1.1.jar // 依赖
11. MySQL执行sql
commons-dbutils-1.3.jar // dao层使用
12. 文件上传下载
commons-fileupload-1.2.1.jar
commons-io-1.4.jar // IOUtils.copy()下载
13. 谷歌验证码库
kaptcha-2.3.2.jar
14. JSON
gson-2.2.4.jar
分页参数
pageNo 当前页码
当前页码由客户端进行传递
pageSize 每页显示数量
每页显示数量由两种因素决定
1. 客户端进行传递
2. 由页面布局决定
pageCountTotal 总记录数
可由sql语句求出
select count(*) from 表名
pageNoTotal 总页码
由总记录数 / 每页数量求出
若总记录数%每页数量>0,则总页码+1
items 当前页数据
可由sql求出
select* from 表名 limit begin,pageSize;
begin公式:(pageNo - 1) * pageSize
将部分异常抛出给Tomcat配置跳转指定页面
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>500</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error500.jsp</location>
</error-page>
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>404</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error404.jsp</location>
</error-page>
但是需要注意 在某些地方捕获了异常 最后层次必须要抛出异常通知Tomcat,这样配置才会生效
Cookie
1. 什么是Cookie
服务器端通知客户端保存键值对的一种技术;
客户端有了Cookie后,每次请求都发送给服务器端;
每个Cookie大小不超过4kb
2. 服务端如何创建Cookie并通知客户端(Servlet版本)
Cookie cookie = new Cookie("keyTest","valueTest");
resp.addCookie(cookie);
// 实际通过http响应头Set-Cookie:keyTest=valueTest 告知客户端(浏览器)创建
3. 服务端如何获取Cookie(Servlet版本)
客户端http请求头中包含了Cookie信息;
Cookie[] cookies = req.getCookies() // Cookie[]
for(Cookie cookie : cookies){
// cookie.getName() 获取key
// cookie.getValue() 获取value
}
4. 服务端如何修改Cookie(Servlet版本)
4.1 // 创建一个需要修改的同名key的cookie对象
Cookie editCookie = new Cookie("key","value");
// 设置响应头
resp.addCookie(cookie);
4.2 // 先找到需要修改的Cookie对象
// 使用Cookie对象的setValue()方法
cookie.setValue("editValue");
// 设置响应头
resp.addCookie(cookie);
5. Cookie生命控制
主要使用cookie对象的setMaxAge()方法
正数:表示在指定的秒数后过期
负数:表示浏览器一关,Cookie 就会被删除(默认值是-1)
0:表示马上删除 Cookie
6. Cookie的path属性过滤
Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发
path 属性是通过请求的地址来进行有效的过滤
// cookie1 path=/工程路径
// cookie2 path=/工程路径/test
若此时访问路径为http://ip:port/工程路径
此时在浏览器的cookie中只会看见cookie1,而看不见cookie2,同样请求也只会带cookie1,不会带cookie2
若此时访问路径为http://ip:port/工程路径/test/xxx.html
此时浏览器中同时看到cookie1,cookie2,同样请求也会同时带上
Cookie cookie2 = new Cookie("path","path");
cookie2.setPath(req.getContextPath + "/test");
resp.addCookie(cookie2);
Session
1. 什么是Session
是一个接口(HttpSession);
是一个会话,它是用来维护一个客户端和服务器之间关联的一种技术;
每个客户端都有自己的一个Session会话;
2. 服务端如何创建或获取Session
// 创建和获取是同一个方法
HttpSession session = req.getSession()
// 通过isNew()方法来判断是否是第一次创建
Boolean isNew = session.isNew();
// 每个会话都有一个身份证号。也就是 ID 值。而且这个 ID 是唯一的。
String id = session.getId();
3. session生命控制
3.1 Session默认的超时时长在我们的服务器(Tomcat)的配置文件web.xml中配置的,表示当前tomcat服务器下所有的session超时时长是30分钟
<session-config>
<session-timeout>30</session-timeout>
</session-config>
3.2 若想当前Tomcat服务器下的某个web工程,设置其session时长,则需要在自己工程下的web.xml中更改配置
<session-config>
<session-timeout>20</session-timeout>
</session-config>
3.3 若想指定修改个别Session超时时长,则需要使用HttpSession提供的3个方法
session.setMaxInactiveInterval() // 以秒为单位,值为正数时,到点销毁,值为负数时,表示永不销毁
session.getMaxInactiveInterval() // 获取当前session的超时时长
session.invalidate() // 让当前session会话立马超时无效
3.4 Session超时概念
它是指,客户端两次请求的最大间隔时长
4. session技术,底层是基于Cookie技术来实现的
什么是Filter过滤器
1. Filter过滤器是JavaWeb三大组件之一,三大组件分别是:Servlet程序、Listener监听器、Filter过滤器
2. 是一个JavaEE的规范接口,过滤器是执行过滤任务的对象,这些任务是针对对某一资源(servlet 或静态内容)的请求或来自某一资源的响应执行的,抑或同时针对这两者执行。
3. 它的作用:拦截请求、过滤响应
4. 常见的应用场景:
权限检查、日志操作、事务管理...
如何创建Filter过滤器
1. 编写一个类去实现Filter接口
2. 实现其过滤方法doFilter()
// 这里注意下doFilter()方法中的参数 需要强制类型转换为HttpServletRequest
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
Object user = session.getAttribute("user");
if (user == null) {
System.out.println("无权限");
} else {
// 继续执行(必须执行该方法 不然没效果)
filterChain.doFilter(servletRequest, servletResponse);
}
3. web.xml配置Filter拦截路径
<!-- 配置Filter过滤器 大致和Servlet相同 -->
<filter>
<!-- 给Filter过滤器起一个别名-->
<filter-name>AdminFilter</filter-name>
<!-- Filter类全类名路径-->
<filter-class>com.atguigu.filter.AdminFilter</filter-class>
</filter>
<!-- 配置filter拦截路径-->
<filter-mapping>
<!-- 拦截哪一个Filter名称-->
<filter-name>AdminFilter</filter-name>
<!-- 指定拦截路径:斜杠表示http://ip:port/工程路径/ 其映射到idea的web目录下 admin目录下的全部-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
Filter生命周期
1. 执行构造器方法
2. 执行init初始化方法
// 1、2步是在web工程启动的时候执行(Filter已经创建)
3. doFilter()方法
// 每次拦截到请求,会执行
4. destroy方法
// 在web工程停止的时候调用
FilterConfig类
1. Filter过滤器的配置文件类
2. Tomcat每次创建Filter的时候,同时创建了一个FilterConfig类
3. 作用
获取Filter的名称 web.xml中filter-name配置项
获取Filter配置的参数 web.xml中 init-param参数 // 同servletConfig
获取ServletContext对象
// web.xml
<filter>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
</filter>
FilterChain过滤器链
针对同一个路径进行多次过滤器调用
Filter拦截路径方式
1. 精准匹配
<url-pattern>/xxx.jsp</url-pattern>
// http:ip:port/工程路径/xxx.jsp
2. 目录匹配
<url-pattern>/admin/*</url-pattern>
// http:ip:port/工程路径/admin/*
3. 后缀名匹配
<url-pattern>*.html</url-pattern>
<url-pattern>*.png</url-pattern>
Filter过滤器只关心请求地址是否匹配,不关心请求资源是否存在
ThreadLocal
1. ThreadLocal它可以给当前线程关联一个数据(可以是普通变量、对象、数组、集合)
2. 作用:解决多线程的数据安全问题
3. 特点:
3.1 ThreadLocal可以为当前线程关联一个数据(它可以像Map一样存取数据,key为当前线程)
3.2 每一个ThreadLocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例
3.3 每个ThreadLocal对象实例定义的时候,一般都是static类型
3.4 ThreadLocal中保存数据,在线程销毁后,会由JVM虚拟自动释放
public class ThreadLocalTest {
public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
private static final Random random = new Random();
public static class Task implements Runnable {
@Override
public void run() {
// 在 Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为 key 保存到 map 中
int i = random.nextInt(1000);
// 获取当前线程名
String name = Thread.currentThread().getName();
System.out.println("线程[" + name + "]生成的随机数是:" + i);
// 将随机数放入到当前threadLocal实例中
threadLocal.set(i);
// 休眠3秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 在 Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
Object o = threadLocal.get();
System.out.println("在线程[" + name + "]快结束时取出关联的数据是:" + o);
}
}
public static void main(String[] args) {
// 通过for循环创建三个新线程
for (int i = 0; i < 3; i++) {
new Thread(new Task()).start();
}
}
}
ThreadLocal如何配合JDBC开启数据库事务
/**
* 获取数据库连接池中的连接
* @return 如果返回 null,说明获取连接失败<br/>有值就是获取连接成功
*/
public static Connection getConnection(){
Connection conn = conns.get();
if (conn == null) {
try {
conn = dataSource.getConnection();//从数据库连接池中获取连接
conns.set(conn); // 保存到 ThreadLocal 对象中,供后面的 jdbc 操作使用
conn.setAutoCommit(false); // 设置为手动管理事务
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}
/**
* 提交事务,并关闭释放连接
*/
public static void commitAndClose(){
Connection connection = conns.get();
if (connection != null) { // 如果不等于 null,说明 之前使用过连接,操作过数据库
try {
connection.commit(); // 提交 事务
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close(); // 关闭连接,资源资源
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)
conns.remove();
}
/**
* 回滚事务,并关闭释放连接
*/
public static void rollbackAndClose(){
Connection connection = conns.get();
if (connection != null) { // 如果不等于 null,说明 之前使用过连接,操作过数据库
try {
connection.rollback();//回滚事务
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close(); // 关闭连接,资源资源
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)
conns.remove();
}
需要注意的是我们要在dao层如果捕获到异常 要抛出来 throw new RuntimeException(e) 到上层的service层,如果在service层作commit()或者rollback()操作,岂不是所有的service我们都要去try/catch,因此如下如何用Filter配合处理统一作提交或者回滚事务
再次需要注意 若嵌套多层 需要再每一层往外层抛出异常 最后Filter才好捕获!
Filter如何统一try/catch service层作所有servlet操作事务
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain
filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(servletRequest,servletResponse);
JdbcUtils.commitAndClose();// 提交事务
} catch (Exception e) {
JdbcUtils.rollbackAndClose();//回滚事务
e.printStackTrace();
}
}
将部分异常抛出给Tomcat配置跳转指定页面
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>500</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error500.jsp</location>
</error-page>
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>404</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error404.jsp</location>
</error-page>
但是需要注意 在某些地方捕获了异常 最后层次必须要抛出异常通知Tomcat,这样配置才会生效
2022.05.11
JSON
1.
json 是一种轻量级的数据交换格式。
轻量级指的是跟 xml 做比较。
数据交换指的是客户端和服务器之间业务数据的传递格式。
2. 定义
json 是由键值对组成,并且由花括号(大括号)包围。每个键由引号引起来,键和值之间使用冒号进行分隔,多组键值对之间进行逗号进行分隔。
3. 访问
json 本身是一个对象。
json 中的 key 我们可以理解为是对象中的一个属性。
json 中的 key 访问就跟访问对象的属性一样: json 对象.key
4. 两个常用方法
json 的存在有两种形式。
一种是:对象的形式存在,我们叫它 json 对象。
一种是:字符串的形式存在,我们叫它 json 字符串。
一般我们要操作 json 中的数据的时候,需要 json 对象的格式。
一般我们要在客户端和服务器之间进行数据交换的时候,使用 json 字符串。
JSON.stringify() 把 json 对象转换成为 json 字符串
JSON.parse() 把 json 字符串转换成为 json 对象
5. 常用转换 (使用Gson依赖包)
5.1 Json2Bean & Bean2Json
JsonBeanTest jsonBeanTest = new JsonBeanTest(1, "javabean2Json");
Gson gson = new Gson();
// 转换为JSON字符串
String s = gson.toJson(jsonBeanTest);
System.out.println("bean2JSONString: " + s);
// JSON字符串转换为bean对象
JsonBeanTest json2bean = gson.fromJson(s, jsonBeanTest.getClass()); // 参数2是Class Type 针对JavaBean使用
System.out.println("JSONString2bean: " + json2bean);
5.2 Json2List & List2Json
// 1. 创建List对象
List<JsonBeanTest> beanList = new ArrayList<>();
beanList.add(new JsonBeanTest(1, "root"));
beanList.add(new JsonBeanTest(2, "admin"));
Gson gson = new Gson();
// 2. 转为JSON字符串
String s = gson.toJson(beanList);
System.out.println("list2JsonString: " + s);
// 3. JSON转List
List<JsonBeanTest> beanList2 = gson.fromJson(s, new TypeToken<ArrayList<JsonBeanTest>>() {
}.getType()); // 匿名内部类
System.out.println("JsonString2List: " + beanList2);
System.out.println("ListOneOf: " + beanList2.get(1));
5.3 Json2Map & Map2Json
// 1. 创建Map对象
Map<String, JsonBeanTest> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("1", new JsonBeanTest(1, "li"));
stringObjectHashMap.put("2", new JsonBeanTest(2, "da"));
stringObjectHashMap.put("3", new JsonBeanTest(3, "ye"));
Gson gson = new Gson();
// map2json
String s = gson.toJson(stringObjectHashMap);
System.out.println("map2json: " + s);
// json2map
Map<String, Object> o = gson.fromJson(s, new
TypeToken<HashMap<Integer, JsonBeanTest>>() {
}.getType()); // 匿名内部类
System.out.println("json2map: " + o);
System.out.println(o.get(1));