一个图带你认识MVC框架
说明:该项目只是为了熟悉MVC框架的搭建。
View
包结构
Control和Model---Src目录
关键代码:
数据库连接池工具
采用动态代理的思想+ThreadLocal技术实现一个线程最多只能获取到一个数据库连接。
package cn.hncu.pub;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Queue;
/**
* 我的数据库连接池:
* 动态代理+ThreadLocal
* 做成一个线程共享一个连接
* CreateTime: 2018年9月21日 下午11:36:49
* @author 宋进宇 Email:447441478@qq.com
*/
public class MyConnPool {
//存放连接的队列
private static Queue<Connection> conPool = new LinkedList<Connection>();
/* 存放 Thread 和 con 的键值对,Thread为当前线程,隐示状态。
* tlPool.get/set方法内部会通过Thread.currentThread()获取到当前线程。
*/
private static ThreadLocal<Connection> tlPool = new ThreadLocal<Connection>();
//连接池默认大小为3
private static int size = 3;
static {
Properties p = new Properties();
try {
//加载配置文件
p.load( MyConnPool.class.getClassLoader().getResourceAsStream("myConnPool.properties"));
//读取配置信息
String driver = p.getProperty("driver");
String url = p.getProperty("url");
String username = p.getProperty("username");
String password = p.getProperty("password");
String strSize = p.getProperty("size");
size = Integer.valueOf( strSize );
//加载驱动
Class.forName( driver );
for( int i = 0; i < size; i++ ) {
//因为匿名内部类需要调用该对象,所以用final修饰
final Connection con = DriverManager.getConnection(url, username, password);
//关键点
//这是一个 Connection 接口的代理对象
Object proxiedObj = Proxy.newProxyInstance(
MyConnPool.class.getClassLoader(),
new Class[] {Connection.class},
new InvocationHandler() { //这个才是关键点
//参数 proxy对象 就是proxiedObj对象, method就是被调用的方法对象 , args方法参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断被调用的方法是否是close()方法
if( "close".equals( method.getName() ) ) {
//移除当前线程所对应的连接对象。
tlPool.set(null);
//把连接对象返回连接池中
conPool.add( (Connection) proxy );
System.out.println("还回来一个conn...");
return null;
}
return method.invoke(con, args);
}
});
Connection con2 = (Connection) proxiedObj;
conPool.add(con2);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* 获取数据库连接对象
* @return 数据库连接对象
*/
public synchronized static Connection getConnection() {
//先通过tlPool获取con
Connection con = tlPool.get();
//判断con是否为空
if( con == null ) {//能进来说明当前线程是没有获得到con的,从连接池中获取一个连接
//判断连接池是否为空
if( conPool.size() <= 0) {
//能进来说明连接池为空,当前线程睡一下再重新调用getConnection()方法
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getConnection();
}
con = conPool.poll();
/* 能到这里说明当前线程获取到了一个连接对象,
* 把连接对象存储到tlPool中,这样就形成一个线程对应一个连接对象了。
*/
tlPool.set(con);
}
return con;
}
}
Spring中AOP 思想
通过给service接口中的业务方法加注解实现,自动处理事务功能。
@Transaction注解类
package cn.hncu.pub.transaction;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 通过该注解能够识别调用的方法是否需要做事务
* CreateTime: 2018年9月22日 下午10:44:55
* @author 宋进宇 Email:447441478@qq.com
*/
@Retention( RetentionPolicy.RUNTIME ) //让Transaction注解能够带到JVM中
@Target( value= {ElementType.METHOD} ) //规定Transaction注解只能在方法上使用
public @interface Transaction {
}
生成具有通过注解就可实现自动处理事务的工具---动态代理
package cn.hncu.pub.transaction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import cn.hncu.pub.MyConnPool;
/**
* 生成一个代理对象,该代理对象的执行方法时,
* 根据注解@Transaction的有无,来判断是否自动处理事务。
* CreateTime: 2018年9月22日 下午10:50:39
* @author 宋进宇 Email:447441478@qq.com
*/
public class TransactionProxyUtil implements InvocationHandler{
//被代理的原对象
private Object srcObj;
//构造方法保护化
protected TransactionProxyUtil(Object srcObj) {
this.srcObj = srcObj;
}
/**
* 生成一个代理对象,该代理对象的执行方法时,
* 根据注解@Transaction的有无,来判断是否自动处理事务。<br/>
* 注意:使用注解时,需要在 srcObj的接口中注解相应的方法。
* @param srcObj 被代理的原对象
* @return 代理对象
*/
@SuppressWarnings("unchecked")
public static<T> T getTransactionProxy(T srcObj) {
Object proxiedObj = Proxy.newProxyInstance(
//以当前类的加载器作为生成代理对象的类加载器
TransactionProxyUtil.class.getClassLoader(),
//生成的代理对象需要实现的接口
srcObj.getClass().getInterfaces(),
//InvocationHandler的一个实现类
new TransactionProxyUtil(srcObj) );
return (T) proxiedObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断method是否被 @Transaction注解
if( method.isAnnotationPresent( Transaction.class ) ) {
System.out.println(method.getName()+"方法被@Transaction 注解,开始事务处理");
//能进行说明该方法需要进行事务处理
Connection con = null;
Object returnValue = null;
try {
con = MyConnPool.getConnection();
con.setAutoCommit(false); //开启事务
/执行原对象的业务方法//
returnValue = method.invoke(srcObj, args);
//
//能到这里说明业务处理正常,把事务提交
con.commit();
} catch (Exception e) {
//e.printStackTrace();
//能进入到这里说明业务处理出现问题,把事务进行回滚
con.rollback();
//把捕捉到的异常抛出
throw e;
} finally {
if( con != null ) {
//还原事务自动提交
con.setAutoCommit(true);
// 把连接关闭,其实是换回连接池
con.close();
}
}
return returnValue;
}
System.out.println(method.getName()+"方法没有被@Transaction 注解");
//能到这里 说明是method没有被 @Transaction 注解,则该方法原样执行
return method.invoke(srcObj, args);
}
}
Strust1核心思想
把所有Servlet都要执行的基本代码抽取出来作为一个基类,然后继承该基类,并且不覆盖基类中的方法。基类就可以通过类反射机制来为继承它的子类进行动态调用前端请求的相应方法。
package cn.hncu.pub.baseServlet;
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;
/**
*   为继承该类(Servlet)的子类(Servlet)进行动态调用所需业务。
* 根据前端所带的 action 的参数的值加类反射技术。就可以决定该调用哪个方法处理业务。<br/>
*   <b>注意:</b>子类不能覆盖doGet()和doPost()方法,如果覆盖了那么继承该类就没有意义了。因为这里运用了多态的思想。
* <br/><br/><b>CreateTime:</b><br/>     2018年9月22日 下午11:50:58
* @author 宋进宇 Email:447441478@qq.com
*/
public abstract class BaseServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 当 action值为null或者为""的时候,默认的处理业务方法。
* @param request 请求对象
* @param response 响应对象
* @throws ServletException
* @throws IOException
*/
public abstract void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//能调用到这个方法是因为子类没有覆盖该方法,所以调用了父类(BaseServlet)的方法。
request.setCharacterEncoding("utf-8");
String action = request.getParameter("action");
//当action为 null或者为""时执行默认的业务
if( action == null || action.trim().length() == 0) {
action="execute";
}
//注意 this 是BaseServlet的子类对象!!!!多态new谁调谁。
Class<? extends BaseServlet> clazz = this.getClass();
try {
Method method = clazz.getMethod(action,HttpServletRequest.class,HttpServletResponse.class );
//注意 this 是BaseServlet的子类对象!!!!多态new谁调谁。
method.invoke(this, request,response);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(),e);
}
}
}