JavaWeb笔记-19- BaseServlet 、Service事务

1、BaseServet


愿景:在一个Servlet中可以有多个请求处理方法。并完成转发或重定向。

做法:
1)客户端发送请求时,必须多给出一个参数,指明要调用的方法
    要求: 1)请求处理方法的签名必须和service相同。
         即:参数类型、返回值、声明的异常 必须相同
2)服务器根据参数调用指定方法

具体步骤:
//通过反射调用方法。
    查询当前类的方法,则得到当前类的Class
        this.getClass();

    1)得到客户端传递过来的方法名,通过方法名再得到Method类的对象

    2)需要得到Class文件,然后调用方法查询,得到当前Method
        查询当前类的方法,则得到当前类的Class

    3)反射调用method表示的方法
    public abstract class BaseServlet extends HttpServlet {
    public void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        /*
         * 1. 获取参数,用来识别用户想请求的方法
         * 2. 然后判断是否哪一个方法,是哪一个我们就调用哪一个
         */
        // 通过反射调用方法。
        // 先得到方法名,判断客户端有没有传值
        String methodName = req.getParameter("method");

        if (methodName == null || methodName.trim().isEmpty()) {
            throw new RuntimeException("您没有传参,无法指定调用发个方法");
        }

        // 通过方法名再得到Method类的对象来调用指定方法。

        // 得到当前类的.Class文件
        Class c = this.getClass();
        Method method = null;
        try {
            //通过Class类对象得到方法
            method = c.getMethod(methodName, 
                    HttpServletRequest.class,
                    HttpServletResponse.class);
        } catch (Exception e) {
            throw new RuntimeException("您要调用的方法:"+methodName +"不存在");
        } 

        //调用method表示的方法
        try {
            //反射调用。 用this对象调用method表示的方法。 传参为(request,response)
            method.invoke(this,req,resp);
        /*
             * 获取请求处理方法执行后返回的字符串,它表示转发或重定向的路径!
             * 帮它完成转发或重定向!
             */
            /*
             * 如果用户返回的是字符串为null,或为"",那么我们什么也不做!
             */
            if(result == null || result.trim().isEmpty()) {
                return;
            }
            /*
             * 查看返回的字符串中是否包含冒号,如果没有,表示转发
             * 如果有,使用冒号分割字符串,得到前缀和后缀!
             * 其中前缀如果是f,表示转发,如果是r表示重定向,后缀就是要转发或重定向的路径了!
             */
            if(result.contains(":")) {
                // 使用冒号分割字符串,得到前缀和后缀
                int index = result.indexOf(":");//获取冒号的位置
                String s = result.substring(0, index);//截取出前缀,表示操作
                String path = result.substring(index+1);//截取出后缀,表示路径
                if(s.equalsIgnoreCase("r")) {//如果前缀是r,那么重定向!
                    resp.sendRedirect(req.getContextPath() + path);
                } else if(s.equalsIgnoreCase("f")) {
                    req.getRequestDispatcher(path).forward(req, resp);
                } else {
                    throw new RuntimeException("你指定的操作:" + s + ",当前版本还不支持!");
                }
            } else {//没有冒号,默认为转发!
                req.getRequestDispatcher(result).forward(req, resp);
            }
        } catch (Exception e) {
            System.out.println("您调用的方法:" + methodName + ", 它内部抛出了异常!");
            throw new RuntimeException(e);
        }
    }

    }
    }

2、Service事务


Service中的方法对应一个业务逻辑。
我们需要在Service中的一方法中调用DAO的多个方法,而这些方法应该在一起事务中

将一个完整的servic设置为事务。

关键点:使DAO的多个方法使用相同的Connection

核心原理:在Service中使用ThreadLocal来完成事务。

这里写图片描述

public class JdbcUtils {
    // 配置文件的默认配置!要求必须给出c3p0-config.xml!!!
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    // 它是事务专用连接!
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    /**
     * 使用连接池返回一个连接对象
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        Connection con = tl.get();
        // 当con不等于null,说明已经调用过beginTransaction(),表示开启了事务!
        if(con != null) return con;
        return dataSource.getConnection();
    }

    /**
     * 返回连接池对象!
     * @return
     */
    public static DataSource getDataSource() {
        return dataSource;
    }

    /**
     * 开启事务
     * 1. 获取一个Connection,设置它的setAutoComnmit(false)
     * 2. 还要保证dao中使用的连接是我们刚刚创建的!
     * --------------
     * 1. 创建一个Connection,设置为手动提交
     * 2. 把这个Connection给dao用!
     * 3. 还要让commitTransaction或rollbackTransaction可以获取到!
     * @throws SQLException 
     */
    public static void beginTransaction() throws SQLException {
        Connection con = tl.get();
        if(con != null) throw new SQLException("已经开启了事务,就不要重复开启了!");
        /*
         * 1. 给con赋值!
         * 2. 给con设置为手动提交!
         */
        con = getConnection();//给con赋值,表示事务已经开始了
        con.setAutoCommit(false);

        tl.set(con);//把当前线程的连接保存起来!
    }

    /**
     * 提交事务
     * 1. 获取beginTransaction提供的Connection,然后调用commit方法
     * @throws SQLException 
     */
    public static void commitTransaction() throws SQLException {
        Connection con = tl.get();//获取当前线程的专用连接
        if(con == null) throw new SQLException("还没有开启事务,不能提交!");
        /*
         * 1. 直接使用con.commit()
         */
        con.commit();
        con.close();
        // 把它设置为null,表示事务已经结束了!下次再去调用getConnection()返回的就不是con了
        tl.remove();//从tl中移除连接
    }

    /**
     * 提交事务
     * 1. 获取beginTransaction提供的Connection,然后调用rollback方法
     * @throws SQLException 
     */
    public static void rollbackTransaction() throws SQLException {
        Connection con = tl.get();
        if(con == null) throw new SQLException("还没有开启事务,不能回滚!");
        /*
         * 1. 直接使用con.rollback()
         */
        con.rollback();
        con.close();
        tl.remove();
    }

    /**
     * 释放连接 
     * @param connection
     * @throws SQLException 
     */
    public static void releaseConnection(Connection connection) throws SQLException {
        Connection con = tl.get();
        /*
         * 判断它是不是事务专用,如果是,就不关闭!
         * 如果不是事务专用,那么就要关闭!
         */
        // 如果con == null,说明现在没有事务,那么connection一定不是事务专用的!
        if(con == null) connection.close();
        // 如果con != null,说明有事务,那么需要判断参数连接是否与con相等,若不等,说明参数连接不是事务专用连接
        if(con != connection) connection.close();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41307491/article/details/81570403
今日推荐