友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

4. 代理模式 与 AOP

4.1. 定义

代理模式(Proxy)

为其他对象提供一种代理以控制这个对象的访问。

— Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
《设计模式》

4.2. 类图

Diagram

Subject类,定义了RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。

4.3. 静态代理模式

代码 12. proxy/Subject.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.diguage.didp.proxy;

/**
 * Subject类,定义了 {@link RealSubject} 和 {@link Proxy} 的共用接口。
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-16
 */
public interface Subject {
  void request();
}

RealSubject类,定义Proxy所代表的真实实体。

代码 13. proxy/RealSubject.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.diguage.didp.proxy;

/**
 * RealSubject 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-16
 */
public class RealSubject implements Subject {
  @Override
  public void request() {
    System.out.println("真是请求!");
  }
}

Proxy类,保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。

代码 14. proxy/Proxy.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.diguage.didp.proxy;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-16
 */
public class Proxy implements Subject {
  private Subject realSubject;

  public Proxy(Subject realSubject) {
    this.realSubject = realSubject;
  }

  @Override
  public void request() {
    System.out.println("使用代理开始请求…");
    realSubject.request();
  }
}
代码 15. proxy/Client.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.diguage.didp.proxy;

/**
 * Client 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-16
 */
public class Client {
  public static void main(String[] args) {
    Subject subject = new Proxy(new RealSubject());
    subject.request();
  }
}

4.3.1. 再来一个例子…

代码 16. proxy/UserService.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.diguage.didp.proxy;

/**
 * 用户接口
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 16/11/2016.
 */
public interface UserService {
  String getById(int id);
}
代码 17. proxy/UserServiceImpl.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.diguage.didp.proxy;

import java.util.concurrent.TimeUnit;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 16/11/2016.
 */
public class UserServiceImpl implements UserService {
  @Override
  public String getById(int id) {
    try {
      System.out.println("真是请求:根据ID获取对应用户…");
      TimeUnit.MILLISECONDS.sleep(id);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return "User-" + id;
  }
}
代码 18. proxy/UserServiceAuthenticationProxy.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.diguage.didp.proxy;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 16/11/2016.
 */
public class UserServiceAuthenticationProxy implements UserService {
  UserService userService;

  public UserServiceAuthenticationProxy(UserService userService) {
    this.userService = userService;
  }

  @Override
  public String getById(int id) {
    if (id < 1000) {
      System.out.println("非特权用户,禁止访问……");
      return null;
    }
    return userService.getById(id);
  }
}
代码 19. proxy/UserServiceProxyMain.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.diguage.didp.proxy;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 16/11/2016.
 */
public class UserServiceProxyMain {
  public static void main(String[] args) {
    UserService userService = new UserServiceImpl();
    UserService proxy = new UserServiceAuthenticationProxy(userService);
    System.out.println(proxy.getById(123));
  }
}

请思考:

到现在为止,你们发现什么问题了吗?

代理模式这样实现有什么问题吗?还有其他改进空间吗??

4.4. 动态代理

先看一个图,看看方法调用的:

Diagram

Java 是面向对象,那么 Java 如何对 Java 中 packageclass、 方法等如何建模呢?

我们可以从上图的流程中可以横切一刀,如下图:

Diagram
代码 20. proxy/ProfilerInvocationHandler.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.diguage.didp.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-16
 */
public class ProfilerInvocationHandler implements InvocationHandler {
  private Object realObject;

  public ProfilerInvocationHandler(Object realObject) {
    this.realObject = realObject;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.printf("Profiler======\n%s\n", method.getName());
    long start = System.currentTimeMillis();
    Object result = method.invoke(realObject, args);
    long time = System.currentTimeMillis() - start;
    System.out.println("耗时: " + time);
    System.out.println("Profiler======");
    return result;
  }
}
代码 21. proxy/LoggerInvocationHandler.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.diguage.didp.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2018-03-16
 */
public class LoggerInvocationHandler implements InvocationHandler {
  private Object realObject;

