类图

image.png

代理模式

静态代理实现

PrintaPrintableble

  1. public interface Printable {
  2. void setPrinterName(String name);
  3. String getPrinterName();
  4. void print(String str);
  5. }

Printer

  1. import java.util.concurrent.TimeUnit;
  2. public class Printer implements Printable {
  3. private String name;
  4. public Printer(String name) {
  5. this.name = name;
  6. heavyJob("正在生成Printer的实例(" + name + ")");
  7. }
  8. public Printer() {
  9. heavyJob("正在生成Printer的实例(" + name + ")");
  10. }
  11. @Override
  12. public void setPrinterName(String name) {
  13. this.name = name;
  14. }
  15. @Override
  16. public String getPrinterName() {
  17. return name;
  18. }
  19. @Override
  20. public void print(String str) {
  21. System.out.println("======" + name + "=====");
  22. System.out.println(str);
  23. }
  24. private void heavyJob(String msg) {
  25. System.out.println(msg);
  26. for (int i = 0; i < 5; i++) {
  27. try {
  28. TimeUnit.SECONDS.sleep(1);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.print(".");
  33. }
  34. System.out.println("结束");
  35. }
  36. }

PrinterProxy

  1. public class PrinterProxy implements Printable {
  2. private String name;
  3. private Printer real;
  4. public PrinterProxy() {
  5. }
  6. public PrinterProxy(String name) {
  7. this.name = name;
  8. }
  9. @Override
  10. public void setPrinterName(String name) {
  11. if (real != null) {
  12. real.setPrinterName(name);
  13. }
  14. this.name = name;
  15. }
  16. @Override
  17. public String getPrinterName() {
  18. return name;
  19. }
  20. @Override
  21. public void print(String str) {
  22. realize();
  23. real.print(str);
  24. }
  25. private void realize() {
  26. if (real == null) {
  27. real = new Printer(name);
  28. }
  29. }
  30. }

Printe>rPrPrinterProxyoxy

Main

  1. public class Main {
  2. public static void main(String[] args) {
  3. Printable p = new PrinterProxy("Alice");
  4. System.out.println("现在的名字是:"+p.getPrinterName());
  5. p.setPrinterName("Bob");
  6. System.out.println("现在的名字是:"+p.getPrinterName());
  7. p.print("Hello, world.");
  8. }
  9. }

类图,如下所示:

Main.png

不论setPrinterName方法和getPrinterName方法被调用多少次,都不会生成Printer类的实例。Printer类并不知道PrinterProxy类的存在,即Printer类并不知道自己到底是通过PrinterProxy被调用的还是直接被调用的。

动态代理

JDK动态代理

依据运行时托自省能力,在运行时动态获取类或者对象的信息

利用动态代理辅助打印 jdk二分搜索的执行轨迹

  1. package com.cninfo.designpattern.structural.proxy.repeat02;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.util.Arrays;
  6. import java.util.concurrent.CountDownLatch;
  7. /**
  8. * @author chenxinwei
  9. * @date 2021/5/13 17:11
  10. **/
  11. public class ProxyMain {
  12. private static final int COUNT = 1000;
  13. public static void main(String[] args) {
  14. Comparable[] values = new Comparable[COUNT];
  15. for (int i = 0; i < COUNT; i++) {
  16. Integer value = Integer.valueOf(i);
  17. values[i] = (Comparable) Proxy.newProxyInstance(
  18. null,
  19. value.getClass().getInterfaces(),
  20. new InvocationHandler() {
  21. @Override
  22. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  23. System.out.println(value + "." + method.getName() + Arrays.toString(args));
  24. return method.invoke(value, args);
  25. }
  26. });
  27. }
  28. CountDownLatch countDownLatch = new CountDownLatch(1);
  29. System.out.println(Arrays.binarySearch(values, 264));
  30. try {
  31. countDownLatch.await();
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }
  1. 499.compareTo[264]
  2. 249.compareTo[264]
  3. 374.compareTo[264]
  4. 311.compareTo[264]
  5. 280.compareTo[264]
  6. 264.compareTo[264]
  7. 264

查看生成的代理类

使用arthas-boot.jar,下载地址参考:https://arthas.aliyun.com/doc/download.html

  1. 搜索包下的class, sc com.cninfo.designpattern.structural.proxy.repeat02.*
  2. 反编译class jad com.cninfo.designpattern.structural.proxy.repeat02.ProxyMain$1

image.png

字节码动态代理

asm(cglib)

Proxy生成的代理类时,对原始类有要求(该类必须实现接口)。如果给一个没有实现接口的类生成代理,可以使用cglib
用来对class进行增强

cglib is a powerful, high performance and quality Code Generation Library. It is used to extend JAVA classes and implements interfaces at runtime.

  • cglib-#.#_#.jar
    • binary distribution, CGLIB classes only, it must be used to extend cglib classes dependant on ASM API
  • cglib-nodep-#.#_#.jar
    • binary distribution, CGLIB and renamed ASM classes, not extendable

分为依赖ASM和不依赖ASM的两种实现

MyBean

  1. package com.cninfo.designpattern.structural.proxy.cglib;
  2. import java.beans.PropertyChangeListener;
  3. import java.io.Serializable;
  4. /**
  5. * @author chenxinwei
  6. * @date 2021/5/13 18:15
  7. **/
  8. public abstract class MyBean implements Serializable {
  9. String property;
  10. abstract public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener);
  11. abstract public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener);
  12. public String getProperty() {
  13. return property;
  14. }
  15. public void setProperty(String property) {
  16. this.property = property;
  17. }
  18. @Override
  19. public String toString() {
  20. return "MyBean{" +
  21. "property='" + property + '\'' +
  22. '}';
  23. }
  24. }

