Java动态代理详解(Proxy+InvocationHandler)

1. 什么是代理

以商品的生产、销售为例。现有某品牌U盘的生产工厂,该工厂并不直接销售U盘给我们,而是批发卖给N个商家,然后由商家进行销售。

我们可以理解为,商家就是工厂的代理。商家作为代理,可以实现工厂所不具备的功能,例如:

  • 对会员用户进行打折优惠
  • 对黑名单用户禁止销售
  • 对部分用户赠送优惠券
  • ……

在这里插入图片描述

2. 静态代理

2.1 静态代理代码示例

仍然以U盘为例,现有如下角色:

  • U盘生产工厂:金士顿工厂、闪迪工厂
  • 销售商家:金士顿商家、闪迪商家
  • 客户(购买者)

接口类 UsbSellService

public interface UsbSellService {
    
    
    //销售
    float sell(int amount);
}

工厂类(目标类)

  • 金士顿工厂 UsbKingFactory
public class UsbKingFactory implements UsbSellService {
    
    
    @Override
    public float sell(int amount) {
    
    
        System.out.println("金士顿工厂卖出数量:" + amount);
        return 10f * amount;
    }
}
  • 闪迪工厂
public class UsbSdFactory implements UsbSellService {
    
    
    @Override
    public float sell(int amount) {
    
    
        System.out.println("闪迪工厂卖出数量:" + amount);
        return 8f * amount;
    }
}

商家类(代理类)

  • 金士顿商家 UsbKingSeller
public class UsbKingSeller implements UsbSellService {
    
    

    private UsbSellService usbKingFactory = new UsbKingFactory();

    @Override
    public float sell(int amount) {
    
    
        float money = usbKingFactory.sell(amount);
        String sellName = UsbKingSeller.class.getSimpleName();
        System.out.println(sellName + "销售" + amount + "个,总价:" + money);
        System.out.println(sellName + "赠送了10元优惠券");
        return money;
    }
}
  • 闪迪商家 UsbSdSeller
public class UsbSdSeller implements UsbSellService {
    
    

    private UsbSdFactory usbSdFactory = new UsbSdFactory();

    @Override
    public float sell(int amount) {
    
    
        float money = usbSdFactory.sell(amount);
        String sellName = UsbSdSeller.class.getSimpleName();
        System.out.println(sellName + "销售" + amount + "个,总价:" + money);
        System.out.println(sellName + "赠送了8元优惠券");
        return money;
    }
}

客户类(购买者)

public class CustomerApp {
    
    
    public static void main(String[] args) {
    
    
        UsbSellService usbKingSeller = new UsbKingSeller();
        int amount = 50;
        usbKingSeller.sell(amount);

        System.out.println("---------------------------------------");

        UsbSellService usbSdSeller = new UsbSdSeller();
        usbSdSeller.sell(amount);
    }
}

输出结果如下:

金士顿工厂卖出数量:50
UsbKingSeller销售50个,总价:500.0
UsbKingSeller赠送了10元优惠券
---------------------------------------
闪迪工厂卖出数量:50
UsbSdSeller销售50个,总价:400.0
UsbSdSeller赠送了8元优惠券

2.2 静态代理总结

Customer 并不直接操作工厂类,而是操作商家对象,每个商家类依赖了对应的工厂。

2.3 静态代理优缺点

优点

  • 实现容易
  • 便于理解

缺点

  • 上述例子中,如果同一个商家要代理多个目标,则需要编写多个商家代理类
  • 当工厂(目标)类增加了,代理类可能需要成倍地增加
  • 当接口增加了方法,所有的实现类(目标类、代理类)都要修改

3. 动态代理

3.1 动态代理概念

Java的动态代理,主要由 InvocationHandler 和 Proxy 实现。

  • InvocationHandler 负责实现代理需要的功能。主要是调用目标类的方法,以及实现功能增强
  • Proxy 根据InvocationHandler,创建对应的代理对象

3.2 动态代理例子

创建 InvocationHandler

public class SellHandler implements InvocationHandler {
    
    

    private Object target; //代理目标对象

    public SellHandler(UsbSellService factory){
    
    
        this.target = factory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
    	//调用目标对象方法
        float result = (Float)method.invoke(target, args);
        //AOP,功能增强
        result += 25;
        System.out.println("动态代理SellHandler向您赠送优惠券");
        
        return result;
    }
}

创建代理对象

public class ProxyCustomerApp {
    
    
    public static void main(String[] args) {
    
    
        //1.创建目标对象
        UsbSellService factory = new UsbKingFactory();
        //2.创建 InvocationHandler 对象
        InvocationHandler handler = new SellHandler(factory);
        //3.创建代理对象
        UsbSellService proxy = (UsbSellService) Proxy.newProxyInstance(
        		factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),
                handler);

        proxy.sell(50);
    }
}

4. 原理解析

上面这个例子,我们创建了proxy动态代理对象,通过这个对象执行了InvocationHandler对应的方法。

问题一: Proxy.newProxyInstance 创建的是什么对象,为什么可以强制转换成 UsbSellService 类型?

为了了解这个原理,我们进入 Proxy.newProxyInstance 源码看看。这里我只贴出几行关键代码,其他代码大家打开IDEA详细查看:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        //1. 生成代理类型 com.sun.proxy.$Proxy0
        Class<?> cl = getProxyClass0(loader, intfs);
        
        //2. 获取代理类型构造函数
        final Constructor<?> cons = cl.getConstructor(constructorParams);

		final InvocationHandler ih = h;

		//3. 将InvocationHandler作为参数,实例化 com.sun.proxy.$Proxy0 对象
		return cons.newInstance(new Object[]{
    
    h});
	}

问题二: com.sun.proxy.$Proxy0 是何方神圣?它是怎么产生的?

我们大胆猜测,com.sun.proxy.$Proxy0 至少是实现了 UsbSellService 接口的类型,否则怎么可能强制转换。

修改之前写的 ProxyCustomerApp ,在第一行加上这句代码,然后重新运行

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

此时,在项目根路径下会生成 com\sun\proxy\$Proxy0.class 文件,这是底层调用 new ProxyGenerator().generateProxyClass() 动态创建的类型。将 $Proxy0.class 使用JAD.EXE反编译(直接IDEA打开查看也可以,但是比较不直观)得到 $Proxy0.java 源代码文件,内容如下:

package com.sun.proxy;

public final class $Proxy0 extends Proxy
    implements UsbSellService
{
    
    

    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 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);
        }
    }

    public final float sell(int i)
    {
    
    
        try
        {
    
    
            return ((Float)super.h.invoke(this, m3, new Object[] {
    
    
                Integer.valueOf(i)
            })).floatValue();
        }
        catch(Error _ex) {
    
     }
        catch(Throwable throwable)
        {
    
    
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;

    static 
    {
    
    
        try
        {
    
    
            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]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("com.train.dynamicproxy.service.UsbSellService").getMethod("sell", new Class[] {
    
    
                Integer.TYPE
            });
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
    
    
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
    
    
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

重点看2个地方:

  • com.sun.proxy.$Proxy0 果然实现了 UsbSellService 接口
  • com.sun.proxy.$Proxy0 的 sell() 方法,本质上就是调用 InvocationHandler.invoke 方法

猜你喜欢

转载自blog.csdn.net/qq_28834355/article/details/113996604