书城项目第八阶段:使用Filter过滤器实现后台的权限管理
- 7、书城第八阶段
7、书城第八阶段
1、使用Filter过滤器拦截/pages/manager/所有内容,实现权限检查
新建com.atguigu/filter/MangerFilter
package com.atguigu.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class ManageFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
Object user = httpServletRequest.getSession().getAttribute("user");
if (user==null){
httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);
}else {
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
}
}
配置web.xml
<filter>
<filter-name>ManageFilter</filter-name>
<filter-class>com.atguigu.filter.ManageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManageFilter</filter-name>
<url-pattern>/pages/manager/*</url-pattern>
<url-pattern>/manager/bookServlet</url-pattern>
</filter-mapping>
2、ThreadLocal的使用
ThreadLocal的作用,它可以解决多线程的数据安全问题。
ThreadLocal它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
Threadlocal的特点:
1、Threadlocal可以为当前线程关联一个数据。(它可以像Map一样存取数据,key为当前线程)
2、每一个Threadlocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 Threadlocal对象实例。
3、每个Threadlocal对象实例定义的时候,一般都是static类型
4、Threadlocal中保存数据,在线程销毁后。会由JVM虚拟自动释放。
新建tmp/src/threadlocal/ThreadLocalTest
package threadlocal;
import java.util.Random;
public class ThreadLocalTest {
// public final static Map<String,Object> data=new ConcurrentHashMap<>();线程安全
// public final static Map<String,Object> data=new Hashtable<>();
public static ThreadLocal<Object> threadLocal=new ThreadLocal<>();
private static Random random=new Random();
public static class Task implements Runnable{
@Override
public void run() {
// threadLocal.set("abc");
// threadLocal.set("bbj");
// System.out.println(threadLocal.get());//bbj 覆盖
//在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(3000);
// } 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();
}
}
}
新建threadlocal/OrderService
package threadlocal;
public class OrderService {
public void createOrder(){
String name = Thread.currentThread().getName();
System.out.println("OrderService 当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.threadLocal.get());
new OrderDao().saveOrder();
}
}
新建threadlocal/OrderDao
package threadlocal;
public class OrderDao {
public void saveOrder(){
String name = Thread.currentThread().getName();
// System.out.println("OrderDao 当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.data.get(name));
System.out.println("OrderDao 当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.threadLocal.get());
}
}
结果
3、使用Filter和ThreadLocal组合管理事务
3.1 使用ThreadLocal确保所有操作都使用同一个Connection来实现
验证是否为同一线程
修改 OrderServiceImpl 模拟错误
package com.atguigu.service.impl;
import com.atguigu.dao.BookDao;
import com.atguigu.dao.OrderDao;
import com.atguigu.dao.OrderItemDao;
import com.atguigu.dao.impl.BookDaoImpl;
import com.atguigu.dao.impl.OrderDaoImpl;
import com.atguigu.dao.impl.OrderItemDaoImpl;
import com.atguigu.pojo.*;
import com.atguigu.service.OrderService;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao =new OrderDaoImpl();
private OrderItemDao orderItemDao=new OrderItemDaoImpl();
private BookDao bookDao=new BookDaoImpl();
@Override
public String createOrder(Cart cart, Integer userId) {
System.out.println("OrderServiceImpl 程序在["+Thread.currentThread().getName()+"]中");
//订单号==唯一性
String orderId=System.currentTimeMillis()+""+userId;
//创建一个订单对象
Order order=new Order(orderId,new Date(),cart.getTotalPrice(),0,userId);
//保存订单
orderDao.saveOrder(order);
//模拟错误
int i=12/0;
//遍历购物车中每一个商品项转换为订单保存到数据库
for (Map.Entry<Integer, CartItem>entry:cart.getItems().entrySet()) {
//获取购物车每一个商品项
CartItem cartItem=entry.getValue();
//转换为订单
OrderItem orderItem=new OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),orderId);
//保存到数据库
orderItemDao.saveOrderItem(orderItem);
//更新库存和销量
Book book = bookDao.queryBookById(cartItem.getId());
book.setSales(book.getSales()+cartItem.getCount());
book.setStock(book.getStock()-cartItem.getCount());
bookDao.updateBook(book);
}
//清空购物车
cart.clear();
return orderId;
}
@Override
public List<Order> showAllOrders() {
return orderDao.queryOrders();
}
@Override
public int sendOrder(String orderId) {
return orderDao.changeOrderStatus(orderId,1);
}
@Override
public List<OrderItem> showOrderDetail(String orderId) {
return orderItemDao.queryOrderItemByOrderId(orderId);
}
@Override
public List<Order> showMyOrders(int userId) {
return orderDao.queryByUserId(userId);
}
@Override
public int receiverOrder(String orderId) {
return orderDao.changeOrderStatus(orderId,2);
}
}
结果 网页,添加购物车去结账
t_order表无记录
t_order_item有记录
原理
修改 JdbcUtils
package com.atguigu.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JdbcUtils {
private static DruidDataSource dataSource;
private static ThreadLocal<Connection> conns=new ThreadLocal<>();
static {
try {
Properties properties=new Properties();
//读取jdbc.properties属性配置文件
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
//从流中加载数据
properties.load(inputStream);
//创建数据连接池
dataSource= (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);//Ctrl+ALT+T
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 获取数据库连接池中的连接
* @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 throwables) {
throwables.printStackTrace();
}
}
return conn;
}
/**
* 提交事务,并关闭释放连接
*/
public static void commitAndClose(){
Connection connection=conns.get();
if (connection!=null){//如果不等于null,说明之前使用过连接,操作过数据库
try {
connection.commit();//提交 事务
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
try {
connection.close();//关闭连接,释放资源
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//一定要执行remove操作,否则就会出错。(因为Tomcat服务器底层使用了线程池技术)
conns.remove();
}
/**
* 回滚事务,并关闭释放连接
*/
public static void rollbackAndClose(){
Connection connection=conns.get();
if (connection!=null){//如果不等于null,说明之前使用过连接,操作过数据库
try {
connection.rollback();//回滚 事务
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
try {
connection.close();//关闭连接,释放资源
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//一定要执行remove操作,否则就会出错。(因为Tomcat服务器底层使用了线程池技术)
conns.remove();
}
/**
* 关闭连接,放回数据库连接池
* @param conn
public static void close(Connection conn){
if (conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
*/
}
修改 JdbcUtilsTest
package com.atguigu.test;
import org.junit.Test;
public class JdbcUtilsTest {
@Test
public void testJdbcUtils(){
// for (int i = 0; i < 100; i++) {
// Connection connection = JdbcUtils.getConnection();
// System.out.println(connection);
// JdbcUtils.close(connection);
// }
}
}
修改 BaseDao
package com.atguigu.dao;
import com.atguigu.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public abstract class BaseDao {
//使用DbUtils操作数据库
private QueryRunner queryRunner=new QueryRunner();
/**
* update() 方法用来执行,Insert\Update\Delete语句
* @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
*/
public int update(String sql,Object... args){
System.out.println("BaseDao 程序在["+Thread.currentThread().getName()+"]中");
Connection connection= JdbcUtils.getConnection();
try {
return queryRunner.update(connection,sql,args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 查询返回一个javabean的sql语句
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回类型的泛型
* @return
*/
public <T> T queryForOne(Class<T> type,String sql,Object... args){
Connection con=JdbcUtils.getConnection();
try {
return queryRunner.query(con,sql,new BeanHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 查询返回多个javabean的sql语句
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回类型的泛型
* @return
*/
public <T>List<T> queryForList(Class<T> type,String sql,Object... args){
Connection con=JdbcUtils.getConnection();
try {
return queryRunner.query(con,sql,new BeanListHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 执行返回一行一列的sql语句
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @return
*/
public Object queryForSingleValue(String sql,Object... args){
Connection conn=JdbcUtils.getConnection();
try {
return queryRunner.query(conn,sql,new ScalarHandler(),args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
修改 OrderServlet
package com.atguigu.web;
import com.atguigu.pojo.Cart;
import com.atguigu.pojo.User;
import com.atguigu.service.OrderService;
import com.atguigu.service.impl.OrderServiceImpl;
import com.atguigu.utils.JdbcUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class OrderServlet extends BaseServlet {
private OrderService orderService = new OrderServiceImpl();
/**
* 生成订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先获取Cart购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart"); //获取Userid
User loginUser = (User) req.getSession().getAttribute("user");
if (loginUser == null) {
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
return;
}
System.out.println("OrderServlet 程序在["+Thread.currentThread().getName()+"]中");
Integer userId = loginUser.getId();
//调用orderservice.createorder(cart,userid);生成订单
String orderId = null;
try {
orderId = orderService.createOrder(cart, userId);
JdbcUtils.commitAndClose();//提交事务
} catch (Exception e) {
JdbcUtils.rollbackAndClose();//回滚事务
e.printStackTrace();
}
// req.setAttribute("orderId",orderId);
//请求转发至pages/cart/checkout.jsp
// req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req,resp);
req.getSession().setAttribute("orderId", orderId);
resp.sendRedirect(req.getContextPath() + "/pages/cart/checkout.jsp");
}
/**
* 查看所有订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 发货
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 查看订单详情
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 查看我的订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void showMyOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 签收订单/确认收货
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void receiverOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
结果 网页,添加购物车去结账
3.2、使用Filter统一给所有Service方法都加上try-catch来实现管理事务
新建 filter/TransactionFilter
package com.atguigu.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action=req.getParameter("action");
// System.out.println(action);
//action的value和调用的方法名是统一的
// if ("login".equals(action)){
System.out.println("处理登录的需求");
// login(req,resp);
// }else if ("regist".equals(action)){
System.out.println("处理注册的需求");
// regist(req,resp);
// }
//反射
try {
//获取action业务鉴别字符串,获取相应的业务方法 反射对象
Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
//调用目标业务方法
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);//把异常抛给Filter过滤器
}
}
}
配置web.xml
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.atguigu.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- /*当前工程下所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
修改 OrderServlet
package com.atguigu.web;
import com.atguigu.pojo.Cart;
import com.atguigu.pojo.User;
import com.atguigu.service.OrderService;
import com.atguigu.service.impl.OrderServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class OrderServlet extends BaseServlet {
private OrderService orderService = new OrderServiceImpl();
/**
* 生成订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先获取Cart购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart"); //获取Userid
User loginUser = (User) req.getSession().getAttribute("user");
if (loginUser == null) {
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
return;
}
System.out.println("OrderServlet 程序在["+Thread.currentThread().getName()+"]中");
Integer userId = loginUser.getId();
//调用orderservice.createorder(cart,userid);生成订单
String orderId = orderService.createOrder(cart, userId);
// req.setAttribute("orderId",orderId);
//请求转发至pages/cart/checkout.jsp
// req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req,resp);
req.getSession().setAttribute("orderId", orderId);
resp.sendRedirect(req.getContextPath() + "/pages/cart/checkout.jsp");
}
/**
* 查看所有订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 发货
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 查看订单详情
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 查看我的订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void showMyOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* 签收订单/确认收货
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
public void receiverOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
修改 BaseServlet
package com.atguigu.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action=req.getParameter("action");
// System.out.println(action);
//action的value和调用的方法名是统一的
// if ("login".equals(action)){
System.out.println("处理登录的需求");
// login(req,resp);
// }else if ("regist".equals(action)){
System.out.println("处理注册的需求");
// regist(req,resp);
// }
//反射
try {
//获取action业务鉴别字符串,获取相应的业务方法 反射对象
Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
//调用目标业务方法
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);//把异常抛给Filter过滤器
}
}
}
3.3、将所有异常都交给Tomcat,让Tomcat显示友好的错误信息页面
在web.xml 中我们可以通过错误页面配置来进行管理。
新建 pages/error/error404.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>纺织用品</title>
<%--静态包含 base标签,css样式,jquery文件 --%>
<%@ include file="/pages/common/head.jsp"%>
</head>
<body>
很抱歉,您访问的页面不存在,或已经被删除!!!<br>
<a href="index.jsp">返回首页</a>
</body>
</html>
并配置 web.xml
<!-- error-page标签配置,服务器出错之后,自动跳转的页面 -->
<error-page>
<!-- error-code是错误类型 -->
<error-code>500</error-code>
<!-- location标签表示,要跳转的页面路径-->
<location>/pages/error/error500.jsp</location>
</error-page>
修改 TransactionFilter
package com.atguigu.filter;
import com.atguigu.utils.JdbcUtils;
import javax.servlet.*;
import java.io.IOException;
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@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();
throw new RuntimeException(e);//把异常抛给Tomcat管理展示友好的错误页面
}
}
@Override
public void destroy() {
}
}
新建error/error404.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>纺织用品</title>
<%--静态包含 base标签,css样式,jquery文件 --%>
<%@ include file="/pages/common/head.jsp"%>
</head>
<body>
很抱歉,您访问的页面不存在,或已经被删除!!!<br>
<a href="index.jsp">返回首页</a>
</body>
</html>
配置web.xml
<!-- error-page标签配置,服务器出错之后,自动跳转的页面 -->
<error-page>
<!-- error-code是错误类型 -->
<error-code>404</error-code>
<!-- location标签表示,要跳转的页面路径-->
<location>/pages/error/error404.jsp</location>
</error-page>