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