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

Posted jzssuanfa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入剖析动态代理(上)之代理的方式相关的知识,希望对你有一定的参考价值。

    关于动态代理,大家显式使用的可能比較少,可是说到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动态代理的源代码介绍的。写着写着就写成介绍代理都有哪些类型及实现方式了,再写篇幅就有点长了。所以放到下篇博客说明。




以上是关于深入剖析动态代理(上)之代理的方式的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出Java动态代理

Java深入浅出系列——深入剖析动态代理--从静态代理到动态代理的演化

JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析

SpringCloud原理:OpenFeign之FeignClient动态代理生成原理

Retrofit 源码剖析-深入

Retrofit 源码剖析-深入