【Java知识点详解 2】动态代理

发布于 2021-1-13 15:38
浏览
0收藏

一、代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

著名的代理模式例子为引用计数(英语:reference counting)指针对象。

当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

二、组成
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

三、优点
1、职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。

2、保护对象
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。

3、高扩展性
四、模式结构
一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理
  对象实现同一个接口,先访问代理类再访问真正要访问的对象。

代理模式分为静态代理、动态代理。

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

五、静态代理
创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。

六、动态代理
1、动态代理流程图

【Java知识点详解 2】动态代理-开源基础软件社区
2、动态代理代码实现
(1)代理类

利用反射机制在运行时创建代理类。
接口、被代理类不变,我们构建一个ProxyInvocationHandler类来实现InvocationHandler接口。

package com.guor.aop.dynamicproxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class ProxyInvocationHandler implements InvocationHandler {
 private Object target;
 
 public Object getTarget() {
  return target;
 }
 
 public void setTarget(Object target) {
  this.target = target;
 }
 
 //生成得到代理类
 public Object getProxy() {
  return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
 }
 
 //处理代理实例,并返回结果
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  log(method.getName());
  //动态代理的本质就是使用反射机制来实现
  Object result = method.invoke(target, args);
  return result;
 }
 
 public void log(String msg) {
  System.out.println("执行了"+msg+"方法");
 }
}


 通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler。 

(2)被代理类UserService

package com.guor.aop;
 
public interface UserService {
 public void add();
 public void delete();
 public void update();
 public void query();
}

 

package com.guor.aop;
 
public class UserServiceImpl implements UserService {
 
 public void add() {
  System.out.println("add");
 }
 
 public void delete() {
  System.out.println("delete");
 }
 
 public void update() {
  System.out.println("update");
 }
 
 public void query() {
  System.out.println("query");
 }
 
}


(3)执行动态代理

package com.guor.aop.dynamicproxy;
 
import com.guor.aop.UserService;
import com.guor.aop.UserServiceImpl;
 
public class Client {
 
 public static void main(String[] args) {
  //真实角色
  UserService userService = new UserServiceImpl();
  
  //代理角色
  ProxyInvocationHandler pih = new ProxyInvocationHandler();
  //通过调用程序处理角色来处理我们要调用的接口对象
  pih.setTarget(userService);
  
  UserService proxy = (UserService) pih.getProxy();
  proxy.update();
 }
 
}


(4)控制台输出 

【Java知识点详解 2】动态代理-开源基础软件社区

七、动态代理底层实现
1、动态代理具体步骤
通过实现 InvocationHandler 接口创建自己的调用处理器;
通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
2、源码分析
(1)newProxyInstance

既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?

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


(2)getProxyClass0

利用getProxyClass0(loader, intfs)生成代理类Proxy的Class对象。

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
 if (interfaces.length > 65535) {
  throw new IllegalArgumentException("interface limit exceeded");
 }
 
 // If the proxy class defined by the given loader implementing
 // the given interfaces exists, this will simply return the cached copy;
 // otherwise, it will create the proxy class via the ProxyClassFactory
 return proxyClassCache.get(loader, interfaces);
}

(3)ProxyClassFactory

ProxyClassFactory内部类创建、定义代理类,返回给定ClassLoader 和interfaces的代理类。

private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
 // prefix for all proxy class names
 private static final String proxyClassNamePrefix = "$Proxy";
 
 // next number to use for generation of unique proxy class names
 private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
 @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;
   try {
    interfaceClass = Class.forName(intf.getName(), false, loader);
   } catch (ClassNotFoundException e) {
   }
   if (interfaceClass != intf) {
    throw new IllegalArgumentException(
     intf + " is not visible from class loader");
   }
   /*
    * Verify that the Class object actually represents an
    * interface.
    */
   if (!interfaceClass.isInterface()) {
    throw new IllegalArgumentException(
     interfaceClass.getName() + " is not an interface");
   }
   /*
    * Verify that this interface is not a duplicate.
    */
   if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
    throw new IllegalArgumentException(
     "repeated interface: " + interfaceClass.getName());
   }
  }
 
  String proxyPkg = null;     // package to define proxy class in
  int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
 
  /*
   * Record the package of a non-public proxy interface so that the
   * proxy class will be defined in the same package.  Verify that
   * all non-public proxy interfaces are in the same package.
   */
  for (Class<?> intf : interfaces) {
   int flags = intf.getModifiers();
   if (!Modifier.isPublic(flags)) {
    accessFlags = Modifier.FINAL;
    String name = intf.getName();
    int n = name.lastIndexOf('.');
    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
    if (proxyPkg == null) {
     proxyPkg = pkg;
    } else if (!pkg.equals(proxyPkg)) {
     throw new IllegalArgumentException(
      "non-public interfaces from different packages");
    }
   }
  }
 
  if (proxyPkg == null) {
   // if no non-public proxy interfaces, use com.sun.proxy package
   proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  }
 
  /*
   * Choose a name for the proxy class to generate.
   */
  long num = nextUniqueNumber.getAndIncrement();
  String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
  /*
   * Generate the specified proxy class.
   */
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
   proxyName, interfaces, accessFlags);
  try {
   return defineClass0(loader, proxyName,
        proxyClassFile, 0, proxyClassFile.length);
  } catch (ClassFormatError e) {
   /*
    * A ClassFormatError here means that (barring bugs in the
    * proxy class generation code) there was some other
    * invalid aspect of the arguments supplied to the proxy
    * class creation (such as virtual machine limitations
    * exceeded).
    */
   throw new IllegalArgumentException(e.toString());
  }
 }
}


