首页 > 代码库 > 深入剖析动态代理(上)之代理的方式

深入剖析动态代理(上)之代理的方式

    关于动态代理,大家显式使用的可能比较少,但是说到Spring的Interceptor、各种各样的事务管理,大家会更熟悉一些,没错,这些在底层实现上,都是使用的动态代理,确切的说,想要为一个类的方法,动态添加功能,比如验证、资源释放、日志处理等,大部分都是借助动态代理。

    为了平缓的过渡,先来说一下静态代理。

静态代理

    静态代理的思路很简单:把一个真实对象的实例放到代理对象的实例中,然后调用代理对象方法,代理对象的方法调用真实对象的方法,以事务管理为例,如下:

    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动态代理的源码介绍的,写着写着就写成介绍代理都有哪些类型及实现方式了,再写篇幅就有点长了,所以放到下篇博客说明。




深入剖析动态代理(上)之代理的方式