我们发现,当我们需要再添加其他方法时,还需要再添加if..else语句。非常麻烦。 我们能否用其他的方法代替if…else语句呢? 答案是可以。我们利用反射来调用方法。
public class BaseServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String method=request.getParameter("m"); //获取参数 if(method==null){ throw new RuntimeException("您没有传递method参数"); } Class c=this.getClass(); //获取当前类对象 Method m=null; //创建方法对象 try { //获取方法对象,获取不到就抛出异常 m=c.getMethod(method, HttpServletRequest.class,HttpServletResponse.class); } catch (NoSuchMethodException | SecurityException e) { throw new RuntimeException("您要调用的方法不存在"); } try { //执行方法,执行错误抛出异常 m.invoke(this, request,response); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException("方法内部出现异常"); } } }这样我们就解决了需要不断去判断的问题 如果我们需要这个功能的话,只需要我们的Servlet继承这个类即可。
我们继续来完善一下我们的BaseServlet类,我们在Servlet中常常需要做重定向和请求转发工作 我们可以简化一下
public class BaseServlet extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String method=request.getParameter("m"); //获取参数 if(method==null){ throw new RuntimeException("您没有传递method参数"); } Class c=this.getClass(); Method m=null; try { m=c.getMethod(method, HttpServletRequest.class,HttpServletResponse.class); } catch (NoSuchMethodException | SecurityException e) { throw new RuntimeException("您要调用的方法不存在"); } try { String result=(String)m.invoke(this, request,response); //执行方法后返回一个字符串,看是否需要请求转发或者重定向操作 if(result==null) //如果字符串为空,则直接结束 { return ; } if(result.contains(":")) //判断字符串包含:字符 { //获取:的位置 int index=result.indexOf(":"); //获得:前面的字符串,判断是请求转发还是重定向 String s=result.substring(0, index); //获取:后面的路径 String path=result.substring(index+1); //如果前面的字符串是forward,则进行请求转发操作 if(s.equals("forward")) { request.getRequestDispatcher(path).forward(request, response); }else if(s.equals("redirect")) //如果是redirect,则执行重定向 { response.sendRedirect(request.getContextPath()+path); }else{ //否则抛出异常 throw new RuntimeException("您当前的操作有误"); } } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException("方法内部出现异常"); } } }我们测试一下我们的类
public class BServlet extends BaseServlet { public String fun1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { return "forward:/index.jsp"; } public String fun2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { return "redirect:/index.jsp"; } public void fun3(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("addUser()"); } } 我们只需要传递一个参数就可以调用相应的方法 例如:http://localhost:8080/BServlet?m=fun2例如:
我们在DAO层处理数据 public class BankDao { public void update(String name,int money) throws SQLException { QueryRunner qr=new QueryRunner(); //使用DBUtils帮我们简化操作 String sql="update bank set money=money+? where name=?"; //sql模板 Object[] params={money,name}; //参数数组 Connection con=JdbcUtils.getConnection(); //获取连接 qr.update(con,sql,params); //执行操作 } } 我们在Service层完成转账业务的逻辑。 public class ServiceTest { public static void main(String[] args) throws SQLException { BankDao dao=new BankDao(); //先创建dao层对象 try{ JdbcUtils.beginTransaction(); //我们如果想要完成事务的话必须获取连接。而我们不能在service层暴露出 Connection con=getConnection();等方法。所以我们直接封装在我们的工具类中。 dao.update("zhangsan", -500); //调用dao层对数据的处理方法 dao.update("lisi", 500); JdbcUtils.commitTransaction(); //提交事务 }catch (Exception e) { JdbcUtils.rollbackTransaction();//回滚事务 } } }而事务的操作必须保证是同一个Connction,我们必须在service层中声明Connection对象。 例如 Connection conn=new Connection()。 但是我们不想将这个对象暴露出来,我们选择将处理事务的方法封装在我们的JdbcUtils工具类中。
事务的一系列操作必须保证是同一个Connection。所以我们需要考虑如何在dao层处理数据时使用同一个连接 -我们在JdbcUtils定义一个事务连接,当我们开启事务时就将这个连接赋值
-当我们dao层处理数据获取连接时,获取连接之前先判断事务连接是否为null,如果不为空,则说明要处理事务,返回这个事务连接
public class JdbcUtils { private static ComboPooledDataSource ds = new ComboPooledDataSource(); private static Connection conn=null; //定义一个事务专用的连接 public static Connection getConnection() throws SQLException { if(conn!=null) return conn; //判断是否开启了事务,如果开启,就将专用的事务的连接返回 return ds.getConnection(); //否则返回一个连接池中的连接 } public static DataSource getDataSource() { return ds; } public static void beginTransaction() throws SQLException { if(conn!=null) throw new RuntimeException("您已经开启过事务"); //得到连接 //开启事务 conn=getConnection(); //如果调用开启事务的方法,就将我们的连接赋值,这样我们就可以保证事务的一系列操作用的是一个连接 conn.setAutoCommit(false); //开启事务 } public static void commitTransaction() throws SQLException { if(conn==null) throw new RuntimeException("您还没有开启事务"); conn.commit(); conn.close(); conn=null; //当我们提交完事务之后,连接只是被归还了,conn并没有为空,这时候再调用,就会报错,因为连接已经归还。所以提交完之后需要将连接为null } public static void rollbackTransaction() throws SQLException { if(conn==null) throw new RuntimeException("您还没有开启事务"); conn.rollback(); conn.close(); conn=null; } }当我们在DAO层获取连接时,我们是否需要关闭连接呢?答案是看情况而定。
比如我们开启了事务,调用了DAO层的方法,如果我们擅自关闭的话,那就会导致调用后面的DAO方法不可用。
所以我们需要判断连接是否是事务的连接,是的话不关闭,因为事务连接提交或者回滚时就会关闭,不是就需要我们自己关闭。
DAO层不涉及事务的处理,所以不能判断连接是否属于事务,我们在service层判断,我们将方法封装到JdbcUtils工具类中。
public class JdbcUtils { private static ComboPooledDataSource ds = new ComboPooledDataSource(); private static Connection conn=null; public static Connection getConnection() throws SQLException { if(conn!=null) return conn; //判断是否开启了事务,如果开启,就将事务的连接返回 return ds.getConnection(); } public static DataSource getDataSource() { return ds; } public static void beginTransaction() throws SQLException { if(conn!=null) throw new RuntimeException("您已经开启过事务"); //得到连接 //开启事务 conn=getConnection(); conn.setAutoCommit(false); } public static void commitTransaction() throws SQLException { if(conn==null) throw new RuntimeException("您还没有开启事务"); conn.commit(); conn.close(); conn=null; //当我们提交完事物之后,连接只是被归还了,conn并没有为空,这时候再调用,就会报错,因为连接已经归还。。所以提交完之后需要将连接为null } public static void rollbackTransaction() throws SQLException { if(conn==null) throw new RuntimeException("您还没有开启事务"); conn.rollback(); conn.close(); conn=null; } public static void closeConnection(Connection con) throws SQLException{ if(conn==null) con.close(); //如果事务连接为null,那就直接关闭 if(conn!=con) con.close(); //如果传入的连接和事务连接不相同,关闭连接。 } }