代理模式 订阅
代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。 展开全文
代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。
信息
示    例
引用计数
适用范围
程序设计
组成角色
抽象角色、代理角色、真实角色
中文名
代理模式
性    质
一种设计模式
外文名
Proxy Pattern
代理模式简介
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。著名的代理模式例子为引用计数(英语:reference counting)指针对象。当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。 [1] 
收起全文
精华内容
下载资源
问答
  • 代理模式

    千次阅读 2019-05-08 15:13:26
    代理模式的应用场景 在实际生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日只监听等,这些都是代理模式的实际体现。代理模式的定义也非常简单,是指为其它对象...

    代理模式的应用场景
    在实际生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日只监听等,这些都是代理模式的实际体现。代理模式的定义也非常简单,是指为其它对象提供一种代理,以控制对这个对象的访问。
    代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构性设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象,下面是代理模式的结构类图:
    在这里插入图片描述
    Subject是顶层接口,RealSubject是真实对象,Proxy是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是在代理对象前后增加一些处理。在代码中,我们想到代理,就会理解为是代码的增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构性模式,有静态代理和动态代理。

    静态代理
    举个例子:人到了试婚年龄,父母总是迫不及待的开始到处为自己的子女相亲。这个相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
    顶层接口Person:

    public interface Person {
    	public void findLover();
    }
    

    儿子要找对象,实现Son 类:

    public class Son implements Person {
    	public void findLover() {
    		System.out.println("儿子要求:xxxxxx");
    	}
    }
    

    父亲要帮儿子相亲,实现Father 类:

    public class Father {
    	private Son son = null;
    	public Father(Son son) {
    		this.son = son;
    	}
    	public void findLover() {
    		System.out.println("父母物色对象");
    		this.son.findLover();
    		System.out.println("双方同意");
    	}
    }
    

    来看测试代码:

    public class FatherTest {
    
    	public static void main(String[] args) {
    		Father father = new Father(new Son());
    		father.findLover();
    	}
    }
    

    运行结果:

    父母物色对象
    儿子要求:xxxxxx
    双方同意
    

    这里小伙伴们可能会觉得还是不知道如何讲代理模式应用到业务场景中,那么我们再来举例一个实际的业务场景。在分布式业务场景中,我们通常会对数据库进行分库分表,分库分表之后使用Java 操作时,就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。先创建Order 订单实体:

    public class Order {
    	private Object orderInfo;
    	private Long createTime;
    	private String id;
    	public Object getOrderInfo() {
    		return orderInfo;
    	}
    	public void setOrderInfo(Object orderInfo) {
    		this.orderInfo = orderInfo;
    	}
    	public Long getCreateTime() {
    		return createTime;
    	}
    	public void setCreateTime(Long createTime) {
    		this.createTime = createTime;
    	}
    	public String getId() {
    		return id;
    	}
    	public void setId(String id) {
    		this.id = id;
    	}
    }
    

    创建OrderDao 持久层操作类:

    public class OrderDao {
    	public int insert(Order order) {
    		System.out.println("OrderDao创建Order成功");
    		return 1;
    	}
    }
    

    创建OrderService 接口:

    public interface OrderService {
    	int createOrder(Order order);
    }
    

    创建OrderService 实现类:

    public class OrderServiceBean implements OrderService {
    	private OrderDao orderDao;
    	public OrderServiceBean() {
    		// 如果使用Spring应该是自动注入
    		// 我们为了测试方便,在构造方法中将orderDao直接初始化了
    		this.orderDao = new OrderDao();
    	}
    	
    	public int createOrder(Order order) {
    		System.out.println("OrderService调用OrderDao创建Order");
    		return orderDao.insert(order);
    	}
    }
    

    接下来是用静态代理,主要完成的功能是根据订单创建时间自动按年份进行分开。根据开闭原则,原来写好的逻辑我们不去修改,通过代理对象来完成。先创建数据源路由对象,我们使用ThreadLocal的单例实现,DynamicDataSourceEntry类:

    public class DynamicDataSourceEntry {
    	private final static String DEFAULT = null;
    	private final static ThreadLocal<String> local = new ThreadLocal<String>();
    	private DynamicDataSourceEntry() {}
    	// 清空数据源
    	public static void clear() {
    		local.remove();
    	}
    	// 获取当前正在使用的数据源的名字
    	public static String get() {
    		return local.get();
    	}
    	// 还原当前切面的数据源
    	public static void reset() {
    		local.set(DEFAULT);
    	}
    	// 设置数据源的名字
    	public static void set(String source) {
    		local.set(source);
    	}
    	// 根据年份动态设置数据源
    	public static void set(int year) {
    		local.set("DB_" + year);
    	}
    }
    

    创建切换数据源的静态代理OrderServiceStaticProxy:

    public class OrderServiceStaticProxy implements OrderService {
    	private OrderService orderService;
    	public OrderServiceStaticProxy(OrderService orderService) {
    		this.orderService = orderService;
    	}
    	public int createOrder(Order order) {
    		before();
    		Long createTime = order.getCreateTime();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
    		Integer routeNumber = Integer.valueOf(sdf.format(new Date(createTime)));
    		System.out.println("静态代理自动分配到 DB_" + routeNumber + " 数据源处理数据");
    		DynamicDataSourceEntry.set(routeNumber);
    		this.orderService.createOrder(order);
    		after();
    		return 0;
    	}
    	private void after() {
    		System.out.println("Proxy after method");
    	}
    	private void before() {
    		System.out.println("Proxy before method");
    	}
    }
    

    测试代码:

    public class OrderServiceStaticProxyTest {
    
    	public static void main(String[] args) throws Exception {
    		Order order = new Order();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    		Date date = sdf.parse("2017/02/02");
    		order.setCreateTime(date.getTime());
    		OrderService orderService = new OrderServiceStaticProxy(new OrderServiceBean());
    		orderService.createOrder(order);
    	}
    }
    

    运行结果:

    Proxy before method
    静态代理自动分配到 DB_2017 数据源处理数据
    OrderService调用OrderDao创建Order
    OrderDao创建Order成功
    Proxy after method
    

    符合我们预期的效果,我们再来回归一下类图,看看是不是和我们最开始画的类图一致:
    在这里插入图片描述

    动态代理
    动态代理和静态代理基本思路是一致的,只不过动态代理功能更强大,随着业务的扩展适应性更强。如果还以相亲为例,使用动态代理相当于是能够适应复杂的业务场景,不仅仅是父亲给儿子相亲,如果相亲这一业务发展成了一个产业,进而出现了媒婆、婚介所等这样的形式。那么,此时用的静态代理成本就更大了,需要一个更加通用的解决方案,要满足任何单身人士相亲的需求。我们现在升级一下之前的代码,先看用JDK方式实现:
    JDK方式
    创建媒婆JDKMeipo类:

    public class JDKMeiPo implements InvocationHandler {
    	private Object target;
    	public Object getInstance(Object obj) {
    		this.target = obj;
    		Class<?> clazz = this.target.getClass();
    		return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    	}
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		before();
    		Object result = method.invoke(this.target, args);
    		after();
    		return result;
    	}
    	private void after() {
    		System.out.println("物色成功");
    	}
    	private void before() {
    		System.out.println("我是媒婆,以拿到需求");
    		System.out.println("开始物色人选");
    	}
    }
    

    创建单身客户Customer类:

    public class Customer implements Person {
    	public void findLover() {
    		System.out.println("Customer 相亲对象要求:XXXXXXX");
    	}
    }
    

    测试代码:

    public class JDKMeiPoTest {
    	public static void main(String[] args) throws Exception {
    		Person person = (Person) new JDKMeiPo().getInstance(new Customer());
    		person.findLover();
    	}
    }
    

    运行结果:

    我是媒婆,以拿到需求
    开始物色人选
    Customer 相亲对象要求:XXXXXXX
    物色成功
    

    上面这个例子理解了的话,我们再看看数据源动态路由的业务,创建动态代理的类:

    public class OrderServiceDynamicProxy implements InvocationHandler {
    	private Object target;
    	public Object getInstance(Object obj) {
    		this.target = obj;
    		Class<?> clazz = this.target.getClass();
    		return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    	}
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		before(args[0]);
    		Object obj = method.invoke(this.target, args);
    		after();
    		return obj;
    	}
    	private void before(Object object) {
    		try {
    			System.out.println("proxy before method");
    			Long createTime = (Long) object.getClass().getMethod("getCreateTime").invoke(object, new Object[] {});
    			SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
    			Integer routeNumber = Integer.valueOf(sdf.format(new Date(createTime)));
    			System.out.println("动态代理类自动分配到 DB_" + routeNumber);
    			DynamicDataSourceEntry.set(routeNumber);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	private void after() {
    		System.out.println("proxy after method");
    		DynamicDataSourceEntry.reset();
    	}
    }
    

    测试类代码:

    public class OrderServiceDynamicProxyTest {
    	public static void main(String[] args) throws ParseException {
    		Order order = new Order();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    		Date date = sdf.parse("2017/02/02");
    		order.setCreateTime(date.getTime());
    		OrderService orderService = (OrderService) new OrderServiceDynamicProxy().getInstance(new OrderServiceBean());
    		orderService.createOrder(order);
    	}
    }
    

    运行结果:

    proxy before method
    动态代理类自动分配到 DB_2017
    OrderService调用OrderDao创建Order
    OrderDao创建Order成功
    proxy after method
    

    依然能够达到相同的效果,但是使用动态代理后,我们不仅能够实现Order类的动态路由,还可以实现其它任何类的数据源动态路由。当然,有比较重要的约定,必须要求实现getCreateTime()方法,因为路由规则是根据时间来运算的。
    JDK Proxy功能这么强大,它是如何实现的呢?我们知道JDK Proxy采用字节码重组,重新生成新的对象来替代原始的对象以达到动态代理的目的,JDK Proxy生成对象的步骤如下:

    1. 拿到被代理对象的引用,通过反射获取到它的所有的接口
    2. JDK Proxy重新生成一个新的类,同时新的类要实现被代理类的所有接口方法
    3. 动态生成Java字节码,把新加的业务逻辑方法由一定的逻辑代码去调用
    4. 编译新生成的Java代码.class
    5. 重新加载到JVM中运行
      以上这个过程就叫做字节码重组,JDK中有一个规范,在classpath下只要是$开头的class文件一般都是自动生成的。那么我们有没有办法看到替代后的对象的真实内容呢?做这样一个测试,我们把内存中的对象字节码通过文件流输出到一个新的class文件,然后利用反编译工具查看class的源代码:
    public class JDKMeiPoTest {
    	public static void main(String[] args) throws Exception {
    		Person person = (Person) new JDKMeiPo().getInstance(new Customer());
    		person.findLover();
    	
    		byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
    		FileOutputStream os = new FileOutputStream("E://sourceCode//$Proxy0.class");
    		os.write(bytes);
    		os.close();
    	}
    }
    

    运行之后我们能在E盘找到一个 P r o x y 0. c l a s s 文 件 , 使 用 J a d 反 编 译 得 到 Proxy0.class文件,使用Jad反编译得到 Proxy0.class使JadProxy0.jad文件,打开可以看到下面内容:

    // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
    // Jad home page: http://www.kpdus.com/jad.html
    // Decompiler options: packimports(3) 
    
    import com.yrk.designpattern.proxypattern.common.Person;
    import java.lang.reflect.*;
    
    public final class $Proxy0 extends Proxy
        implements Person
    {
    
        public $Proxy0(InvocationHandler invocationhandler)
        {
            super(invocationhandler);
        }
    
        public final boolean equals(Object obj)
        {
            try
            {
                return ((Boolean)super.h.invoke(this, m1, new Object[] {
                    obj
                })).booleanValue();
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final void findLover()
        {
            try
            {
                super.h.invoke(this, m3, null);
                return;
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final String toString()
        {
            try
            {
                return (String)super.h.invoke(this, m2, null);
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final int hashCode()
        {
            try
            {
                return ((Integer)super.h.invoke(this, m0, null)).intValue();
            }
            catch(Error _ex) { }
            catch(Throwable throwable)
            {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        static 
        {
            try
            {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                    Class.forName("java.lang.Object")
                });
                m3 = Class.forName("com.yrk.designpattern.proxypattern.common.Person").getMethod("findLover", new Class[0]);
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            }
            catch(NoSuchMethodException nosuchmethodexception)
            {
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());
            }
            catch(ClassNotFoundException classnotfoundexception)
            {
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());
            }
        }
    }
    

    我们发现$Proxy0 继承了Proxy 类,同时还实现了我们的Person 接口,而且重写了findLove()等方法。而且在静态块中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,在重写的方法用反射调用目标对象的方法。小伙伴们此时一定在好奇,这些代码是哪里来的呢?其实是JDK 帮我们自动生成的。

    CGLib动态代理
    简单看一下CGLib代理类的使用,还是以媒婆为例,创建CGLibMeipo:

    public class CglibMeiPoProxy implements MethodInterceptor{
    	
    	public Object getInstance(Class<?> clazz) {
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(clazz);
    		enhancer.setCallback(this);
    		return enhancer.create();
    	}
    	private void before() {
    		System.out.println("我是媒婆,以拿到需求");
    		System.out.println("开始物色人选");
    	}
    	private void after() {
    		System.out.println("物色成功");
    	}
    	@Override
    	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
    		before();
    		Object o = arg3.invokeSuper(arg0, arg2);
    		after();
    		return o;
    	}
    }
    

    有个小细节,CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现的动态代理。
    测试类:

    public class CglibMeiPoProxyTest {
    
    	public static void main(String[] args) {
    		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
    				"E://sourceCode//cglib_proxy_class/");
    		Customer customer = (Customer) new CglibMeiPoProxy().getInstance(Customer.class);
    		customer.findLover();
    	}
    }
    

    CGLib 的实现原理又是怎样的呢?我们可以在测试代码中加上一句代码,将CGLib 代理后的class 写入到磁盘,然后,我们再反编译一探究竟:
    运行结果:

    CGLIB debugging enabled, writing to 'E://sourceCode//cglib_proxy_class/'
    我是媒婆,以拿到需求
    开始物色人选
    Customer 相亲对象要求:XXXXXXX
    物色成功
    

    重新执行代码,我们会发现在E://sourceCode//cglib_proxy_class 目录下多了三个class 文件,如图:
    在这里插入图片描述
    通过调试跟踪,我们发现Customer E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB85c3b6e2.class 就是CGLib生成的代理类,继承了Customer 类。反编译后代码是这样的:

    // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
    // Jad home page: http://www.kpdus.com/jad.html
    // Decompiler options: packimports(3) 
    // Source File Name:   <generated>
    
    package com.yrk.designpattern.proxypattern.common;
    
    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.*;
    
    // Referenced classes of package com.yrk.designpattern.proxypattern.common:
    //            Customer
    
    public class Customer$$EnhancerByCGLIB$$85c3b6e2 extends Customer
        implements Factory
    {
    
        static void CGLIB$STATICHOOK1()
        {
            Method amethod[];
            Method amethod1[];
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class class1 = Class.forName("com.yrk.designpattern.proxypattern.common.Customer$$EnhancerByCGLIB$$85c3b6e2");
            Class class2;
            amethod = ReflectUtils.findMethods(new String[] {
                "findLover", "()V"
            }, (class2 = Class.forName("com.yrk.designpattern.proxypattern.common.Customer")).getDeclaredMethods());
            Method[] _tmp = amethod;
            CGLIB$findLover$0$Method = amethod[0];
            CGLIB$findLover$0$Proxy = MethodProxy.create(class2, class1, "()V", "findLover", "CGLIB$findLover$0");
            amethod1 = ReflectUtils.findMethods(new String[] {
                "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"
            }, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());
            Method[] _tmp1 = amethod1;
            CGLIB$equals$1$Method = amethod1[0];
            CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
            CGLIB$toString$2$Method = amethod1[1];
            CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
            CGLIB$hashCode$3$Method = amethod1[2];
            CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");
            CGLIB$clone$4$Method = amethod1[3];
            CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        }
    
        final void CGLIB$findLover$0()
        {
            super.findLover();
        }
    
        public final void findLover()
        {
            CGLIB$CALLBACK_0;
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 37;
               goto _L3 _L4
    _L3:
            break MISSING_BLOCK_LABEL_21;
    _L4:
            break MISSING_BLOCK_LABEL_37;
            this;
            CGLIB$findLover$0$Method;
            CGLIB$emptyArgs;
            CGLIB$findLover$0$Proxy;
            intercept();
            return;
            super.findLover();
            return;
        }
    
        final boolean CGLIB$equals$1(Object obj)
        {
            return super.equals(obj);
        }
    
        public final boolean equals(Object obj)
        {
            CGLIB$CALLBACK_0;
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 57;
               goto _L3 _L4
    _L3:
            this;
            CGLIB$equals$1$Method;
            new Object[] {
                obj
            };
            CGLIB$equals$1$Proxy;
            intercept();
            JVM INSTR dup ;
            JVM INSTR ifnonnull 50;
               goto _L5 _L6
    _L5:
            JVM INSTR pop ;
            false;
              goto _L7
    _L6:
            (Boolean);
            booleanValue();
    _L7:
            return;
    _L4:
            return super.equals(obj);
        }
    
        final String CGLIB$toString$2()
        {
            return super.toString();
        }
    
        public final String toString()
        {
            CGLIB$CALLBACK_0;
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 40;
               goto _L3 _L4
    _L3:
            this;
            CGLIB$toString$2$Method;
            CGLIB$emptyArgs;
            CGLIB$toString$2$Proxy;
            intercept();
            (String);
            return;
    _L4:
            return super.toString();
        }
    
        final int CGLIB$hashCode$3()
        {
            return super.hashCode();
        }
    
        public final int hashCode()
        {
            CGLIB$CALLBACK_0;
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 52;
               goto _L3 _L4
    _L3:
            this;
            CGLIB$hashCode$3$Method;
            CGLIB$emptyArgs;
            CGLIB$hashCode$3$Proxy;
            intercept();
            JVM INSTR dup ;
            JVM INSTR ifnonnull 45;
               goto _L5 _L6
    _L5:
            JVM INSTR pop ;
            0;
              goto _L7
    _L6:
            (Number);
            intValue();
    _L7:
            return;
    _L4:
            return super.hashCode();
        }
    
        final Object CGLIB$clone$4()
            throws CloneNotSupportedException
        {
            return super.clone();
        }
    
        protected final Object clone()
            throws CloneNotSupportedException
        {
            CGLIB$CALLBACK_0;
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 37;
               goto _L3 _L4
    _L3:
            this;
            CGLIB$clone$4$Method;
            CGLIB$emptyArgs;
            CGLIB$clone$4$Proxy;
            intercept();
            return;
    _L4:
            return super.clone();
        }
    
        public static MethodProxy CGLIB$findMethodProxy(Signature signature)
        {
            String s = signature.toString();
            s;
            s.hashCode();
            JVM INSTR lookupswitch 5: default 120
        //                   -1700020978: 60
        //                   -508378822: 72
        //                   1826985398: 84
        //                   1913648695: 96
        //                   1984935277: 108;
               goto _L1 _L2 _L3 _L4 _L5 _L6
    _L2:
            "findLover()V";
            equals();
            JVM INSTR ifeq 121;
               goto _L7 _L8
    _L8:
            break MISSING_BLOCK_LABEL_121;
    _L7:
            return CGLIB$findLover$0$Proxy;
    _L3:
            "clone()Ljava/lang/Object;";
            equals();
            JVM INSTR ifeq 121;
               goto _L9 _L10
    _L10:
            break MISSING_BLOCK_LABEL_121;
    _L9:
            return CGLIB$clone$4$Proxy;
    _L4:
            "equals(Ljava/lang/Object;)Z";
            equals();
            JVM INSTR ifeq 121;
               goto _L11 _L12
    _L12:
            break MISSING_BLOCK_LABEL_121;
    _L11:
            return CGLIB$equals$1$Proxy;
    _L5:
            "toString()Ljava/lang/String;";
            equals();
            JVM INSTR ifeq 121;
               goto _L13 _L14
    _L14:
            break MISSING_BLOCK_LABEL_121;
    _L13:
            return CGLIB$toString$2$Proxy;
    _L6:
            "hashCode()I";
            equals();
            JVM INSTR ifeq 121;
               goto _L15 _L16
    _L16:
            break MISSING_BLOCK_LABEL_121;
    _L15:
            return CGLIB$hashCode$3$Proxy;
    _L1:
            JVM INSTR pop ;
            return null;
        }
    
        public static void CGLIB$SET_THREAD_CALLBACKS(Callback acallback[])
        {
            CGLIB$THREAD_CALLBACKS.set(acallback);
        }
    
        public static void CGLIB$SET_STATIC_CALLBACKS(Callback acallback[])
        {
            CGLIB$STATIC_CALLBACKS = acallback;
        }
    
        private static final void CGLIB$BIND_CALLBACKS(Object obj)
        {
            Customer$$EnhancerByCGLIB$$85c3b6e2 customer$$enhancerbycglib$$85c3b6e2 = (Customer$$EnhancerByCGLIB$$85c3b6e2)obj;
            if(customer$$enhancerbycglib$$85c3b6e2.CGLIB$BOUND) goto _L2; else goto _L1
    _L1:
            Object obj1;
            customer$$enhancerbycglib$$85c3b6e2.CGLIB$BOUND = true;
            obj1 = CGLIB$THREAD_CALLBACKS.get();
            obj1;
            if(obj1 != null) goto _L4; else goto _L3
    _L3:
            JVM INSTR pop ;
            CGLIB$STATIC_CALLBACKS;
            if(CGLIB$STATIC_CALLBACKS != null) goto _L4; else goto _L5
    _L5:
            JVM INSTR pop ;
              goto _L2
    _L4:
            (Callback[]);
            customer$$enhancerbycglib$$85c3b6e2;
            JVM INSTR swap ;
            0;
            JVM INSTR aaload ;
            (MethodInterceptor);
            CGLIB$CALLBACK_0;
    _L2:
        }
    
        public Object newInstance(Callback acallback[])
        {
            CGLIB$SET_THREAD_CALLBACKS(acallback);
            CGLIB$SET_THREAD_CALLBACKS(null);
            return new Customer$$EnhancerByCGLIB$$85c3b6e2();
        }
    
        public Object newInstance(Callback callback)
        {
            CGLIB$SET_THREAD_CALLBACKS(new Callback[] {
                callback
            });
            CGLIB$SET_THREAD_CALLBACKS(null);
            return new Customer$$EnhancerByCGLIB$$85c3b6e2();
        }
    
        public Object newInstance(Class aclass[], Object aobj[], Callback acallback[])
        {
            CGLIB$SET_THREAD_CALLBACKS(acallback);
            JVM INSTR new #2   <Class Customer$$EnhancerByCGLIB$$85c3b6e2>;
            JVM INSTR dup ;
            aclass;
            aclass.length;
            JVM INSTR tableswitch 0 0: default 35
        //                   0 28;
               goto _L1 _L2
    _L2:
            JVM INSTR pop ;
            Customer$$EnhancerByCGLIB$$85c3b6e2();
              goto _L3
    _L1:
            JVM INSTR pop ;
            throw new IllegalArgumentException("Constructor not found");
    _L3:
            CGLIB$SET_THREAD_CALLBACKS(null);
            return;
        }
    
        public Callback getCallback(int i)
        {
            CGLIB$BIND_CALLBACKS(this);
            this;
            i;
            JVM INSTR tableswitch 0 0: default 30
        //                   0 24;
               goto _L1 _L2
    _L2:
            CGLIB$CALLBACK_0;
              goto _L3
    _L1:
            JVM INSTR pop ;
            null;
    _L3:
            return;
        }
    
        public void setCallback(int i, Callback callback)
        {
            switch(i)
            {
            case 0: // '\0'
                CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
                break;
            }
        }
    
        public Callback[] getCallbacks()
        {
            CGLIB$BIND_CALLBACKS(this);
            this;
            return (new Callback[] {
                CGLIB$CALLBACK_0
            });
        }
    
        public void setCallbacks(Callback acallback[])
        {
            this;
            acallback;
            JVM INSTR dup2 ;
            0;
            JVM INSTR aaload ;
            (MethodInterceptor);
            CGLIB$CALLBACK_0;
        }
    
        private boolean CGLIB$BOUND;
        public static Object CGLIB$FACTORY_DATA;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback CGLIB$STATIC_CALLBACKS[];
        private MethodInterceptor CGLIB$CALLBACK_0;
        private static Object CGLIB$CALLBACK_FILTER;
        private static final Method CGLIB$findLover$0$Method;
        private static final MethodProxy CGLIB$findLover$0$Proxy;
        private static final Object CGLIB$emptyArgs[];
        private static final Method CGLIB$equals$1$Method;
        private static final MethodProxy CGLIB$equals$1$Proxy;
        private static final Method CGLIB$toString$2$Method;
        private static final MethodProxy CGLIB$toString$2$Proxy;
        private static final Method CGLIB$hashCode$3$Method;
        private static final MethodProxy CGLIB$hashCode$3$Proxy;
        private static final Method CGLIB$clone$4$Method;
        private static final MethodProxy CGLIB$clone$4$Proxy;
    
        static 
        {
            CGLIB$STATICHOOK1();
        }
    
        public Customer$$EnhancerByCGLIB$$85c3b6e2()
        {
            CGLIB$BIND_CALLBACKS(this);
        }
    }
    

    重写了Customer 类的所有方法。我们通过代理类的源码可以看到,代理类会获得所有在父类继承来的方法, 并且会有MethodProxy 与之对应, 比如MethodCGLIB$findLove 0 0 0Method、MethodProxy CGLIB$findLove 0 0 0Proxy;这些方法在代
    理类的findLove()中都有调用。

    final void CGLIB$findLover$0()
        {
            super.findLover();
        }
    
        public final void findLover()
        {
            CGLIB$CALLBACK_0;
            if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
    _L1:
            JVM INSTR pop ;
            CGLIB$BIND_CALLBACKS(this);
            CGLIB$CALLBACK_0;
    _L2:
            JVM INSTR dup ;
            JVM INSTR ifnull 37;
               goto _L3 _L4
    _L3:
            break MISSING_BLOCK_LABEL_21;
    _L4:
            break MISSING_BLOCK_LABEL_37;
            this;
            CGLIB$findLover$0$Method;
            CGLIB$emptyArgs;
            CGLIB$findLover$0$Proxy;
            intercept();
            return;
            super.findLover();
            return;
        }
    

    调用过程: 代理对象调用this.findLove() 方法-> 调用拦截器->methodProxy.invokeSuper->CGLIB$findLove$0->被代理对象findLove()方法。此时,我们发现拦截器MethodInterceptor 中就是由MethodProxy 的invokeSuper方法调用代理方法的,MethodProxy 非常关键,我们分析一下它具体做了什么。

    public class MethodProxy {
        private Signature sig1;
        private Signature sig2;
        private CreateInfo createInfo;
        
        private final Object initLock = new Object();
        private volatile FastClassInfo fastClassInfo;
        
        /**
         * For internal use by {@link Enhancer} only; see the {@link net.sf.cglib.reflect.FastMethod} class
         * for similar functionality.
         */
        public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
            MethodProxy proxy = new MethodProxy();
            proxy.sig1 = new Signature(name1, desc);
            proxy.sig2 = new Signature(name2, desc);
            proxy.createInfo = new CreateInfo(c1, c2);
            return proxy;
        }
    
        private void init()
        {
            /* 
             * Using a volatile invariant allows us to initialize the FastClass and
             * method index pairs atomically.
             * 
             * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this 
             * code could allow fastClassInfo to be instantiated more than once, which
             * appears to be benign.
             */
            if (fastClassInfo == null)
            {
                synchronized (initLock)
                {
                    if (fastClassInfo == null)
                    {
                        CreateInfo ci = createInfo;
    
                        FastClassInfo fci = new FastClassInfo();
                        fci.f1 = helper(ci, ci.c1);
                        fci.f2 = helper(ci, ci.c2);
                        fci.i1 = fci.f1.getIndex(sig1);
                        fci.i2 = fci.f2.getIndex(sig2);
                        fastClassInfo = fci;
                        createInfo = null;
                    }
                }
            }
        }
    
        private static class FastClassInfo
        {
            FastClass f1;
            FastClass f2;
            int i1;
            int i2;
        }
    
        private static class CreateInfo
        {
            Class c1;
            Class c2;
            NamingPolicy namingPolicy;
            GeneratorStrategy strategy;
            boolean attemptLoad;
            
            public CreateInfo(Class c1, Class c2)
            {
                this.c1 = c1;
                this.c2 = c2;
                AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
                if (fromEnhancer != null) {
                    namingPolicy = fromEnhancer.getNamingPolicy();
                    strategy = fromEnhancer.getStrategy();
                    attemptLoad = fromEnhancer.getAttemptLoad();
                }
            }
        }
    
        private static FastClass helper(CreateInfo ci, Class type) {
            FastClass.Generator g = new FastClass.Generator();
            g.setType(type);
            g.setClassLoader(ci.c2.getClassLoader());
            g.setNamingPolicy(ci.namingPolicy);
            g.setStrategy(ci.strategy);
            g.setAttemptLoad(ci.attemptLoad);
            return g.create();
        }
    
        private MethodProxy() {
        }
    
        /**
         * Return the signature of the proxied method.
         */
        public Signature getSignature() {
            return sig1;
        }
    
        /**
         * Return the name of the synthetic method created by CGLIB which is
         * used by {@link #invokeSuper} to invoke the superclass
         * (non-intercepted) method implementation. The parameter types are
         * the same as the proxied method.
         */
        public String getSuperName() {
            return sig2.getName();
        }
    
        /**
         * Return the {@link net.sf.cglib.reflect.FastClass} method index
         * for the method used by {@link #invokeSuper}. This index uniquely
         * identifies the method within the generated proxy, and therefore
         * can be useful to reference external metadata.
         * @see #getSuperName
         */
        public int getSuperIndex() {
            init();
            return fastClassInfo.i2;
        }
    
        // For testing
        FastClass getFastClass() {
          init();
          return fastClassInfo.f1;
        }
    
        // For testing
        FastClass getSuperFastClass() {
          init();
          return fastClassInfo.f2;
        }
    
        /**
         * Return the <code>MethodProxy</code> used when intercepting the method
         * matching the given signature.
         * @param type the class generated by Enhancer
         * @param sig the signature to match
         * @return the MethodProxy instance, or null if no applicable matching method is found
         * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
         */
        public static MethodProxy find(Class type, Signature sig) {
            try {
                Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
                                                  MethodInterceptorGenerator.FIND_PROXY_TYPES);
                return (MethodProxy)m.invoke(null, new Object[]{ sig });
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
            } catch (IllegalAccessException e) {
                throw new CodeGenerationException(e);
            } catch (InvocationTargetException e) {
                throw new CodeGenerationException(e);
            }
        }
    
        /**
         * Invoke the original method, on a different object of the same type.
         * @param obj the compatible object; recursion will result if you use the object passed as the first
         * argument to the MethodInterceptor (usually not what you want)
         * @param args the arguments passed to the intercepted method; you may substitute a different
         * argument array as long as the types are compatible
         * @see MethodInterceptor#intercept
         * @throws Throwable the bare exceptions thrown by the called method are passed through
         * without wrapping in an <code>InvocationTargetException</code>
         */
        public Object invoke(Object obj, Object[] args) throws Throwable {
            try {
                init();
                FastClassInfo fci = fastClassInfo;
                return fci.f1.invoke(fci.i1, obj, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            } catch (IllegalArgumentException e) {
                if (fastClassInfo.i1 < 0)
                    throw new IllegalArgumentException("Protected method: " + sig1);
                throw e;
            }
        }
    
        /**
         * Invoke the original (super) method on the specified object.
         * @param obj the enhanced object, must be the object passed as the first
         * argument to the MethodInterceptor
         * @param args the arguments passed to the intercepted method; you may substitute a different
         * argument array as long as the types are compatible
         * @see MethodInterceptor#intercept
         * @throws Throwable the bare exceptions thrown by the called method are passed through
         * without wrapping in an <code>InvocationTargetException</code>
         */
        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                init();
                FastClassInfo fci = fastClassInfo;
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    }
    

    上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。还记得之前生成三个class 文件吗?
    Customer$$EnhancerByCGLIB$$85c3b6e2$$FastClassByCGLIB$$59eda6fa.class 就是代理类的FastClass,Customer$$FastClassByCGLIB$$9243bf7a.class 就是被代理类的FastClass。CGLib 动态代理执行代理方法效率之所以比JDK 的高是因为Cglib 采用了FastClass 机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class 会为代理类或被代理类的方法分配一个index(int 类型)。这个index 当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

    CGLib 和JDK 动态代理对比

    1. JDK 动态代理是实现了被代理对象的接口,CGLib是集成了被代理对象
    2. JDK和CGLib都是在运行期间生成字节码,JDK是直接写Class字节码,CGLib使用ASM框架写Class字节码,CGLib实现更复杂,生成代理类比JDK效率低
    3. JDK调用代理方法是通过反射调用,CGLib是通过FastCalss机制直接调用,CGLib执行效率更高

    代理模式在Spring中的应用
    Spring利用动态代理实现AOP有两个非常重要的类,一个是JdkDynamicAopProxy,一个是CglibAopProxy。来看一下类图:
    在这里插入图片描述
    Spring中代理选择的原则:

    1. 当Bean有实现接口时,Spring就会用JDK动态代理
    2. 当Bean没有实现接口,Spring选择CGLib
    3. Spring可以通过配置强制使用CGLib,只需要在Spring中加入下面代码:
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    

    静态代理和动态代理的区别

    1. 静态代理只能通过手动完成代理操作,如果被代理类增加新方法,代理类需要同步新增方法,违背开闭原则
    2. 动态代理采用运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则
    3. 若动态代理需要对目标类的增强逻辑扩展,结合策略模式,只需新增策略类便可完成,无需修改代理类代码

    代理模式优缺点
    使用代理模式有以下优点:

    1. 代理模式能将代理对象与真实被调用的目标对象隔离
    2. 一定程度上降低了系统的耦合度,扩展性好
    3. 可以起到保护目标对象的作用
    4. 可以对目标对象的功能增强

    当然,代理模式也有缺点:

    1. 代理模式会造成系统设计中类的数量增加
    2. 在客户端与目标对象之间增加一个代理对象,会造成请求处理速度变慢
    3. 增加了系统的复杂度
    展开全文
  • 轻松学,Java 中的代理模式及动态代理

    万次阅读 多人点赞 2017-06-29 22:08:55
    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。...

    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。

    我们先来分析代理这个词。

    代理

    代理是英文 Proxy 翻译过来的。我们在生活中见到过的代理,大概最常见的就是朋友圈中卖面膜的同学了。

    她们从厂家拿货,然后在朋友圈中宣传,然后卖给熟人。

    这里写图片描述

    按理说,顾客可以直接从厂家购买产品,但是现实生活中,很少有这样的销售模式。一般都是厂家委托给代理商进行销售,顾客跟代理商打交道,而不直接与产品实际生产者进行关联。

    所以,代理就有一种中间人的味道。

    接下来,我们说说软件中的代理模式。

    代理模式

    代理模式是面向对象编程中比较常见的设计模式。
    这里写图片描述

    这是常见代理模式常见的 UML 示意图。

    需要注意的有下面几点:

    1. 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
    展开全文
  • 代理模式看这一篇就够了~

    千次阅读 多人点赞 2020-08-07 22:16:58
    不知各位是否还记得这两篇文章APP启动流程解析和Android Hook告诉你 如何启动未注册的Activity,这两篇文章中使用的技术基础都包含了 代理模式,其中在文章中也说道 “说到代理其实就是代理模式,关于什么是代理模式...

    前言

    不知各位是否还记得这两篇文章APP启动流程解析 和 Android Hook告诉你 如何启动未注册的Activity,这两篇文章中使用的技术基础都包含了 代理模式,其中在文章中也说道  “说到代理其实就是代理模式,关于什么是代理模式以及动态代理和静态代理的使用可以持续关注我,后面会单独写篇文章进行介绍。” 

    如今整整一年过去了,我还是曾经那个少年,没有一丝丝改变。  这篇文章来了~

    什么是代理模式

     说到设计模式,离我们特别远,又特别近。

     问许多工程师,设计模式用过哪些,相信很多人都会说 单例模式、工厂模式等等,但是很少有人提及桥接模式、门面模式、解释器模式等 甚至都没有听说过。

     代理模式 是 结构型模式之一,主要是将类和对象结合在一起解决特定的应用场景问题。对于Android工程师来说,我觉得了解并掌握代理模式是必要的,因为了解Android Hook、AMS代理等插件化技术,是离不开代理模式的,这也是我一直觉得要有这篇文章的原因,如果你还不了解代理模式对Android开发者有什么用途,可移步至前言的两篇文章。

    使用代理模式

    代理模式简单的说就是可以在不改被代理类代码的情况下,通过引入代理类来扩展功能。比如我们现在有一个登录注册类LoginAndRegist.java  和一个登录方法 一个注册方法。

    public void login(String userName){
            System.out.println("我是登录方法");
        }
    
        public void reist(String userName){
            System.out.println("我是注册方法");
        }
    

    同时我们新建一个Test类来测试方法

    public class Test {
    
        public static void main(String[] args) {
            LoginAndRegist loginAndRegist = new LoginAndRegist();
            loginAndRegist.reist("huanglinqing");
            loginAndRegist.login("huanglinqing");
        }
    }
    

    运行Test.main 打印如下所示:

    我是注册方法
    我是登录方法
    
    Process finished with exit code 0
    

    那么我们现在有需求,为登录和注册添加相关日志,我们该怎么来实现呢,你可能说了,这不简单吗,直接在login 和 regist方法中 直接再加两行打印不就行了吗?

    可以是可以 但是随着系统的庞大,你会越来越痛苦

    第一  LoginAndRegist类不是你写的,难道要让各自负责人去修改自己的代码吗 

    第二 添加日志 是一个日志系统 是一个独立的系统,不应该和业务掺杂在一起

    第三 如果要添加日志的类 是jar包中的呢

    第******

    静态代理

    那么我们该如何实现上面的功能呢,我们以 为登录注册方法 添加 时间日志为例,首先 有经验的工程师在写代码的时候,就应该知道,我们要遵循基于接口而非实现的设计原则,所以我们应把login 和 regist 抽取出来,让LoginAndRegist 类 继承盖接口,如下所示:

    public interface UserInter {
    
        /**
         * 登录
         * @param name name
         */
        void login(String name);
    
        /**
         * 注册
         * @param name name
         */
        void regist(String name);
    }
    
    public class LoginAndRegist implements UserInter {
    
        private static final String TAG = "Login";
    
        @Override
        public void login(String userName) {
            System.out.println("我是登录方法");
        }
    
        @Override
        public void regist(String name) {
            System.out.println("我是注册方法");
        }
    
    }
    

    为LoginAndRegist 类 创建代理类 LoginAndRegistProxy,让代理类 实现 和 原始类同样的接口,并调用原始类的方法 ,并在调用前 打印当前时间戳,代码如下所示:

    public class LoginAndRegistProxy implements UserInter {
    
        private static final String TAG = "Login";
    
        private UserInter userInter;
    
        public LoginAndRegistProxy(UserInter userInter) {
            this.userInter = userInter;
        }
    
        @Override
        public void login(String userName) {
            System.out.println("调用登录接口的时间:" + System.currentTimeMillis());
            userInter.login(userName);
        }
    
        @Override
        public void regist(String name) {
            System.out.println("调用注册接口的时间:" + System.currentTimeMillis());
            userInter.login(name);
        }
    
    }
    

    在Test中修改调用方法为代理类调用:

     public static void main(String[] args) {
            LoginAndRegist loginAndRegist = new LoginAndRegist();
            LoginAndRegistProxy loginAndRegistProxy = new LoginAndRegistProxy(loginAndRegist);
            loginAndRegistProxy.regist("huanglinqing");
            loginAndRegistProxy.login("huanglinqing");
        }
    

    执行结果 如下所示:

    调用注册接口的时间:1596793891141
    我是登录方法
    调用登录接口的时间:1596793891141
    我是登录方法
    
    Process finished with exit code 0
    

    这样呢,我们就实现了不改变原始的情况下,为类方法添加日志的功能,但是呢,这种方法存在的问题 我们上面也提到了

    第一 如果原始类 没有实现接口怎么办

    第二 如果原始类的源代码 我们获取不到怎么办

    对于原始类 并没有实现接口,并且我们无法修改的情况下,这种我们称为对外部类的扩展,外部类的扩展我们一般使用继承的方式去扩展,这种方式我们就不解释了。

    第三 如果为每个类都添加代理类,会增加大量的文件

    第三个问题才是我们实际开发中需要首要解决的问题,所以 为了解决静态代理文件过多的的问题,我们需要使用动态代理。

    动态代理

    动态代理简单的说 就是我们不需要事先为某个原始类编写代理类,而是在运行的时候,动态的创建代理类,然后将原始类替换为代理类。动态代理的魅力在Android中真的是非常非常大,如果你还不了解,一定要回头看我前言中提到的两篇文章。而在java中动态代理的基础是反射,如果你还不了解反射技术,请移步至我的这篇文章Java反射技术详解

    动态代理,我们主要依赖的是newProxyInstance方法,该方法返回的是指定接口代理类的实例。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    

    第一个参数loader 指的是目标对象class对应的classLoader

    第二个参数interfaces是设置为对象class所实现的接口类型,第一个参数和第二个参数其实在业务上都是固定的,在这里就是UserInter对应的的classLoader和接口类型。

    我们主要来看第三个参数 InvocationHandler,它是一个实现了InvocationHandler接口的类对象

    首先我们来定义一个MyInvocationHandler实现InvocationHandler

    public class MyInvocationHandler implements java.lang.reflect.InvocationHandler {
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           return null;
        }
    }
    

      这里实现的invoke方法就是动态代理的核心,此外我们需要将代理类传进来,并在invoke方法中执行代理方法

    public class MyInvocationHandler implements java.lang.reflect.InvocationHandler {
    
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("开始执行前:" + method.getName());
            Object object = method.invoke(target, args);
            System.out.println("执行结束:" + method.getName());
            return object;
        }
    }
    

    当外部代理类调用某个方法的时候,其实就是在调用invoke中的method.invoke方法,args就是执行对应方法所需的参数,我们这里 在方法前后分别加上日志。

    上面我们已经说了,动态代理是通过newProxyInstance方法创建的,我们来看Test中如何使用

      public static void main(String[] args) {
            UserInter loginAndRegist = new LoginAndRegist();
            UserInter loginAndRegistProxy = (UserInter) Proxy.newProxyInstance(loginAndRegist.getClass().getClassLoader(),
                    loginAndRegist.getClass().getInterfaces(),new MyInvocationHandler(loginAndRegist));
            loginAndRegistProxy.regist("huang_动态代理");
            loginAndRegistProxy.login("huang_静态代理");
        }
    

    newProxyInstance中的参数我们在上面已经说明了,运行结果如下所示:

    开始执行前:regist
    我是注册方法
    执行结束:regist
    开始执行前:login
    我是登录方法
    执行结束:login
    
    Process finished with exit code 0
    

    如此我们就通过动态代理,给所有类的方法 统一添加日志了。

    在Android中我们用Proxy.newProxyInstance生成的对象,直接替换掉原来的对象,这个技术就是听起来很高大上的Hook技术。

    此外Spring AOP 底层的实现原理就是基于动态代理。

    代理模式还有哪些应用场景

    如果我们想要很好的应用代理模式,我们需要了解代理模式的应用场景有哪些

    业务系统非功能性需求

    在业务系统中一些非功能性需求,比如:监控、统计、鉴权、事务、日志等。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,这样可以与业务解耦并且做到职责明确划分。

    除此之外代理模式还在RPC技术、Android Hook、插件化等技术领域有着广泛的应用。

    现在你是否对代理模式有清晰的了解了呢?

    展开全文
  • 代理模式的使用总结

    万次阅读 多人点赞 2020-04-20 14:14:37
    一、代理模式 二、静态代理 (一)静态代理 (二)静态代理简单实现 三、动态代理 (一)动态代理 (二)动态代理简单实现 四、动态代理原理分析 五、InvocationHandler接口和Proxy类详解 六、JDK动态代理...

    目录

    一、代理模式

    二、静态代理

    (一)静态代理

    (二)静态代理简单实现

    三、动态代理

    (一)动态代理

    (二)动态代理简单实现

    四、动态代理原理分析

    五、InvocationHandler接口和Proxy类详解

    六、JDK动态代理和CGLIB动态代理代码示例比较与总结

    (一)定义创建用户管理接口

    (二)用户管理实现类,实现用户管理接口(被代理的实现类)

    (三)采用JDK代理实现:JDK动态代理实现InvocationHandler接口

    (四)采用CGLIB代理实现:需要导入asm版本包,实现MethodInterceptor接口

    (五)客户端调用测试与结果

    (六)JDK和CGLIB动态代理总结

    参考书籍、文献和资料

    一、代理模式

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

    二、静态代理

    (一)静态代理

    静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

    (二)静态代理简单实现

    根据上面代理模式的类图,来写一个简单的静态代理的例子:假如一个班的同学要向老师交班费,但是都是通过班长把自己的钱转交给老师。这里,班长代理学生上交班费,班长就是学生的代理。

    • 1.确定创建接口具体行为

    首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

    /**
     * 创建Person接口
     */
    public interface Person {
        //上交班费
        void giveMoney();
    }
    • 2.被代理对象实现接口,完成具体的业务逻辑

    Student类实现Person接口。Student可以具体实施上交班费的动作:

    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
        
        @Override
        public void giveMoney() {
           System.out.println(name + "上交班费50元");
        }
    }
    • 3.代理类实现接口,完成委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

    StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象。由于实现了Peson接口,同时持有一个学生对象,那么他可以代理学生类对象执行上交班费(执行giveMoney()方法)行为。

    /**
     * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
     * @author Gonjan
     *
     */
    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
        
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
        
        //代理上交班费,调用被代理学生的上交班费行为
        public void giveMoney() {
            stu.giveMoney();
        }
    }
    • 4.客户端使用操作与分析
    public class StaticProxyTest {
        public static void main(String[] args) {
            //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
            Person zhangsan = new Student("张三");
            
            //生成代理对象,并将张三传给代理对象
            Person monitor = new StudentsProxy(zhangsan);
            
            //班长代理上交班费
            monitor.giveMoney();
        }
    }

    这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了,这就是代理模式。

    代理模式最主要的就是有一个公共接口(Person),一个具体的类(Student),一个代理类(StudentsProxy),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。就这个例子来说,加入班长在帮张三上交班费之前想要先反映一下张三最近学习有很大进步,通过代理模式很轻松就能办到:

    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
        
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
        
        //代理上交班费,调用被代理学生的上交班费行为
        public void giveMoney() {
            System.out.println("张三最近学习有进步!");
            stu.giveMoney();
        }
    }

    只需要在代理类中帮张三上交班费之前,执行其他操作就可以了。这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。其实也是切面思想的主要思路,这个后面会出一篇博客,并且举例怎么使用,想法是:对controller层出入参进行检验和必要的操作等,Spring源码里面也有许多这样的例子,后期有时间整理。

    三、动态代理

    (一)动态代理

    代理类在程序运行时创建的代理方式被成为动态代理

    我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法 比如说,想要在每个代理的方法前都加上一个处理方法:

        public void giveMoney() {
            //调用被代理方法前加入处理方法
            beforeMethod();
            stu.giveMoney();
        }

    这里只有一个giveMoney方法,就写一次beforeMethod方法,但是如果除了giveMonney还有很多其他的方法,那就需要写很多次beforeMethod方法,麻烦。所以建议使用动态代理实现。

    (二)动态代理简单实现

    在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口通过这个类和这个接口可以生成JDK动态代理类和动态代理对象

    • 1.确定创建接口具体行为

    首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

    /**
     * 创建Person接口
     */
    public interface Person {
        //上交班费
        void giveMoney();
    }
    • 2.被代理对象实现接口,完成具体的业务逻辑(此处增加一些方法用于检测后面使用动态代理用于区分)

    Student类实现Person接口。

    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
        
        @Override
        public void giveMoney() {
            try {
                //假设数钱花了一秒时间
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           System.out.println(name + "上交班费50元");
        }
    }

    再定义一个检测方法执行时间的工具类,在任何方法执行前先调用start方法,执行后调用finsh方法,就可以计算出该方法的运行时间,这也是一个最简单的方法执行时间检测工具。

    public class MonitorUtil {
        
        private static ThreadLocal<Long> tl = new ThreadLocal<>();
        
        public static void start() {
            tl.set(System.currentTimeMillis());
        }
        
        //结束时打印耗时
        public static void finish(String methodName) {
            long finishTime = System.currentTimeMillis();
            System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
        }
    }
    • 3.代理类实现接口,完成委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

    创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例targetInvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。在invoke方法中执行被代理对象target的相应方法。在代理过程中,我们在真正执行被代理对象的方法前加入自己其他处理。这也是Spring中的AOP实现的主要原理,这里还涉及到一个很重要的关于java反射方面的基础知识

    public class StuInvocationHandler<T> implements InvocationHandler {
        //invocationHandler持有的被代理对象
        T target;
        
        public StuInvocationHandler(T target) {
           this.target = target;
        }
        
        /**
         * proxy:代表动态代理对象
         * method:代表正在执行的方法
         * args:代表调用目标方法时传入的实参
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理执行" +method.getName() + "方法");  
            //代理过程中插入监测方法,计算该方法耗时
            MonitorUtil.start();
            Object result = method.invoke(target, args);
            MonitorUtil.finish(method.getName());
            return result;
        }
    }
    • 4.客户端使用操作与分析
    public class ProxyTest {
        public static void main(String[] args) {
            
            //创建一个实例对象,这个对象是被代理的对象
            Person zhangsan = new Student("张三");
            
            //创建一个与代理对象相关联的InvocationHandler
            InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
            
            //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
            Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
    
           //代理执行上交班费的方法
            stuProxy.giveMoney();
        }
    }

    创建了一个需要被代理的学生张三,将zhangsan对象传给了stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数了的,上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。

    动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。例如,这里的方法计时,所有的被代理对象执行的方法都会被计时,然而我只做了很少的代码量。

    动态代理的过程,代理对象和被代理对象的关系不像静态代理那样一目了然,清晰明了。因为动态代理的过程中,我们并没有实际看到代理类,也没有很清晰地的看到代理类的具体样子,而且动态代理中被代理对象和代理对象是通过InvocationHandler来完成的代理过程的,其中具体是怎样操作的,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行。带着这些问题,我们就需要对java动态代理的源码进行简要的分析,弄清楚其中缘由。

    四、动态代理原理分析

    我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                              InvocationHandler h) throws IllegalArgumentException {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }

    重点是这四处位置:

    final Class<?>[] intfs = interfaces.clone();
    Class<?> cl = getProxyClass0(loader, intfs);
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    return cons.newInstance(new Object[]{h});

    最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的,我们可以通过下面的方法将其打印到文件里面,一睹真容:

    byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",Student.class.getInterfaces());
    String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
    try(FileOutputStream fos = new FileOutputStream(path)) {
        fos.write(classFile);
        fos.flush();
        System.out.println("代理类class文件写入成功");
    } catch (Exception e) {
        System.out.println("写文件错误");
    }

    对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.Person;
    
    public final class $Proxy0 extends Proxy implements Person {
      private static Method m1;
      private static Method m2;
      private static Method m3;
      private static Method m0;
      
      /**
      *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
      *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
      *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
      *
      *super(paramInvocationHandler),是调用父类Proxy的构造方法。
      *父类持有:protected InvocationHandler h;
      *Proxy构造方法:
      *    protected Proxy(InvocationHandler h) {
      *         Objects.requireNonNull(h);
      *         this.h = h;
      *     }
      *
      */
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
      
      //这个静态块本来是在最后的,我把它拿到前面来,方便描述
       static
      {
        try
        {
          //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
     
      /**
      * 
      *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
      *this.h.invoke(this, m3, null);这里简单,明了。
      *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
      *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
      */
      public final void giveMoney()
        throws 
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
    
    }

    jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。

    我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用

    代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。

    总结:生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了Proxy类,所以也就决定了java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的

    五、InvocationHandler接口和Proxy类详解

    InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法
    看下官方文档对InvocationHandler接口的描述:

    {@code InvocationHandler} is the interface implemented by
         the <i>invocation handler</i> of a proxy instance.
         <p>Each proxy instance has an associated invocation handler.
         When a method is invoked on a proxy instance, the method
         invocation is encoded and dispatched to the {@code invoke}
         method of its invocation handler.

    当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用,看如下invoke方法:

        /**
        * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
        * method:我们所要调用某个对象真实的方法的Method对象
        * args:指代代理对象方法传递的参数
        */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

    Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

    public static Object newProxyInstance(ClassLoader loader, 
                                                Class<?>[] interfaces, 
                                                InvocationHandler h)

    这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

    • loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
    • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
    • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

    六、JDK动态代理和CGLIB动态代理代码示例比较与总结

    (一)定义创建用户管理接口

    /**
     * 用户管理接口
     */
    public interface UserManager {
        //新增用户抽象方法
        void addUser(String userName, String password);
        //删除用户抽象方法
        void delUser(String userName);
    }

    (二)用户管理实现类,实现用户管理接口(被代理的实现类)

    
    /**
     * 用户管理实现类,实现用户管理接口(被代理的实现类)
     */
    public class UserManagerImpl implements UserManager{
     
        //重写用户新增方法
        @Override
        public void addUser(String userName, String password) {
            System.out.println("调用了用户新增的方法!");
            System.out.println("传入参数:\nuserName = " + userName +", password = " + password);
        }
     
        //重写删除用户方法
        @Override
        public void delUser(String userName) {
            System.out.println("调用了删除的方法!");
            System.out.println("传入参数:\nuserName = "+userName);
        }
    }

    (三)采用JDK代理实现:JDK动态代理实现InvocationHandler接口

    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
     
    /**
     * JDK动态代理实现InvocationHandler接口
     */
    public class JdkProxy implements InvocationHandler {
     
        private Object targetObject;  //需要代理的目标对象
     
     
        //定义获取代理对象的方法(将目标对象传入进行代理)
        public Object getJDKProxy(Object targetObject){
            //为目标对象target赋值
            this.targetObject = targetObject;
            //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
            Object proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
            //返回代理对象
            return proxyObject;
        }
     
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("JDK动态代理,监听开始!");
            // 调用invoke方法,result存储该方法的返回值
            Object result = method.invoke(targetObject,args);
            System.out.println("JDK动态代理,监听结束!");
            return result;
        }
     
    //    public static void main(String[] args) {
    //        JdkProxy jdkProxy = new JdkProxy();  //实例化JDKProxy对象
    //        UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());   //获取代理对象
    //        user.addUser("admin","123456");
    //    }
    }
    

    (四)采用CGLIB代理实现:需要导入asm版本包,实现MethodInterceptor接口

    import com.proxy.UserManager;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
     
    import java.lang.reflect.Method;
     
    /**
     * Cglib动态代理:
     * (需要导入两个jar包,asm-5.0.3.jar,cglib-3.1.jar 版本可自行选择)
     */
     
    //Cglib动态代理,实现MethodInterceptor接口
    public class CglibProxy implements MethodInterceptor {
        private Object target;//需要代理的目标对象
     
        //重写拦截方法
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("Cglib动态代理,监听开始!");
            Object result = method.invoke(target,args);//方法执行参数:target 目标对象 arr参数数组
            System.out.println("Cglib动态代理,监听结束!");
            return result;
        }
     
        //定义获取代理对象的方法
        public UserManager getCglibProxy(Object targetObject) {
            this.target = targetObject;//为目标对象target赋值
            Enhancer enhancer = new Enhancer();
            //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
            enhancer.setSuperclass(targetObject.getClass()); //UserManagerImpl
            enhancer.setCallback(this);//设置回调
            Object result = enhancer.create();//创建并返回代理对象
            return (UserManager) result;
        }
     
     
    //    public static void main(String[] args) {
    //        CglibProxy cglibProxy = new CglibProxy(); //实例化CglibProxy对象
    //        UserManager user = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象
    //        user.delUser("admin");
    //    }
     
    }

    (五)客户端调用测试与结果

    import com.proxy.CglibProxy.CglibProxy;
    import com.proxy.JDKProxy.JdkProxy;
     
    public class ClientTest {
        public static void main(String[] args) {
     
            JdkProxy jdkProxy = new JdkProxy();  //实例化JDKProxy对象
            UserManager userJdk = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());   //获取代理对象
            userJdk.addUser("admin","123456");
     
            CglibProxy cglibProxy = new CglibProxy(); //实例化CglibProxy对象
            UserManager userCglib = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象
            userCglib.delUser("admin");
        }
    }

    运行结果:

    JDK动态代理,监听开始!
    调用了用户新增的方法!
    传入参数:
    userName = admin, password = 123456
    JDK动态代理,监听结束!
    Cglib动态代理,监听开始!
    调用了删除的方法!
    传入参数:
    userName = admin
    Cglib动态代理,监听结束!

    (六)JDK和CGLIB动态代理总结

    • JDK动态代理只能对实现了接口的类生成代理,而不能针对类 ,使用的是 Java反射技术实现,生成类的过程比较高效
    • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ,使用asm字节码框架实现,相关执行的过程比较高效,生成类的过程可以利用缓存弥补,因为是继承,所以该类或方法最好不要声明成final 
    • JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:实现InvocationHandler + 使用Proxy.newProxyInstance产生代理对象 + 被代理的对象必须要实现接口
    • CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,是一种继承但是针对接口编程的环境下推荐使用JDK的代理

    参考书籍、文献和资料

    1.https://blog.csdn.net/zxzzxzzxz123/article/details/69941910

    2.https://www.cnblogs.com/liubin1988/p/8909610.html

    3.https://blog.csdn.net/weixin_38327420/article/details/85068641  动态代理的举例

    4.https://www.cnblogs.com/wangenxian/p/10885309.html

    5.https://www.cnblogs.com/gonjan-blog/p/6685611.html.  主要学习思路来源

    展开全文
  • 详解设计模式之 - 代理模式

    千次阅读 2019-07-01 15:16:53
    什么是代理模式代理模式有什么作用? 代理模式 为真实对象提供一个代理,从而控制对真实对象的访问 代理模式的作用 通过代理,控制对对象的访问! 可以详细控制访问某个(某类)对象的方法,在调用这个方法前...
  • 面试中关于Spring AOP和代理模式的那些事

    万次阅读 多人点赞 2018-12-09 23:00:16
    我们知道,Spring 中 AOP 是一大核心技术,也是面试中经常会被问到的问题,最近我在网上也看到很多面试题,其中和 Spring AOP 相关的就有...谈谈你对代理模式的理解? 静态代理和动态代理有什么区别? 如何实现动态...
  • 常用设计模式总结(未完待续) 单例模式 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型...
  • 秒懂Java代理与动态代理模式

    万次阅读 多人点赞 2018-06-30 17:08:23
    什么是代理模式?解决什么问题(即为什么需要)?什么是静态代理?什么是动态代理模式?二者什么关系?具体如何实现?什么原理?如何改进?这即为我们学习一项新知识的正确打开方式,我们接下来会以此展开,让你秒懂...
  • 设计模式 – 代理模式

    千次阅读 2020-10-21 15:43:34
    在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式(Proxy Pattern)的定义也非常简单,是指为其他对象提供一...
  • 深入理解代理模式:静态代理与JDK动态代理

    万次阅读 多人点赞 2018-03-01 00:22:11
     代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其...
  • 代理模式——远程代理(一)

    千次阅读 2019-03-19 14:33:20
    代理模式定义 为另一个对象提供一个替身或占位符以控制对这个对象的访问。使用代理模式创建代表对象,让代表对象控制对某对象的访问,被代理的对象可是远程的对象、创建开销大的对象或需要安全控制的对象。 代理分...
  • 今天面试聊到了模式。 面试官问:装饰者模式了解吗,讲一下? 我:blabla... 面试官:那代理模式呢?...代理模式是一项基本的设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上也...
  • 代理模式是一种结构性设计模式。为对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,并允许在将请求提交给对象前后进行一些处理。 被代理的对象可以是远程对象、创建开销大的对象或需要安全...
  • 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是中介。 想象一下我们生活中购买火车票的情节,我们可以通过飞猪购买,也可以到窗口购买,飞猪就相当于代理模式,秒...
  • 为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。 好处:可以不用动原来类的逻辑,...
  • Java设计模式-之代理模式(动态代理)

    千次阅读 2017-09-10 15:47:17
    1、什么叫代理模式: &amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;...
  • 代理模式(Proxy模式)详解

    千次阅读 2020-03-16 14:15:50
    在有些情况下,一个客户不能或者不想直接访问另一...在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真...
  • 我们都知道SpringAOP是用代理模式实现,那么到底是如何实现的呢?我们来探一探究竟,并手写部分代码。 定义 代理模式(Proxy Pattern):是指为其他对象提供一种代理、来控制这个对象的访问。 代理对象在客户端和...
  • 这篇文章是我找到关于介绍代理模式和装饰器模式比较好的文章了。 节前把3个设计模式的学习心得分享了一下,分别是适配器模式,装饰模式,代理模式。 但是似乎越写越有些迷糊,这三种模式都有着一些大同小异的...
  • java设计模式之代理模式详解

    千次阅读 2018-06-03 01:40:03
    代理模式在Java中十分常见,有如Spring实现AOP而使用动态代理,更有RPC实现中使用的调用端调用的代理服务。代理模型除了是一种设计模式之外,它更是一种思维,所以探讨并深入理解这种模型是非常有必要的。1.简介定义...
  • 设计模式学习之代理模式

    千次阅读 2017-04-15 21:41:36
    代理模式,可以分为两种,一种是静态代理,一种是动态代理。 两种代理从虚拟机加载类的角度来讲,本质上都是一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为。在我们平时写代码的...
  • 这篇文章讲述的是java设计模式,包括单例模式、工厂模式、代理模式,如有错误或不当之处,希望各位大神批评指正。 什么是设计模式? 设计模式:是指在大量的理论和实践中总结和理论化之后,优选的代码结构、编程...
  • Java代理模式及其应用

    万次阅读 多人点赞 2017-07-03 16:01:17
    代理根据代理类的产生方式和时机分为静态代理和动态代理两种。代理类不仅可以有效的将具体的实现与调用方进行解耦,通过面向接口进行编码完全将具体的实现隐藏在内部,而且还可以在符合开闭原则的前提下,对目标类...
  • 《设计模式系列》- 代理模式

    千次阅读 多人点赞 2021-07-04 23:17:12
    阿里云一面,代理模式问了半小时

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 537,658
精华内容 215,063
关键字:

代理模式