(4)generateProxyClass

一系列检查后,调用ProxyGenerator.generateProxyClass来生成字节码文件。

public static byte[] generateProxyClass(final String name,Class<?>[] interfaces,int accessFlags)
{
 ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
 final byte[] classFile = gen.generateClassFile();
 
 if (saveGeneratedFiles) {
  java.security.AccessController.doPrivileged(
  new java.security.PrivilegedAction<Void>() {
   public Void run() {
    try {
     int i = name.lastIndexOf('.');
     Path path;
     if (i > 0) {
      Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
      Files.createDirectories(dir);
      path = dir.resolve(name.substring(i+1, name.length()) + ".class");
     } else {
      path = Paths.get(name + ".class");
     }
     Files.write(path, classFile);
     return null;
    } catch (IOException e) {
     throw new InternalError(
      "I/O exception saving generated file: " + e);
    }
   }
  });
 }
 
 return classFile;
}


(5)generateClassFile

生成代理类字节码文件的generateClassFile方法:

private byte[] generateClassFile() {
 /* ============================================================
  * Step 1: Assemble ProxyMethod objects for all methods to
  * generate proxy dispatching code for.
  */
 
 /*
  * Record that proxy methods are needed for the hashCode, equals,
  * and toString methods of java.lang.Object.  This is done before
  * the methods from the proxy interfaces so that the methods from
  * java.lang.Object take precedence over duplicate methods in the
  * proxy interfaces.
  */
 addProxyMethod(hashCodeMethod, Object.class);
 addProxyMethod(equalsMethod, Object.class);
 addProxyMethod(toStringMethod, Object.class);
 
 /*
  * Now record all of the methods from the proxy interfaces, giving
  * earlier interfaces precedence over later ones with duplicate
  * methods.
  */
 for (Class<?> intf : interfaces) {
  for (Method m : intf.getMethods()) {
   addProxyMethod(m, intf);
  }
 }
 
 /*
  * For each set of proxy methods with the same signature,
  * verify that the methods' return types are compatible.
  */
 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  checkReturnTypes(sigmethods);
 }
 
 /* ============================================================
  * Step 2: Assemble FieldInfo and MethodInfo structs for all of
  * fields and methods in the class we are generating.
  */
 try {
  methods.add(generateConstructor());
 
  for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
   for (ProxyMethod pm : sigmethods) {
 
    // add static field for method's Method object
    fields.add(new FieldInfo(pm.methodFieldName,
     "Ljava/lang/reflect/Method;",
      ACC_PRIVATE | ACC_STATIC));
 
    // generate code for proxy method and add it
    methods.add(pm.generateMethod());
   }
  }
 
  methods.add(generateStaticInitializer());
 
 } catch (IOException e) {
  throw new InternalError("unexpected I/O Exception", e);
 }
 
 if (methods.size() > 65535) {
  throw new IllegalArgumentException("method limit exceeded");
 }
 if (fields.size() > 65535) {
  throw new IllegalArgumentException("field limit exceeded");
 }
 
 /* ============================================================
  * Step 3: Write the final class file.
  */
 
 /*
  * Make sure that constant pool indexes are reserved for the
  * following items before starting to write the final class file.
  */
 cp.getClass(dotToSlash(className));
 cp.getClass(superclassName);
 for (Class<?> intf: interfaces) {
  cp.getClass(dotToSlash(intf.getName()));
 }
 
 /*
  * Disallow new constant pool additions beyond this point, since
  * we are about to write the final constant pool table.
  */
 cp.setReadOnly();
 
 ByteArrayOutputStream bout = new ByteArrayOutputStream();
 DataOutputStream dout = new DataOutputStream(bout);
 
 try {
  /*
   * Write all the items of the "ClassFile" structure.
   * See JVMS section 4.1.
   */
         // u4 magic;
  dout.writeInt(0xCAFEBABE);
         // u2 minor_version;
  dout.writeShort(CLASSFILE_MINOR_VERSION);
         // u2 major_version;
  dout.writeShort(CLASSFILE_MAJOR_VERSION);
 
  cp.write(dout);             // (write constant pool)
 
         // u2 access_flags;
  dout.writeShort(accessFlags);
         // u2 this_class;
  dout.writeShort(cp.getClass(dotToSlash(className)));
         // u2 super_class;
  dout.writeShort(cp.getClass(superclassName));
 
         // u2 interfaces_count;
  dout.writeShort(interfaces.length);
         // u2 interfaces[interfaces_count];
  for (Class<?> intf : interfaces) {
   dout.writeShort(cp.getClass(
    dotToSlash(intf.getName())));
  }
 
         // u2 fields_count;
  dout.writeShort(fields.size());
         // field_info fields[fields_count];
  for (FieldInfo f : fields) {
   f.write(dout);
  }
 
         // u2 methods_count;
  dout.writeShort(methods.size());
         // method_info methods[methods_count];
  for (MethodInfo m : methods) {
   m.write(dout);
  }
 
          // u2 attributes_count;
  dout.writeShort(0); // (no ClassFile attributes for proxy classes)
 
 } catch (IOException e) {
  throw new InternalError("unexpected I/O Exception", e);
 }
 
 return bout.toByteArray();
}


字节码生成后,调用defineClass0来解析字节码,生成了Proxy的Class对象。在了解完代理类动态生成过程后,生产的代理类是怎样的,谁来执行这个代理类。

其中,在ProxyGenerator.generateProxyClass函数中 saveGeneratedFiles定义如下,其指代是否保存生成的代理类class文件,默认false不保存。

在前面的示例中,我们修改了此系统变量:

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


 

收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