在上一篇文章《在Java中利用动态代理实现数据库连接与事务的自动管理》讲述了如何使用Java动态代理,但要求被代理对象要实现一个接口,如果不想实现接口怎么办呢?使用开源的cglib可以实现对一个类对象的代理,而不要求实现接口。
cglib(Code Generation Library)能够在程序运行的时候动态生成接口的实现类和继承于某个类的子类,它是依赖于ASM的,而ASM可以灵活的操作字节码。在http://cglib.sourceforge.net/可以下载cglib.jar,本文选择的是cglib-nodep-2.2.jar,这是一个不依赖其他jar的文件。如果选择cglib-2.2.jar,还需要加入几个依赖的jar包才行,在http://cglib.sourceforge.net/dependencies.html列出来了所需要的jar文件。
在《在Java中利用动态代理实现数据库连接与事务的自动管理》的基础上,我们继续完善框架,使一个没有实现接口的普通类也可以以注解的方式来自动开打数据库连接和事务,当然是通过cglib来实现的。
首先建立方法的拦截器类。代码如下:
package demo.dynamicproxy;
import java.lang.reflect.Method;
import java.sql.Connection;
import demo.dynamicproxy.annotation.Transactional;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Object target;
private IDb db;
public CglibProxy(Object target) {
this.target = target;
this.db = (IDb) target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("method invoke begin:" + method.getName());
Object result = null;
boolean isNeedTransaction = false;
boolean isAutoCommit = false;
if (method.isAnnotationPresent(Transactional.class)) {
isNeedTransaction = true;
isAutoCommit = method.getAnnotation(Transactional.class).autoCommit();
}
boolean isLocalOpen = false;
boolean rollback = false;
Connection conn = db.getConnection();
System.out.println("isNeedTransaction:" + isNeedTransaction);
System.out.println("isAutoCommit:" + isAutoCommit);
System.out.println("isLocalOpen:" + isLocalOpen);
try {
if (isNeedTransaction) {
if (conn == null) {
isLocalOpen = true;
try {
conn = DbUtil.getConnection();
if (conn == null) throw new Exception("数据库连接获取错误");
conn.setAutoCommit(isAutoCommit);
}
catch (Exception e) {
throw e;
}
db.setConnection(conn);
}
}
//result = methodProxy.invokeSuper(obj, args);
result = method.invoke(this.target, args);
}
catch(Exception e) {
rollback = true;
//e.printStackTrace();
throw (e.getCause()==null)?e:e.getCause();
}
finally {
if (conn != null && isLocalOpen) {
if (!isAutoCommit) {
if (rollback) conn.rollback();
else conn.commit();
}
db.closeConnection();
}
}
System.out.println("method invoke complete:" + method.getName());
return result;
}
}
import java.lang.reflect.Method;
import java.sql.Connection;
import demo.dynamicproxy.annotation.Transactional;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Object target;
private IDb db;
public CglibProxy(Object target) {
this.target = target;
this.db = (IDb) target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("method invoke begin:" + method.getName());
Object result = null;
boolean isNeedTransaction = false;
boolean isAutoCommit = false;
if (method.isAnnotationPresent(Transactional.class)) {
isNeedTransaction = true;
isAutoCommit = method.getAnnotation(Transactional.class).autoCommit();
}
boolean isLocalOpen = false;
boolean rollback = false;
Connection conn = db.getConnection();
System.out.println("isNeedTransaction:" + isNeedTransaction);
System.out.println("isAutoCommit:" + isAutoCommit);
System.out.println("isLocalOpen:" + isLocalOpen);
try {
if (isNeedTransaction) {
if (conn == null) {
isLocalOpen = true;
try {
conn = DbUtil.getConnection();
if (conn == null) throw new Exception("数据库连接获取错误");
conn.setAutoCommit(isAutoCommit);
}
catch (Exception e) {
throw e;
}
db.setConnection(conn);
}
}
//result = methodProxy.invokeSuper(obj, args);
result = method.invoke(this.target, args);
}
catch(Exception e) {
rollback = true;
//e.printStackTrace();
throw (e.getCause()==null)?e:e.getCause();
}
finally {
if (conn != null && isLocalOpen) {
if (!isAutoCommit) {
if (rollback) conn.rollback();
else conn.commit();
}
db.closeConnection();
}
}
System.out.println("method invoke complete:" + method.getName());
return result;
}
}
修改DefaultProxyFactory,使它支持jdk动态代理和cglib代理。修改后的代码如下:
package demo.dynamicproxy;
import java.lang.reflect.Proxy;
import net.sf.cglib.proxy.Enhancer;
public class DefaultProxyFactory {
/**
* jdk动态代理
* @param <T>
* @param target
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) throws Exception {
if (!(target instanceof IDb)) throw new Exception("target must be instance of IDb");
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
TransactionalInvocationHandler handler = new TransactionalInvocationHandler(target);
return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
/**
* cglib代理
* @param <T>
* @param target
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T createCglibProxy(T target) throws Exception {
if (!(target instanceof IDb)) throw new Exception("target must be instance of IDb");
CglibProxy proxy = new CglibProxy(target);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(proxy);
return (T) enhancer.create();
}
}
import java.lang.reflect.Proxy;
import net.sf.cglib.proxy.Enhancer;
public class DefaultProxyFactory {
/**
* jdk动态代理
* @param <T>
* @param target
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) throws Exception {
if (!(target instanceof IDb)) throw new Exception("target must be instance of IDb");
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
TransactionalInvocationHandler handler = new TransactionalInvocationHandler(target);
return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
/**
* cglib代理
* @param <T>
* @param target
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T createCglibProxy(T target) throws Exception {
if (!(target instanceof IDb)) throw new Exception("target must be instance of IDb");
CglibProxy proxy = new CglibProxy(target);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(proxy);
return (T) enhancer.create();
}
}
很简单,这个框架就修改完了。建立一个业务类进行测试:
package demo.business;
import java.sql.ResultSet;
import java.sql.Statement;
import demo.dynamicproxy.DbImpl;
import demo.dynamicproxy.DefaultProxyFactory;
import demo.dynamicproxy.annotation.Transactional;
public class DmlNoInterface extends DbImpl {
public void insert() throws Exception{
executeNonQuery("insert into t(id) values(1)");
System.out.println("insert 1");
}
public void update() throws Exception{
executeNonQuery("update t set id=2 where id=1");
System.out.println("update 2");
}
public void delete() throws Exception{
executeNonQuery("delete from t where id=2");
System.out.println("delete 2");
}
public void select() throws Exception{
Statement stmt = null;
ResultSet rs = null;
try {
stmt = getConnection().createStatement();
rs = stmt.executeQuery("select id from t");
while (rs.next()) {
System.out.println("select :" + rs.getString("ID"));
}
}
catch(Exception e) {
throw e;
}
finally {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
}
}
@Transactional
public void execute() throws Exception{
System.out.println("begin");
insert();
update();
//if (true) throw new Exception("an error"); //此处测试异常
select();
delete();
}
public static void main(String[] args) {
try {
DmlNoInterface dmlNoInterface = new DmlNoInterface();
DmlNoInterface dml= DefaultProxyFactory.createCglibProxy(dmlNoInterface);
dml.execute();
Thread.sleep(20000); //延迟程序退出,可以查看数据库连接有没有释放
System.out.println("over");
}
catch(Exception e) {
e.printStackTrace();
System.out.println("----------"+e.getMessage());
}
}
}
import java.sql.ResultSet;
import java.sql.Statement;
import demo.dynamicproxy.DbImpl;
import demo.dynamicproxy.DefaultProxyFactory;
import demo.dynamicproxy.annotation.Transactional;
public class DmlNoInterface extends DbImpl {
public void insert() throws Exception{
executeNonQuery("insert into t(id) values(1)");
System.out.println("insert 1");
}
public void update() throws Exception{
executeNonQuery("update t set id=2 where id=1");
System.out.println("update 2");
}
public void delete() throws Exception{
executeNonQuery("delete from t where id=2");
System.out.println("delete 2");
}
public void select() throws Exception{
Statement stmt = null;
ResultSet rs = null;
try {
stmt = getConnection().createStatement();
rs = stmt.executeQuery("select id from t");
while (rs.next()) {
System.out.println("select :" + rs.getString("ID"));
}
}
catch(Exception e) {
throw e;
}
finally {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
}
}
@Transactional
public void execute() throws Exception{
System.out.println("begin");
insert();
update();
//if (true) throw new Exception("an error"); //此处测试异常
select();
delete();
}
public static void main(String[] args) {
try {
DmlNoInterface dmlNoInterface = new DmlNoInterface();
DmlNoInterface dml= DefaultProxyFactory.createCglibProxy(dmlNoInterface);
dml.execute();
Thread.sleep(20000); //延迟程序退出,可以查看数据库连接有没有释放
System.out.println("over");
}
catch(Exception e) {
e.printStackTrace();
System.out.println("----------"+e.getMessage());
}
}
}
因为cglib通过这种实现代理是通过生成被代理类的子类来实现,被代理的类不能是final的,即需要能够被继承。另外,对static的方法也是无效的。
这样这个框架就比较完善了,无论业务类有没有实现接口都可以进行数据库连接和事务的自动管理。但与spring的声明式事务相比还有一些差距,但经过完善是可以达到spring那样的效果的,另外本框架需要注入数据库连接,稍显复杂一些。
参考资料: