JSP 表現層---》Dispatch 分發請求--》Command 交互層---》service 業務邏輯層---》Dao 數據訪問層---》數據庫javascript
上圖爲demo程序的整體結構,其中framework包下是「框架」程序,二次開發人員無須改動。 html
表現層:index.jspjava
<%@ 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配置:web
<?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進行處理。sql
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交互層:api
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.javaoracle
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層:app
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