  public LoggerInvocationHandler(Object realObject) {
    this.realObject = realObject;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.printf("Logger------\nMethod: %s\n", method.getName());
    if (args != null && args.length > 0) {
      System.out.println("Params:");
      for (Object arg : args) {
          System.out.println(arg);
      }
    }
    Object result = method.invoke(realObject, args);
    System.out.println("Logger------");
    return result;
  }
}
代码 22. proxy/UserServiceDynamicProxyMain.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.diguage.didp.proxy;

import java.lang.reflect.Proxy;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 16/11/2016.
 */
public class UserServiceDynamicProxyMain {
  public static void main(String[] args) {
    UserService userService = new UserServiceImpl();

    ClassLoader classLoader = UserService.class.getClassLoader();
    Class<?>[] interfaces = UserServiceImpl.class.getInterfaces();

    UserService profilerInstance =
        (UserService)
            Proxy.newProxyInstance(
                classLoader,
                interfaces,
                new ProfilerInvocationHandler(userService));

    UserService logProfilerInstance =
        (UserService)
            Proxy.newProxyInstance(
                classLoader,
                interfaces,
                new LoggerInvocationHandler(profilerInstance));

    System.out.println("最终结果:" + logProfilerInstance.getById(345));

    Subject subject = (Subject) Proxy.newProxyInstance(
        RealSubject.class.getClassLoader(),
        RealSubject.class.getInterfaces(),
        new LoggerInvocationHandler(new RealSubject()));
    subject.request();
  }

//  public static <T> T newProxyInstance(ClassLoader classLoader,
//                                       InvocationHandler handler, T... t) {
//    return (T) Proxy.newProxyInstance(classLoader, t.getClass(), handler);
//  }
}

流程与这个类似这样的,还有 Java Web 中的 Filter, Spring MVC 中的 ``

请思考:

代理模式这样实现有什么问题吗?还有其他改进空间吗??

4.5. 来个黑魔法…

代码 23. proxy/asm/Account.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.diguage.didp.proxy.asm;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-17 23:19
 */
public class Account {
  public int operation() {
    System.out.println("operation……");
    int timeout = new Random().nextInt(1000);
    try {
      TimeUnit.MILLISECONDS.sleep(timeout + 1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return timeout;
  }

  public String getById(int id) {
    System.out.println("getById……");
    try {
      // 以参数传递过来的数字来决定休眠时间,
      // 来检验实际效果
      TimeUnit.MILLISECONDS.sleep(id);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return "Account-" + id;
  }

  public static void main(String[] args) {
    Account account = new Account();
    account.operation();
    account.getById(123);
  }
}

我们来看一下,编译后生成的文件……

cd /Users/diguage/Documents/wiki.diguage.com/java/deep-in-design-patterns/target/classes/com/diguage/didp/proxy/asm

vim /Users/diguage/Documents/wiki.diguage.com/java/deep-in-design-patterns/target/classes/com/diguage/didp/proxy/asm/Account.class

:%!xxd


javap -v /Users/diguage/Documents/wiki.diguage.com/java/deep-in-design-patterns/target/classes/com/diguage/didp/proxy/asm/Account.class

Java 虚拟机规范中,对 Java Class 文件格式的定义: Chapter 4. The class File Format

Java Class文件格式
图 1. Java Class文件格式

Java 虚拟机规范中,对 Java 方法表格式的定义: Chapter 4. The class File Format

Java Class文件格式-方法表格式
图 2. Java Class文件格式-方法表格式
Java 处理流程
图 3. Java 处理流程
JVM 语言
图 4. JVM 语言

时间统计类……

代码 24. proxy/asm/Profiler.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.diguage.didp.proxy.asm;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-17 23:22
 */
public class Profiler {
  static ThreadLocal<Long> t = new ThreadLocal<Long>();

  public static void start() {
    t.set(System.currentTimeMillis());
  }

  public static void end() {
    long time = System.currentTimeMillis() - t.get();
    System.out.print(Thread.currentThread().getStackTrace()[2] + " speed:");
    System.out.println(time);
  }
}

修改字节码,在每个方法的开始部分和返回部分来插入我们的时间统计代码……

代码 25. proxy/asm/ProfilerMethodAdapter.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.diguage.didp.proxy.asm;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-18 10:15
 */
public class ProfilerMethodAdapter extends MethodVisitor implements Opcodes {

  public ProfilerMethodAdapter(MethodVisitor mv) {
    super(Opcodes.ASM5, mv);
  }

  @Override
  public void visitCode() {
    visitMethodInsn(Opcodes.INVOKESTATIC, "com/diguage/didp/proxy/asm/Profiler", "start", "()V");
    super.visitCode();
  }

  @Override
  public void visitInsn(int opcode) {
    if (opcode >= IRETURN && opcode <= RETURN) {
      visitMethodInsn(Opcodes.INVOKESTATIC, "com/diguage/didp/proxy/asm/Profiler", "end", "()V");
    }
    mv.visitInsn(opcode);
  }
}
代码 26. proxy/asm/ProfilerClassAdapter.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.diguage.didp.proxy.asm;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-18 10:15
 */
public class ProfilerClassAdapter extends ClassVisitor {
  public ProfilerClassAdapter(ClassVisitor cv) {
    super(Opcodes.ASM5, cv);
  }

  @Override
  public MethodVisitor visitMethod(
          final int access,
          final String name,
          final String desc,
          final String signature,
          final String[] exceptions) {
    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
    MethodVisitor wrapperMv = mv;
    if (mv != null) {
      // TODO 如何实现只编制指定类下的方法呢?
//      if (name.equals("operation")) {
      wrapperMv = new ProfilerMethodAdapter(mv);
//      }
    }
    return wrapperMv;
  }
}
代码 27. proxy/asm/ProfilerWeaveMain.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.diguage.didp.proxy.asm;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-17 23:26
 */
public class ProfilerWeaveMain {
  public static void main(String[] args) throws IOException, URISyntaxException {
    System.out.println("开始修改字节码…");
    // 如何实现编制指点类?通过配置文件来获得!
    String className = Account.class.getName();
    ClassReader cr = new ClassReader(className);
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
    ProfilerClassAdapter classAdapter = new ProfilerClassAdapter(cw);
    cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
    byte[] data = cw.toByteArray();
    URL url =
        Account.class.getClassLoader().getResource(className.replaceAll("\\.", "/") + ".class");
//    System.out.println(url);
//    System.out.println(
//        "file:/Users/diguage/Documents/wiki.diguage.com/java/deep-in-design-patterns/target/classes/com/diguage/didp/proxy/asm/Account.class");
//    //    File file = new File(url.replaceFirst("file:/", "file:///"));
    File file = new File(url.toURI());
//    System.out.println(file.exists());
//    if (!file.exists()) {
//      System.out.println("Create:" + file.createNewFile());
//    }
//    System.out.println(file.isFile());
//    System.out.println(file.canWrite());
    FileOutputStream fout = new FileOutputStream(file);
    fout.write(data);
    fout.close();
    System.out.println("字节码修改完毕。");
  }
}



再来运行一下看看…

代码 28. proxy/asm/AfterWeaveProfilerMain.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.diguage.didp.proxy.asm;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-17 23:47
 */
public class AfterWeaveProfilerMain {
  public static void main(String[] args) {
    Account account = new Account();
    System.out.println(account.operation());
    // 可以通过传递不同的数字来观察耗时统计,
    // 来检验是否修改过字节码……
    System.out.println(account.getById(4000));
    System.out.println(account.toString());
  }
}

在使用 ASM 生成代理 class 文件时,发现确定类文件的路径是个大问题。这里需要注意的是针对 file 的 URL 格式规范。

请思考:

代理模式这样实现有什么问题吗?还有其他改进空间吗??

4.6. 来点通俗易懂的…

4.7. ClassLoader

代码 29. proxy/loader/ProxyClassLoader.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.diguage.didp.proxy.loader;


import com.diguage.didp.proxy.asm.ProfilerClassAdapter;
import com.diguage.didp.proxy.bytebuddy.ProfilerInterceptor;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2018-03-23
 */
public class ProxyClassLoader extends ClassLoader {
  public ProxyClassLoader(ClassLoader parent) {
    super(parent);
  }

  /**
   * @param className 全限定名的类目, 例如 com.diguage.Foo
   */
  private Class getClass(String className) throws ClassNotFoundException {
    String file = className.replace('.', File.separatorChar) + ".class";
    byte[] classByteArray = null;
    try {
      // TODO 在这里做手脚
//      ClassReader cr = new ClassReader(className);
//      ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
//      ProfilerClassAdapter classAdapter = new ProfilerClassAdapter(cw);
//      cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
//      classByteArray = cw.toByteArray();

      ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(Thread.currentThread().getContextClassLoader());
      TypePool typePool = TypePool.Default.ofSystemLoader();
//
      classByteArray = new ByteBuddy()
          .rebase(typePool.describe(className).resolve(), classFileLocator)
          .method(ElementMatchers.any())
          .intercept(MethodDelegation.to(ProfilerInterceptor.class))
          .make()
          .getBytes();



//      Class<? extends DynamicType.Unloaded> clazz = new ByteBuddy()
//          .rebase()
//          .rebase(Object.class)
//          .method(ElementMatchers.any())
//          .intercept(MethodDelegation.to(ProfilerInterceptor.class))
//          .make().getClass();
      Class clazz = defineClass(className, classByteArray, 0, classByteArray.length);
      resolveClass(clazz);
      return clazz;
    } catch (Throwable e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * @param name 全限定名的类名
   */
  @Override
  public Class loadClass(String name) throws ClassNotFoundException {
    System.out.println("Loading Class '" + name + "'");
    if (name.startsWith("com.diguage.didp.proxy.asm.Account") && !name.contains("$")) {
      System.out.println("Loading Class using ProxyClassLoader");
      return getClass(name);
    }
    return super.loadClass(name);
  }

  /**
   * @param name 全限定名的类名
   * @return 文件的字节数组
   * @throws IOException
   */
  private byte[] loadClassFileData(String name) throws IOException {
    InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
    int size = stream.available();
    byte buff[] = new byte[size];
    DataInputStream in = new DataInputStream(stream);
    in.readFully(buff);
    in.close();
    return buff;
  }
}
代码 30. proxy/loader/ClassLoaderMain.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.diguage.didp.proxy.loader;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassLoaderMain {
  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
    String progClass = "com.diguage.didp.proxy.asm.Account";

    ProxyClassLoader classLoader = new ProxyClassLoader(Thread.currentThread().getContextClassLoader());
    Class clazz = classLoader.loadClass(progClass);
    Object instance = clazz.newInstance();
    Method operation = clazz.getMethod("operation");
    operation.invoke(instance);

    Method getById = clazz.getMethod("getById", int.class);
    getById.invoke(instance, 123);
  }
}

请思考:

代理模式这样实现有什么问题吗?还有其他改进空间吗??

4.8. 可不可更透明点…

代码 31. proxy/agent/PreMainAddTimeStatAgent.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.diguage.didp.proxy.agent;

import com.diguage.didp.proxy.asm.ProfilerClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-18 10:21
 */
public class PreMainAddTimeStatAgent {
  public static void premain(String agentArgs, Instrumentation instrumentation) {
    System.out.println("agentArgs: " + agentArgs);
    instrumentation.addTransformer(
        new ClassFileTransformer() {
          @Override
          public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if (className.equals("com/diguage/didp/proxy/asm/Account")) {
              System.out.println("meet com.diguage.didp.proxy.asm.Account");
              ClassReader cr = new ClassReader(classfileBuffer);
              ClassWriter cw =
                  new ClassWriter(
                      (ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES));
              ProfilerClassAdapter classAdapter = new ProfilerClassAdapter(cw);
              cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
              return cw.toByteArray();
            } else {
              System.out.println("\nload: " + className);
              return classfileBuffer;
            }
          }
        }
    );
  }
}
代码 32. proxy/agent/RunAccountMain.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.diguage.didp.proxy.agent;

import com.diguage.didp.proxy.asm.Account;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2016-11-18 14:41
 */
public class RunAccountMain {
  public static void main(String[] args) {
    Account account = new Account();
    System.out.println(account.getById(789));
    System.out.println(account.operation());
  }
}
# 运行
java -cp .:/Users/diguage/develop/tools/MavenRepository/org/ow2/asm/asm-all/5.2/asm-all-5.2.jar -javaagent:target/deep-in-design-patterns-0.0.1.jar com.diguage.didp.proxy.agent.RunAccountMain

java -cp .:/Users/diguage/develop/tools/MavenRepository/net/bytebuddy/byte-buddy/1.8.0/byte-buddy-1.8.0.jar -javaagent:target/deep-in-design-patterns-0.0.1.jar com.diguage.didp.proxy.agent.RunAccountMain

请思考:

代理模式这样实现有什么问题吗?还有其他改进空间吗?应用上线后怎么搞?

4.9. 来点通俗易懂的…

用 Byte Buddy 来搞…

代码 33. proxy/bytebuddy/ProfilerAnnotationInterceptor.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.diguage.didp.proxy.bytebuddy;

import net.bytebuddy.asm.Advice;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2018-03-15
 */
public class ProfilerAnnotationInterceptor {
  public static ThreadLocal<Long> threadLocal = new ThreadLocal();

  @Advice.OnMethodEnter
  public static void enter(@Advice.Origin("#t.#m") String signature) {
    System.out.printf("Enter: %s\n", signature);
    long start = System.currentTimeMillis();
    threadLocal.set(start);
  }

  @Advice.OnMethodExit
  public static void exit(@Advice.Origin("#t.#m") String signature) {
    long value = System.currentTimeMillis() - threadLocal.get();
    System.out.printf("Exit: %s\nTime: %d\n\n", signature, value);
  }
}
代码 34. proxy/bytebuddy/ProflierAnnotationAgent.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.diguage.didp.proxy.bytebuddy;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.instrument.Instrumentation;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2018-03-15 19:17:38
 */
public class ProflierAnnotationAgent {
  public static void premain(String agentArgs, Instrumentation instrumentation) {
    System.out.println("Premain started");
    try {
      new AgentBuilder.Default()
//        .with(AgentBuilder.Listener.StreamWriting.toSystemOut()) // Debug
          .with(AgentBuilder.TypeStrategy.Default.REBASE)
//        .type((typeDescription, classLoader, javaModule, aClass, protectionDomain) -> true)
          .type(ElementMatchers.any())
          .transform((builder, typeDescription, classLoader, javaModule) ->
              builder.visit(Advice.to(ProfilerAnnotationInterceptor.class).on(ElementMatchers.any())))
          .installOn(instrumentation);
    } catch (RuntimeException e) {
      System.out.println("Exception instrumenting code : " + e);
      e.printStackTrace();
    }
  }
}

4.9.1. 再来一组更简单的…

代码 35. proxy/bytebuddy/ProfilerInterceptor.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.diguage.didp.proxy.bytebuddy;

import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2018-03-16
 */
public class ProfilerInterceptor {
  @RuntimeType
  public static Object intercept(@Origin Method method,
                                 @SuperCall Callable<?> callable) {
    long start = System.currentTimeMillis();
    try {
      return callable.call();
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException(e.getMessage(), e);
    } finally {
      System.out.println(method + " took " + (System.currentTimeMillis() - start) + "\n\n");
    }
  }
}
代码 36. proxy/bytebuddy/ProflierAgent.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.diguage.didp.proxy.bytebuddy;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.instrument.Instrumentation;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2018-03-16
 */
public class ProflierAgent {
  public static void premain(String arguments,
                             Instrumentation instrumentation) {
    new AgentBuilder.Default()
        .type(ElementMatchers.any())
        .transform((builder, type, classLoader, module) ->
            builder.method(ElementMatchers.any())
                .intercept(MethodDelegation.to(ProfilerInterceptor.class))
        ).installOn(instrumentation);
  }
}

请思考:

代理模式这样实现有什么问题吗?还有其他改进空间吗??

4.11. 作用

一般来说分为几种

  • 第一种应用是远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实[DP]。

  • 第二种应用是虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象[DP]。

  • 第三种应用是安全代理,用来控制真实对象访问时的权限[DP]。

  • 第四种是智能指引,是指当调用真实的对象时,代理处理另外一些事[DP]。

代理模式其实就是在访问对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

代理就是真实对象的代表。