MyBeanEnhancer

  1. package com.cninfo.designpattern.structural.proxy.cglib;
  2. import net.sf.cglib.proxy.Enhancer;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;
  5. import java.beans.PropertyChangeListener;
  6. import java.beans.PropertyChangeSupport;
  7. import java.lang.reflect.Method;
  8. import java.lang.reflect.Modifier;
  9. import java.util.concurrent.CountDownLatch;
  10. /**
  11. * @author chenxinwei
  12. * @date 2021/5/13 18:17
  13. **/
  14. public class MyBeanEnhancer implements MethodInterceptor {
  15. private PropertyChangeSupport propertyChangeSupport;
  16. public void addPropertyChangeListener(PropertyChangeListener listener) {
  17. propertyChangeSupport.addPropertyChangeListener(listener);
  18. }
  19. public void removePropertyChangeListener(PropertyChangeListener listener) {
  20. propertyChangeSupport.removePropertyChangeListener(listener);
  21. }
  22. public static Object newInstance(Class clazz) {
  23. MyBeanEnhancer interceptor = new MyBeanEnhancer();
  24. Enhancer enhancer = new Enhancer();
  25. enhancer.setSuperclass(clazz);
  26. enhancer.setCallback(interceptor);
  27. Object bean = enhancer.create();
  28. interceptor.propertyChangeSupport = new PropertyChangeSupport(bean);
  29. return bean;
  30. }
  31. @Override
  32. public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  33. Object retValFormSuper = null;
  34. try {
  35. if (!Modifier.isAbstract(method.getModifiers())) {
  36. retValFormSuper = methodProxy.invokeSuper(o, args);
  37. }
  38. } finally {
  39. String name = method.getName();
  40. if (name.equals("addPropertyChangeListener")) {
  41. addPropertyChangeListener((PropertyChangeListener) args[0]);
  42. } else if (name.equals("removePropertyChangeListener")) {
  43. removePropertyChangeListener((PropertyChangeListener) args[0]);
  44. }
  45. if (name.startsWith("set") &&
  46. args.length == 1 &&
  47. method.getReturnType() == Void.TYPE) {
  48. char propName[] = name.substring("set".length()).toCharArray();
  49. propName[0] = Character.toLowerCase(propName[0]);
  50. propertyChangeSupport.firePropertyChange(new String(propName), null, args[0]);
  51. }
  52. }
  53. return retValFormSuper;
  54. }
  55. public static void main(String[] args) {
  56. MyBean bean = (MyBean) newInstance(MyBean.class);
  57. bean.addPropertyChangeListener(System.out::println);
  58. bean.setProperty("TEST");
  59. CountDownLatch countDownLatch = new CountDownLatch(1);
  60. try {
  61. countDownLatch.await();
  62. } catch (InterruptedException e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. }

输出:

  1. java.beans.PropertyChangeEvent[propertyName=property; oldValue=null; newValue=TEST; propagationId=null; source=MyBean{property='TEST'}]

javassist

了解
Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。javassist简单易用, 快速。

JDK动态代理和CGLIB动态代理

  1. JDK动态代理只提供接口的代理,不支持类的代理。
  2. CGLIB 是一个代码生成类库,可以在运行时动态生成指定类的一个子类代理对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做动态代理,因此,如果某个类被标记为final,那么它无法使用CGLIG做动态代理
  3. 静态代理与动态代理的区别在于生成AOP对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理。而Spring AOP则无需特定的编译器处理。

直接使用ASM,要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以使用cglib是多数人的选择(够用即可)。

MyBatis为接口生成代理类的过程

代理模式 - 图4

代理模式 - 图5

  1. 初始化的时候最终会调到 org.apache.ibatis.binding.MapperRegistry#``addMapper方法
  2. addMapper方法将new MapperProxyFactory<>(type) 存入到MapperRegistry的变量knowMappers
  3. new MapperProxyFactory<>(type)负责为type接口生成代理类,具体参见 org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)
  4. newInstance中,使用实现了InvocationHandler接口的MapperProxy类,负责具体处理,org.apache.ibatis.binding.MapperProxy#invoke是代理逻辑的核心所在。最终会调用mapperMethod``.execute(``sqlSession``**, **``args)方法。

