彻底明白Java代理

彻底明白Java代理


  学习代理,首先要明白为什么会有代理这种设计模式,它的意义是什么?个人认为最主要的原因是:开闭原则. 举一个例子,鸟儿在空中飞翔,需要统计飞翔的时间,有人认为这个很简单,在开始、结尾记录时间,做差即可.但是假如这个方法是某个jar包提供的,不允许我们修改源码?

  • 静态代理(编译期生成代理类)
  • 基于JDK实现动态代理(JDK动态代理由Java内部的反射机制实例化代理对象,代理的调用可以委托给类方法)
  • 基于CGlib动态代理((cglib动态代理底层是借助asm字节码技术)
  • 基于AspectJ 实现动态代理(修改目标类的字节码,织入代理的字节,在编译的时候插入动态代理的字节码,不会生成全新的class)

遗留问题 ClassLoader的深入理解


一. 静态代理

1.1. 继承实现静态代理

业务逻辑:

public interface ProductService {
    void addProduct(String productName);
}
public class ProductServiceImpl implements ProductService {
    @Override
    public void addProduct(String productName) {
        System.out.println("productName = "+productName);
    }
}
public class ProductProxy extends ProductServiceImpl{
    public void addProduct(String name){
        System.out.println("begin");
        super.addProduct(name);
        System.out.println("end");
    }
}

  继承存在的问题是Java是单继承,如果要增加打印日志的逻辑,还需要继续继承ProductProxy,导致无限的继承,灵活性降低

1.2 聚合实现静态代理

public class ProductProxyInterface implements ProductService{
    private ProductService productService;
    public ProductProxyInterface(ProductService productService) {
        this.productService = productService;
    }
    @Override
    public void addProduct(String productName) {
        System.out.println("interface begin");
        productService.addProduct(productName);
        System.out.println("interface end");
    }
}

继承可以灵活的调整除业务逻辑之外的顺序.
静态代理存在以下问题:

  • 如果同时代理多个类,依然会导致类无限制扩展
  • 如果类中有多个方法,同样的逻辑需要反复实现

二. JDK动态代理

JDK动态代理实现的步骤:

  1. 实现InvocationHandler接口.
  2. 通过Proxy.newProxyInstance获取代理类
  3. 通过代理类对象调用目标方法
public class ProductInvocationHandler implements InvocationHandler {
    private Object target;
    public Object getInstance(Object object) {
        this.target = object;
        Class<?> aClass = this.target.getClass();
        return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String currentDate = simpleDateFormat.format(new Date());
        System.out.println("日期 = "+currentDate);
        return method.invoke(this.target,args);
    }
}
 public static void main(String[] args) {
      ProductService productService = new ProductServiceImpl();
        ProductService proxy = (ProductService) new ProductInvocationHandler().getInstance(productService);//注意class com.sun.proxy.$Proxy0
        proxy.addProduct("name");
        System.out.println(proxy.getClass());
        System.out.println(productService.getClass());//class com.jd.ads.union.demo.web.controller.proxy.ProductServiceImpl
        /**
        * 注意代理类对象是一个新的对象,和目标对象有缺别
        **/
    }
日期 = 2021-01-06
productName = name
class com.sun.proxy.$Proxy0
class com.jd.ads.union.demo.web.controller.proxy.ProductServiceImpl

源码分析

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    {
        //拷贝类对象 新创建的类对象,属性和interfaces一样
        final Class<?>[] intfs = interfaces.clone();
        //生成代理类对象 分析见下
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        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});
    }

newProxyInstance()方法帮我们执行了生成代理类(类对象)----获取构造器----生成代理对象这三步;

  • 生成代理类: Class<?> cl = getProxyClass0(loader, intfs);
  • 获取构造器: final Constructor<?> cons =cl.getConstructor(constructorParams);
  • 生成代理对象: cons.newInstance(new Object[]{h});

getProxyClass0(loader, intfs) 会从缓存中拿,缓存没有,ProxyClassFactory创建

    /** 根据classLoader和interface来生成代理类对象
     * A factory function that generates, defines and returns the proxy class given the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object. 重要
                 */
                Class<?> interfaceClass = null;
                //通过classLoader interfaceName获取interface类的类对象
                interfaceClass = Class.forName(intf.getName(), false, loader);
                //interfaceClass  是否和入参的类对象一样,此处微妙之中在于用classLoader校验类是否一样,后续讲解classLoader
                if (interfaceClass != intf) {
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
            
                }
            }
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            .........
            
            /*
             * Generate the specified proxy class. 生成类字节码 见下面讲解
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            //native 方法
            return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
          
        }
    }
  public static byte[] generateProxyClass(final String var0(proxyName), Class<?>[] var1(interfaces), int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();//生成字节码
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }
        return var4;
    }

举例子,反编译的结果:

public final class $Proxy0 extends Proxy
  implements ProductService //说明了动态代理为什么只能是实现接口,不能在继承上实现,因为java的单继承
{
  // 变量,都是private static Method  XXX
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
 
  // 代理类的构造函数,其参数正是是InvocationHandler实例,Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的
  public $Proxy0(InvocationHandler ProductInvocationHandler)
    throws 
  {
    super(ProductInvocationHandler);
  }
 
  // 以下Object中的三个方法
  public final boolean equals(Object paramObject)
    throws 
  {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    
  }
  
  // 接口代理方法:**重点**
  public final void addProduct(String productName){
      this.h.invoke(this, m3, productName);//ProductInvocationHandler调用
  }
 
  public final String toString(){
   return ((String)this.h.invoke(this, m2, null));
  }
    
 
  public final int hashCode(){
    return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
 
  static
  {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.jpeony.spring.proxy.jdk.IHello").getMethod("sayHello", 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]);
      return;
    }
}

总结:jdk动态代理其实也就是生成代理类对象,调用原来的目标方法,实际内部是调用InvocationHandler的invoke


参考

JDK动态代理实现原理jdk8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值