JSP 表现层---》Dispatch 分发请求--》Command 交互层---》service 业务逻辑层---》Dao 数据访问层---》数据库
上图为demo程序的总体结构,其中framework包下是“框架”程序,二次开发人员无须改动。
表现层:index.jsp
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>Insert title here</title> <script type="text/javascript"> function doSubmit() { var username = document.getElementById("username").value; var password = document.getElementById("password").value; if (username == "" || password == "") { //alert("用户名和密码不能为空!"); document.getElementById("tips").innerHTML="<font color='red'>用户名和密码不能为空!</span>"; } else { document.loginForm.submit(); } } </script> </head> <body> <span id="tips"></span> <form name="loginForm" action="user.cmd.UserCommand.do?method=login" method="post"> 用户名: <input type="text" id="username" name="username" > 密码: <input type="password" id="password" name="password" > <input type="button" value="提交" onclick="doSubmit()"> </form> </body> </html>
web.xml配置:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>dispatch</servlet-name> <servlet-class>tool.Dispatch</servlet-class> </servlet> <servlet-mapping> <servlet-name>dispatch</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
分发器:Dispatch.java,拦截所有.do结尾的请求,并将请求转发给相应的cmd进行处理。
package framework.dispatch; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import framework.context.CommandContext; import framework.factory.InstanceFactory; public class Dispatch extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException { //设置编码 req.setCharacterEncoding("GBK"); //解析请求的url StringBuffer url = req.getRequestURL(); //http://localhost:8080/test4/UserCommand.do int a = url.lastIndexOf("/"); int b = url.lastIndexOf(".do"); //获取请求的cmd的类名(含包路径) String cmdName = url.substring(a + 1, b);//substring(begin(含),end(不含)),即[) try { //获取请求的cmd的实例 Object cmdObj = InstanceFactory.getInstance(cmdName); //设置Command上下文信息,放于线程变量中。 CommandContext.init(req, resp, getServletContext(), getServletConfig()); //获取请求的方法名 String methodName = req.getParameter("method"); //执行请求的方法,cmd层的方法 Method realMehood = cmdObj.getClass().getMethod(methodName); String forwardPath = realMehood.invoke(cmdObj).toString(); //执行完毕,进行页面跳转 if(forwardPath != null){ req.getRequestDispatcher(forwardPath).forward(req, resp); } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
CommandContext,以线程变量的方式存储当前线程的request、response、servletcontext、servletconfig对象。
package framework.context; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CommandContext { private static ThreadLocal<Map<String,Object>> threadLocal = new ThreadLocal<Map<String,Object>>(); private static final String HTTP_SERVLET_REQUEST = "1"; private static final String HTTP_SERVLET_RESPONSE = "2"; private static final String SERVLET_CONTEXT = "3"; private static final String SERVLET_CONFIG = "4"; /* * 初始化线程局部变量 */ public static void init(HttpServletRequest req,HttpServletResponse resp,ServletContext context,ServletConfig config){ threadLocal.remove(); Map<String,Object> localMap = new HashMap<String, Object>(); localMap.put(HTTP_SERVLET_REQUEST, req); localMap.put(HTTP_SERVLET_RESPONSE, resp); localMap.put(SERVLET_CONTEXT, context); localMap.put(SERVLET_CONFIG, config); threadLocal.set(localMap); } /* * 获取request对象 */ public static HttpServletRequest getRequest(){ return (HttpServletRequest)threadLocal.get().get(HTTP_SERVLET_REQUEST); } /* * 获取response对象 */ public static HttpServletResponse getResponse(){ return (HttpServletResponse)threadLocal.get().get(HTTP_SERVLET_RESPONSE); } /* * 获取servletContext对象 */ public static ServletContext getServletContext(){ return (ServletContext)threadLocal.get().get(SERVLET_CONTEXT); } /* * 获取servletConfig对象 */ public static ServletConfig getServletConfig(){ return (ServletConfig)threadLocal.get().get(SERVLET_CONFIG); } }
command交互层:
package user.cmd; import framework.context.CommandContext; import framework.factory.InstanceFactory; import user.service.UserService; public class UserCommand { UserService userService = InstanceFactory.getInstance(UserService.class.getName()); /* * 执行登录验证 */ public String login(){ String username = CommandContext.getRequest().getParameter("username"); String password = CommandContext.getRequest().getParameter("password"); //调用service层 boolean isOk = userService.checkLogin(username, password); if(isOk){ return "ok.jsp"; } return "fail.jsp"; } }
service层:UserService.java
package user.service; import framework.db.TransactionManager; import framework.factory.InstanceFactory; import user.dao.UserDao; public class UserService { UserDao dao = InstanceFactory.getInstance(UserDao.class.getName()); /* * 执行登录验证 */ public boolean checkLogin(String username, String password) { if (password == null) { return false; } String pass = null; //拿到事务管理器 TransactionManager tm = TransactionManager.getTransManager(); try { //开启事务 tm.beginTransaction(); pass = dao.getPassword(username); //提交事务 tm.commitTransaction(); } catch (RuntimeException e) { e.printStackTrace(); tm.rollbackTransaction();//出现异常则回滚事务 } if (password.equals(pass)) { return true; } else { return false; } } }
Dao层:
package user.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import framework.db.DBUtil; //--创建表 T_USERS //CREATE TABLE T_USERS( // USERNAME VARCHAR2(10) NOT NULL, // PASSWORD VARCHAR2(60) NOT NULL //); //--设置主键 //ALTER TABLE T_USERS ADD CONSTRAINT T_USERS_PK PRIMARY KEY(USERNAME); public class UserDao { /* * 根据用户名,查询密码 */ public String getPassword(String username){ String pass = null; Connection conn = null; PreparedStatement ps = null; ResultSet set = null; try{ conn = DBUtil.getCon(); ps = conn.prepareStatement("select password from t_users where username=?"); ps.setString(1, username); set = ps.executeQuery(); if (set.next()){ pass = set.getString("PASSWORD"); } } catch (SQLException e) { throw new RuntimeException("根据用户名查询密码出错",e); }finally{ DBUtil.close(set, ps, conn); } return pass; } }
实例工厂类:
package framework.factory; import java.util.HashMap; import java.util.Map; /* * 实例工厂类,用于统一管理cmd、service、dao的实例。 */ public class InstanceFactory { //创建一个对象池 private static Map<String,Object> objPool = new HashMap<String, Object>(); /* * 根据类的包路径名称,返回该类的一个实例。 */ public static <T> T getInstance(String clazz){ T obj = null; if(objPool.containsKey(clazz)){//如果对象池中已存在,则直接从对象池中获取。 obj = (T)objPool.get(clazz); }else{ try { //如果对象池中不存在,则动态创建一个该类的实例,并将新创建的实例放入对象池。 obj = (T)Class.forName(clazz).newInstance(); objPool.put(clazz, obj); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return obj; } }
TransactionManager 事务管理器
package framework.db; import java.sql.Connection; import java.sql.SQLException; public class TransactionManager { private Connection con; private TransactionManager(Connection con){ this.con = con; } /* * 开启事务 */ public void beginTransaction(){ try { con.setAutoCommit(false); } catch (SQLException e) { throw new RuntimeException("开启事务失败!",e); } } /* * 提交事务 */ public void commitTransaction(){ try { con.commit(); } catch (SQLException e) { throw new RuntimeException("提交事务失败!",e); }finally{ closeConnection(); DBUtil.threadLocalCon.remove();//将数据库连接从线程局部变量中卸载。 } } /* * 回滚事务 */ public void rollbackTransaction(){ try { con.rollback(); } catch (SQLException e) { throw new RuntimeException("回滚事务失败!",e); }finally{ closeConnection(); DBUtil.threadLocalCon.remove();//将数据库连接从线程局部变量中卸载。 } } /* * 获取事务管理器 */ public static TransactionManager getTransManager(){ return new TransactionManager(DBUtil.getCon()); } /* * 关闭数据库连接,仅限事务管理器内部使用,故private */ private void closeConnection(){ if(con != null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
DBUtil ,用于获取数据库连接和关闭连接
package framework.db; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class DBUtil { private static String url = null; private static String driver = null; private static String username = null; private static String password = null; static{ Properties p = new Properties();//加载数据源配置文件 InputStream inputStream = null; try { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("dataSource.properties"); p.load(inputStream); url = p.getProperty("url"); driver = p.getProperty("driver"); username = p.getProperty("username"); password = p.getProperty("password"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } //线程局部变量 protected static ThreadLocal<Connection> threadLocalCon = new ThreadLocal<Connection>(); /* * 获取数据库连接 */ public static Connection getCon() { Connection con = threadLocalCon.get(); try { if (con == null || con.isClosed()) { Class.forName(driver); con = DriverManager.getConnection(url, username, password); threadLocalCon.set(con); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return con; } /* * 关闭结果集 ResultSet */ public static void closeResultSet(ResultSet rs){ if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } /* * 关闭 句柄 */ public static void closeStatement(Statement st){ if(st != null){ try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } } /* * 在事务中调用dao层方法时,会首先设置事务自动提交为false,该场景下,关闭连接由事务管理器负责。 * 如果dao层方法没有在事务中执行,则此时事务自动提交为true,该场景下,由本方法负责关闭连接。 */ public static void closeConnectionIfAutoCommit(Connection con){ if(con != null){ try { if(con.getAutoCommit()){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } } } /* * 依次关闭ResultSet、Statement、Connection */ public static void close(ResultSet rs,Statement st,Connection con){ closeResultSet(rs); closeStatement(st); closeConnectionIfAutoCommit(con); } }
dataSource.properties配置文件
#Oracle DataSource url=jdbc:oracle:thin:@localhost:1521:loushang driver=oracle.jdbc.driver.OracleDriver username=apitest password=apitest