前言
在学习设计模式的时候,对静态代理和动态代理做过研究,静态代理倒是很好理解,代码也简单。但是动态代理代码相对复杂,基于当时的水平,没看太懂。这次就来解析一下java中的动态代理。
静态代理缺点
静态代理是简单,但是他不灵活,使用的话需要为每个类都创建一个代理类。使用起来很不方便,并且也造成了大量的代码重复,在实际应用中并不广泛。而动态代理的出现正好解决了这些问题。
动态代理解析
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
我们在invoke方法中,就可以做一些其他的操作,比如事务的控制,日志输出等等,从而实现AOP的效果。在这里需要提一下Servlet的Filter,因为它也可以实现AOP的效果,和动态代理不同的是,Filter基于拦截器的原理,在访问某个目标资源之前,会对访问的请求和响应进行拦截,然后在偷偷执行一些操作。这两种方式相比较而言,使用场景不一样,Filter适用于前台JSP,Servlet;动态代理适用于后台的一些service方法。
Proxy 这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法,它的作用就是得到一个动态的代理对象。
应用实例
下面是一个具体的示例,通过动态代理实现AOP,统一控制事务。大家可以对比一下使用动态代理前后代码的变化,感受AOP的作用。
一、 原来的manager方法,每个都需要添加相关的事务代码,事务代码大量重复。
public void delFlowCard(String[] flowCardVouNos)
throws ApplicationException
{
Connection conn = null;
try
{
// 取得Connection
conn = ConnectionManager.getConnection();
// 开始事务
ConnectionManager.beginTransaction(conn);
// 删除流向单明细
flowCardDao.delFlowCardDetail(flowCardVouNos);
// 删除流向单主表
flowCardDao.delFlowCardMaster(flowCardVouNos);
// 提交事务
ConnectionManager.commitTransaction(conn);
}
catch (DaoException e)
{
// 回滚事务
ConnectionManager.rollbackTransaction(conn);
throw new ApplicationException("删除流向单失败!");
}
finally
{
// 关闭Connection并从ThreadLocal中清除
ConnectionManager.closeConnection();
}
}
二、 使用动态代理实现AOP效果:
① 创建代理类:
/**
* @ClassName: TransactionHandler
* @Description: 动态代理封装事务
* @author: 十期-牛迁迁
* @date: 2015-10-11 下午2:59:15
*/
public class TransactionHandler implements InvocationHandler
{
private Object targetObject;
public Object newProxyInstance(Object targetObject)
{
this.targetObject = targetObject;
//使用Proxy类,通过反射得到一个动态的代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
//在invoke方法中做一些其他操作
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
Connection conn = null;
Object ret = null;
try
{
// 从ThreadLocal中取得Connection
conn = ConnectionManager.getConnection();
if (method.getName().startsWith("add")
|| method.getName().startsWith("del")
|| method.getName().startsWith("modify"))
{
// 手动控制事务提交
ConnectionManager.beginTransaction(conn);
}
// 调用目标对象的业务逻辑方法
ret = method.invoke(targetObject, args);
if (!conn.getAutoCommit())
{
// 提交事务
ConnectionManager.commitTransaction(conn);
}
}
catch (ApplicationException e)
{
// 回滚事务
ConnectionManager.rollbackTransaction(conn);
throw e;
}
catch (Exception e)
{
e.printStackTrace();
if (e instanceof InvocationTargetException)
{
InvocationTargetException ete = (InvocationTargetException) e;
throw ete.getTargetException();
}
// 回滚事务
ConnectionManager.rollbackTransaction(conn);
throw new ApplicationException("操作失败!");
}
finally
{
ConnectionManager.closeConnection();
}
return ret;
}
}
② 在servlet中创建调用代理类
public void init() throws ServletException
{
flowCardManager = (FlowCardManager) getBeanFactory().getServiceObject(
FlowCardManager.class);
// 调用代理
TransactionHandler transactionHandler = new TransactionHandler();
// 对目标生成代理对象
flowCardManager = (FlowCardManager) transactionHandler
.newProxyInstance(flowCardManager);
}
以后在调用manager的方法时会先跳到代理类中执行相应的事务操作,然后在继续调用目标方法。这样我们的事务代码只需要写一次,就可以满足所有的需要,完成了代码的复用,同时维护的话也只需要维护这一个事务代理类即可。这就是面向切面编程带来的好处。