mapperMethod``.execute(``sqlSession``**, **``args)方法,最终会将交由SqlSession执行具体的数据库操作

  1. public Object execute(SqlSession sqlSession, Object[] args) {
  2. Object result;
  3. switch (command.getType()) {
  4. case INSERT: {
  5. Object param = method.convertArgsToSqlCommandParam(args);
  6. result = rowCountResult(sqlSession.insert(command.getName(), param));
  7. break;
  8. }
  9. case UPDATE: {
  10. Object param = method.convertArgsToSqlCommandParam(args);
  11. result = rowCountResult(sqlSession.update(command.getName(), param));
  12. break;
  13. }
  14. case DELETE: {
  15. Object param = method.convertArgsToSqlCommandParam(args);
  16. result = rowCountResult(sqlSession.delete(command.getName(), param));
  17. break;
  18. }
  19. case SELECT:
  20. if (method.returnsVoid() && method.hasResultHandler()) {
  21. executeWithResultHandler(sqlSession, args);
  22. result = null;
  23. } else if (method.returnsMany()) {
  24. result = executeForMany(sqlSession, args);
  25. } else if (method.returnsMap()) {
  26. result = executeForMap(sqlSession, args);
  27. } else if (method.returnsCursor()) {
  28. result = executeForCursor(sqlSession, args);
  29. } else {
  30. Object param = method.convertArgsToSqlCommandParam(args);
  31. result = sqlSession.selectOne(command.getName(), param);
  32. if (method.returnsOptional()
  33. && (result == null || !method.getReturnType().equals(result.getClass()))) {
  34. result = Optional.ofNullable(result);
  35. }
  36. }
  37. break;
  38. case FLUSH:
  39. result = sqlSession.flushStatements();
  40. break;
  41. default:
  42. throw new BindingException("Unknown execution method for: " + command.getName());
  43. }
  44. if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  45. throw new BindingException("Mapper method '" + command.getName()
  46. + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  47. }
  48. return result;
  49. }
  50. // case SELECT方法中的 method.returnsMany()
  51. private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
  52. List<E> result;
  53. Object param = method.convertArgsToSqlCommandParam(args);
  54. if (method.hasRowBounds()) {
  55. RowBounds rowBounds = method.extractRowBounds(args);
  56. result = sqlSession.selectList(command.getName(), param, rowBounds);
  57. } else {
  58. result = sqlSession.selectList(command.getName(), param);
  59. }
  60. // issue #510 Collections & arrays support
  61. if (!method.getReturnType().isAssignableFrom(result.getClass())) {
  62. if (method.getReturnType().isArray()) {
  63. return convertToArray(result);
  64. } else {
  65. return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
  66. }
  67. }
  68. return result;
  69. }
  1. public class MapperMethod {
  2. private final SqlCommand command;
  3. private final MethodSignature method;
  4. // ...
  5. }
  6. public static class SqlCommand {
  7. private final String name; // 接口全限名
  8. private final SqlCommandType type; //sql类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
  9. // ...
  10. }
  11. public static class MethodSignature {
  12. private final boolean returnsMany;
  13. private final boolean returnsMap;
  14. private final boolean returnsVoid;
  15. private final boolean returnsCursor;
  16. private final boolean returnsOptional;
  17. private final Class<?> returnType;
  18. private final String mapKey;
  19. private final Integer resultHandlerIndex;
  20. private final Integer rowBoundsIndex;
  21. private final ParamNameResolver paramNameResolver;
  22. // ...
  23. }