JavaWeb学习笔记(五)
1.Filter
1.1 介绍
- Filter过滤器是JavaWeb的三大参数之一。三大组件分别是:Servlet程序,Listener监听器,Filter过滤器。
- Filter过滤器是JavaEE的规范,也就是接口。
- Filter过滤器的作用是拦截请求,过滤响应。
- 拦截器的常见应用场景:①权限检查②日记操作③事务管理…
1.2 Filter简单实现
1.2.1 场景引入
1.2.2 代码展示
1.AdminFilter
public class AdminFilter implements Filter {
@Overide
public void init(FilterConfig filterConfig) throws ServletException {
}
@Overide
//用于拦截请求,可以做权限检查
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpSession session = req.getSession();
if(session.getAttribute("username") == null){
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
return;
}else {
//让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Overide
public void destroy() {
}
}
2.web.xml中配置
<filter>
<filter-name>AdminFilter</filter-name>
<filter-class>com.test.filter.AdminFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AdminFilter</filter-name>
<url-parttern>/admin/*</url-parttern>
</filter-mapping>
filter-name:过滤器别名,一般为类名
filter-class:全类名
filter-mapping:配置过滤器的拦截路径
url-parttern:配置拦截路径 “/”对应http://ip:port/工程路径/ 映射到IDEA的web目录
1.2.3 Filter实现步骤
- 编写一个类去实现Filter接口
- 实现过滤方法doFilter()
- 到web.xml中去配置Filter的拦截路径
1.3 Filter生命周期
Filter的生命周期包含以下几个方法
- 构造方法
- init初始化方法
- doFilter过滤方法
- destroy销毁方法
(1)第1、2步在Web工程启动就已执行,即Filter在工程启动时就创建
(2)第3步在每次拦截到请求就会执行
(3)第4步在停止Web工程时就会执行,即停止工程就会销毁Filter
1.4 FilterConfig类
1.4.1 介绍
FilterConfig类是Filter过滤器的配置文件。
Tomcat每次创建Filter类时,同时也会创建对应的FilterConfig类,包含了Filter配置文件的配置信息。
FilterConfig类的作用是获取filter过滤器的配置内容
1.获取Filter的名称即filter-name的内容 FilterConfig.getFilterName()
2. 获取在web.xml中配置的init-param初始化参数 FilterConfig.getInitiParameter()
3. 获取ServletContext对象 FilterConfig.getServletContext();
1.5 FilterChain 过滤器链
1.5.1 多过滤器工作场景
FilterChain就是过滤器链,多个过滤器是如何工作的。
1.正常工作场景
2.去掉Filter2的doFilter()方法
没有doFilter方法,就不会继续执行请求的页面,会立即返回。
如删除Filter2的doFilter()方法,就会执行“Filter1的前置代码→Filter2的前置代码→Filter1的后置代码”。
如果删除Filter1的doFilter()方法,就会执行“Filter1的前置代码”。
3.多个Filter的执行顺序
在多个Filter过滤器执行过程中,它们执行的优先顺序是由在web.xml中从上到下配置的顺序决定的。
4.多个Filter过滤器执行的特点
(1)所有Filter和目标资源默认都执行在同一个线程中
(2)多个Filter共同执行的时候,它们都使用同一个Request对象
1.6 Filter的拦截路径
Filter过滤器有三种匹配方式。
- 精确配置
<url-param>/target.jsp</url-param>
以上配置的路径,请求地址必须为:http://ip:port/工程路径/target.jsp - 目录配置
<url-param>/admin/</url-param>
以上配置的路径,请求地址必须为:http://ip:port/工程路径/admin/ - 后缀名配置
<url-param>*.html</url-param>
以上配置的路径,表示请求地址必须以.html结尾才会拦截到
Filter过滤器只关心请求的地址是否匹配,不关心请求资源是否存在。
1.7 使用Filter过滤器实现权限管理
1.7.1 使用Filter过滤器拦截/pages/manager/下的所有内容,实现全线检查
1.ManagerFilter
public class ManagerFilter implements Filter {
@Overide
public void init(FilterConfig filterConfig) throws ServletException {
}
@Overide
//用于拦截请求,可以做权限检查
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
Object obj = HttpSession session = req.getSession();
if(obj == null){
servletRequest.getRequestDispatcher("pages/user/login.jsp").forward(servletRequest, servletResponse);
return;
}else {
//让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Overide
public void destroy() {
}
}
2.web.xml
<filter>
<filter-name>ManagerFilter</filter-name>
<filter-class>com.test.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManagerFilter</filter-name>
<url-pattern>/pages/manager/*</filter-param>
<url-pattern>/manager/bookServlet</filter-param>
</filter-mapping>
urlPattern可以有多个,如果要限制Servlet,就限制该Servlet所映射到的访问地址。
1.7.2 ThreadLocal的使用
-
ThreadLocal的作用,它可以解决多线程的数据安全问题。
-
ThreadLocal它可以给当前线程关联一个数据(可以是普通变量、对象、数组或集合)。
-
ThreadLocal的特点:
(1)ThreadLocal可以为当前线程关联一个数据。(可以像Map一样存取数据)
(2)每个ThreadLocal对象只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例。
(3)每个ThreadLocal对象实例定义的时候,一般都是static类型。
(4)ThreadLocal中保存数据,在线程销毁后,会由JVM虚拟机自动释放。 -
代码实现
package com.test;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
public class ThreadLocalTest {
// public static Map<String,Object> data = new Hashtable<String, Object>();
public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
private static Random random = new Random();
public static class Task implements Runnable {
@Override
public void run() {
//在run方法中,随机生成一个变量(线程要关联的数据),以线程名为key保存到map中
Integer i = random.nextInt(1000);
String name = Thread.currentThread().getName();
System.out.println("线程["+ name +"]生成的随机数是:" + i);
// data.put(name,i);
threadLocal.set(i);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new OrderService().createOrder();
//run方法结束之前,以当前线程名获取出数据并打印,查看是否可以取出操作
// Object o = data.get(name);
Object o = threadLocal.get();
System.out.println("线程["+ name +"]快结束时取出的关联的数据是:" + o);
}
}
public static void main(String[] args) {
for(int i = 0; i < 3; i++){
new Thread(new Task()).start();
}
}
}
1.7.3 使用Filter和ThreadLocal组合管理事务
事务概念
1.如结算一个订单,需要经历保存订单→发起付款→减扣库存等环节,这些环节必须要执行全部执行,如果中间一个环节失败则会出问题,这种情况叫做事务。
2.JDBC的数据库事务管理
Connection conn = JdbcUtils.getConnection();
try {
conn.setAutoCommit(false); //设置为手动管理事务
//执行一系列的jdbc操作
conn.commit(); //手动提交事务
} catch (Exception e) {
conn.rollback(); //回滚事务
} finally{
JdbcUtils.close(conn); //关闭连接
}
(1)要确保所有事务要么都成功,要么都失败,就必须要使用数据库的事务。
(2)要确保所有操作都在一个事务内,就必须要确保所有操作都使用同一个Connection对象。
(3)我们可以使用ThreadLocal对象来确保所有操作都使用同一个Connection连接对象,那么操作的前提条件是所有操作都必须要在同一个线程内完成。
使用ThreadLocal管理事务
1.修改JdbcUtils
public class JdbcUtils{
private static DruidDataSource dataSource;
private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
static{
try{
Properities properities = new Properities();
//读取jdbc.properities配置文件
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properities");
//从流中加载数据
properities.load(inputStream);
//创建数据库连接池
dataSource = (DruidDataSource) new DruidDataSourceFactory.createDataSource(properities);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 获取数据库连接池中的连接
* @return
*/
public static Connection getConnection(){
Connection conn = conns.get();
if(conn == null) {
try{
conn = dataSource.getConnection();
conns.set(conn); //保存到ThreadLocal对象中
conn.setAutoCommit(false); //设置为手动提交事务
}catch (Exception e){
e.printStackTrace();
}
}
return conn;
}
/**
* 提交事务并关闭连接
*/
public static void commitAndClose() {
Connection conn = conns.get();
if(conn != null) { //如果不为空,说明之前使用过连接操作过数据库
try {
conn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.close(); //关闭连接,释放资源
} catch (Exception e) {
e.printStackTrace();
}
}
}
conns.remove(); //一定要执行remove操作,否则就会出错(因为Tomcat服务器底层使用了线程池)
}
/**
* 回滚事务并关闭连接
*/
public static void rollbackAndClose() {
Connection conn = conns.get();
if(conn != null) { //如果不为空,说明之前使用过连接操作过数据库
try {
conn.rollback();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.close(); //关闭连接,释放资源
} catch (Exception e) {
e.printStackTrace();
}
}
}
conns.remove(); //一定要执行remove操作,否则就会出错(因为Tomcat服务器底层使用了线程池)
}
}
2.修改各个Dao
(1)把finally中的关闭连接删除掉
(2)catch到异常必须抛出,否则service拿不到,无法回滚
3.修改Servlet(调用Service的地方)
try{
①执行service方法
②commitAndClose()提交事务
}catch(Exception e){
①rollbackAncClose()回滚事务
}
使用Filter过滤器统一给所有Service方法都加上try-catch
1.TransactionFilter
public class TransactionFilter implements Filter {
@Overide
public void init(FilterConfig filterConfig) throws ServletException {
}
@Overide
//用于拦截请求,可以做权限检查
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(servletRequest,servletResponse);
JdbcUtils.commitAndClose();
}catch (Exception e) {
JdbcUtils.rollbackAncClose();
e.printStackTrance();
}
}
@Overide
public void destroy() {
}
}
2.web.xml
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.test.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用Tomcat统一管理异常,展示错误信息页面
1.在web.xml中可以通过错误页面配置来进行管理
<error-page>
<error-code>500</erroe-code>
<location>/pages/error/error500.jsp</location>
</error-page>
error-page:配置服务器出错之后自动跳转的页面
error-code:错误类型
location:要跳转到的页面路径
2.在Filter中要再把异常抛出给Tomcat服务器