zoukankan      html  css  js  c++  java
  • 深入剖析动态代理(上)之代理的方式

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




  • 相关阅读:
    预处理与编译阶段
    联合体
    linux shell
    二维数组、字符数组、指针数组涉及字符串和具体元素问题
    二级指针的简单运用
    halcon算子翻译——get_image_type
    halcon算子翻译——get_image_time
    halcon算子翻译——get_image_size
    Halcon算子翻译——get_image_pointer3
    Halcon算子翻译——get_image_pointer1_rect
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/6752002.html
Copyright © 2011-2022 走看看