关于动态代理,大家显式使用的可能比較少,可是说到Spring的Interceptor、各种各样的事务管理,大家会更熟悉一些,没错,这些在底层实现上,都是使用的动态代理,确切的说,想要为一个类的方法,动态加入功能,比方验证、资源释放、日志处理等,大部分都是借助动态代理。
为了平缓的过渡,先来说一下静态代理。
静态代理
静态代理的思路非常easy:把一个真实对象的实例放到代理对象的实例中。然后调用代理对象方法,代理对象的方法调用真实对象的方法,以事务管理为例。例如以下:
UserDao
package com.tgb.staticproxy;
public interface UserDao {
public void add();
public void deleteAll();
}
UserDaoImpl
package com.tgb.staticproxy;
public class UserDaoImpl implements UserDao {
public void add()
{
System.out.println("加入一名用户到数据库");
}
public void deleteAll()
{
System.out.println("删除全部用户");
}
}
UserDaoProxy
package com.tgb.staticproxy;
public class UserDaoProxy implements UserDao {
UserDao userDao=null;
public UserDaoProxy(UserDao userDao)
{
this.userDao=userDao;
}
public void add()
{
System.out.println("开启本地事务");
userDao.add();
System.out.println("提交或回滚事务");
}
public void deleteAll()
{
System.out.println("开启本地事务");
userDao.deleteAll();
System.out.println("提交或回滚事务");
}
}
Test
package com.tgb.staticproxy;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
UserDao userDao=new UserDaoImpl();
UserDaoProxy userDaoProxy=new UserDaoProxy(userDao);
//測试加入
userDaoProxy.add();
System.out.println("..........分隔符..........");
//測试删除
userDaoProxy.deleteAll();
}
}
运行结果
开启本地事务 加入一名用户到数据库 提交或回滚事务 ..........分隔符.......... 开启本地事务 删除全部用户 提交或回滚事务
可是静态代理管理事务的方式问题非常大,每一个Dao类的每一个方法都须要开启和关闭事务,不仅代码反复严重,而事务本来是和业务没什么关联,却耦合到一起。
动态代理
JDK动态代理
相同以事务管理为例,例如以下:
UserDao
package com.tgb.dynamicproxy;
public interface UserDao {
public void add();
public void deleteAll();
}
UserDaoImpl
package com.tgb.dynamicproxy;
public class UserDaoImpl implements UserDao {
@Override
public void deleteAll() {
System.out.println("删除全部用户信息");
}
@Override
public void add() {
System.out.println("加入一名用户到数据库");
}
}
Handler
package com.tgb.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Handler implements InvocationHandler {
private Object target;
public Handler(Object target)
{
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//开启事务
before();
//运行业务
method.invoke(target, args);
//提交或回滚事务
after();
return null;
}
public void before()
{
System.out.println("開始本地事务");
}
public void after()
{
System.out.println("提交或回滚事务");
}
}
Test
package com.tgb.dynamicproxy;
import java.lang.reflect.Proxy;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
try{
UserDao impl=new UserDaoImpl();
Handler handler=new Handler(impl);
UserDao proxy=(UserDao)Proxy.newProxyInstance
(impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), handler);
//測试加入
proxy.add();
System.out.println("..........分隔符..........");
//測试删除
proxy.deleteAll();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
运行结果
開始本地事务 加入一名用户到数据库 提交或回滚事务 ..........分隔符.......... 開始本地事务 删除全部用户信息 提交或回滚事务
JDK的动态代理克服了静态代理耦合和代码反复的问题,可是JDK的代理模式有个比較严重的问题。如UserDao必需要有接口才干够使用JDK动态代理,这就大大限制了JDK动态代理的范围。
cglib动态代理
asm能够动态生成字节码,cglib对asm进行了再封装,cglib并非为了动态代理而生的,可是利用它的特性。却能够非常好的实现动态代理。UserDaoImpl
package com.tgb.cglib;
public class UserDaoImpl {
public void deleteAll() {
System.out.println("删除全部用户信息");
}
public void add() {
System.out.println("加入一名用户到数据库");
}
}
CglibProxy
package com.tgb.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Object target;
private CglibProxy(Object target){
this.target = target;
}
//产生代理对象
@SuppressWarnings("unchecked")
public static <T> T proxyTarget(T t){
Enhancer en = new Enhancer();
en.setSuperclass(t.getClass());
en.setCallback((Callback) new CglibProxy(t));
T tt = (T) en.create();
return tt;
}
//运行拦截
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("开启本地事务");
Object o = method.invoke(target, args);
System.out.println("提交或回滚事务");
return o;
}
}
Test
package com.tgb.cglib;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
//获代替理对象
UserDaoImpl impl=CglibProxy.proxyTarget(new UserDaoImpl());
//測试加入
impl.add();
System.out.println("..........分隔符..........");
//測试删除
impl.deleteAll();
}
}
执行结果
开启本地事务 加入一名用户到数据库 提交或回滚事务 ..........分隔符.......... 开启本地事务 删除全部用户信息 提交或回滚事务
能够看到,这次UserDaoImpl并没有实现不论什么接口接口实现动态代理的功能。
总结
这篇博客本来打算写JDK和cglib动态代理的源代码介绍的。写着写着就写成介绍代理都有哪些类型及实现方式了,再写篇幅就有点长了。所以放到下篇博客说明。