类图

代理模式
静态代理实现
PrintaPrintableble
public interface Printable {void setPrinterName(String name);String getPrinterName();void print(String str);}
Printer
import java.util.concurrent.TimeUnit;public class Printer implements Printable {private String name;public Printer(String name) {this.name = name;heavyJob("正在生成Printer的实例(" + name + ")");}public Printer() {heavyJob("正在生成Printer的实例(" + name + ")");}@Overridepublic void setPrinterName(String name) {this.name = name;}@Overridepublic String getPrinterName() {return name;}@Overridepublic void print(String str) {System.out.println("======" + name + "=====");System.out.println(str);}private void heavyJob(String msg) {System.out.println(msg);for (int i = 0; i < 5; i++) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.print(".");}System.out.println("结束");}}
PrinterProxy
public class PrinterProxy implements Printable {private String name;private Printer real;public PrinterProxy() {}public PrinterProxy(String name) {this.name = name;}@Overridepublic void setPrinterName(String name) {if (real != null) {real.setPrinterName(name);}this.name = name;}@Overridepublic String getPrinterName() {return name;}@Overridepublic void print(String str) {realize();real.print(str);}private void realize() {if (real == null) {real = new Printer(name);}}}
Printe>rPrPrinterProxyoxy
Main
public class Main {public static void main(String[] args) {Printable p = new PrinterProxy("Alice");System.out.println("现在的名字是:"+p.getPrinterName());p.setPrinterName("Bob");System.out.println("现在的名字是:"+p.getPrinterName());p.print("Hello, world.");}}
类图,如下所示:

不论setPrinterName方法和getPrinterName方法被调用多少次,都不会生成Printer类的实例。Printer类并不知道PrinterProxy类的存在,即Printer类并不知道自己到底是通过PrinterProxy被调用的还是直接被调用的。
动态代理
JDK动态代理
依据运行时托自省能力,在运行时动态获取类或者对象的信息
利用动态代理辅助打印 jdk二分搜索的执行轨迹
package com.cninfo.designpattern.structural.proxy.repeat02;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;import java.util.concurrent.CountDownLatch;/*** @author chenxinwei* @date 2021/5/13 17:11**/public class ProxyMain {private static final int COUNT = 1000;public static void main(String[] args) {Comparable[] values = new Comparable[COUNT];for (int i = 0; i < COUNT; i++) {Integer value = Integer.valueOf(i);values[i] = (Comparable) Proxy.newProxyInstance(null,value.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(value + "." + method.getName() + Arrays.toString(args));return method.invoke(value, args);}});}CountDownLatch countDownLatch = new CountDownLatch(1);System.out.println(Arrays.binarySearch(values, 264));try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}}
499.compareTo[264]249.compareTo[264]374.compareTo[264]311.compareTo[264]280.compareTo[264]264.compareTo[264]264
查看生成的代理类
使用arthas-boot.jar,下载地址参考:https://arthas.aliyun.com/doc/download.html
- 搜索包下的class,
sc com.cninfo.designpattern.structural.proxy.repeat02.* - 反编译class
jad com.cninfo.designpattern.structural.proxy.repeat02.ProxyMain$1

字节码动态代理
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
package com.cninfo.designpattern.structural.proxy.cglib;import java.beans.PropertyChangeListener;import java.io.Serializable;/*** @author chenxinwei* @date 2021/5/13 18:15**/public abstract class MyBean implements Serializable {String property;abstract public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener);abstract public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener);public String getProperty() {return property;}public void setProperty(String property) {this.property = property;}@Overridepublic String toString() {return "MyBean{" +"property='" + property + '\'' +'}';}}
MyBeanEnhancer
package com.cninfo.designpattern.structural.proxy.cglib;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.beans.PropertyChangeListener;import java.beans.PropertyChangeSupport;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.util.concurrent.CountDownLatch;/*** @author chenxinwei* @date 2021/5/13 18:17**/public class MyBeanEnhancer implements MethodInterceptor {private PropertyChangeSupport propertyChangeSupport;public void addPropertyChangeListener(PropertyChangeListener listener) {propertyChangeSupport.addPropertyChangeListener(listener);}public void removePropertyChangeListener(PropertyChangeListener listener) {propertyChangeSupport.removePropertyChangeListener(listener);}public static Object newInstance(Class clazz) {MyBeanEnhancer interceptor = new MyBeanEnhancer();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(interceptor);Object bean = enhancer.create();interceptor.propertyChangeSupport = new PropertyChangeSupport(bean);return bean;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object retValFormSuper = null;try {if (!Modifier.isAbstract(method.getModifiers())) {retValFormSuper = methodProxy.invokeSuper(o, args);}} finally {String name = method.getName();if (name.equals("addPropertyChangeListener")) {addPropertyChangeListener((PropertyChangeListener) args[0]);} else if (name.equals("removePropertyChangeListener")) {removePropertyChangeListener((PropertyChangeListener) args[0]);}if (name.startsWith("set") &&args.length == 1 &&method.getReturnType() == Void.TYPE) {char propName[] = name.substring("set".length()).toCharArray();propName[0] = Character.toLowerCase(propName[0]);propertyChangeSupport.firePropertyChange(new String(propName), null, args[0]);}}return retValFormSuper;}public static void main(String[] args) {MyBean bean = (MyBean) newInstance(MyBean.class);bean.addPropertyChangeListener(System.out::println);bean.setProperty("TEST");CountDownLatch countDownLatch = new CountDownLatch(1);try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}}
输出:
java.beans.PropertyChangeEvent[propertyName=property; oldValue=null; newValue=TEST; propagationId=null; source=MyBean{property='TEST'}]
javassist
了解
Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。javassist简单易用, 快速。
JDK动态代理和CGLIB动态代理
- JDK动态代理只提供接口的代理,不支持类的代理。
- CGLIB 是一个代码生成类库,可以在运行时动态生成指定类的一个子类代理对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做动态代理,因此,如果某个类被标记为final,那么它无法使用CGLIG做动态代理
- 静态代理与动态代理的区别在于生成AOP对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理。而Spring AOP则无需特定的编译器处理。
直接使用ASM,要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以使用cglib是多数人的选择(够用即可)。
MyBatis为接口生成代理类的过程

- 初始化的时候最终会调到
org.apache.ibatis.binding.MapperRegistry#``addMapper方法 addMapper方法将new MapperProxyFactory<>(type) 存入到MapperRegistry的变量knowMappers中new MapperProxyFactory<>(type)负责为type接口生成代理类,具体参见org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)newInstance中,使用实现了InvocationHandler接口的MapperProxy类,负责具体处理,org.apache.ibatis.binding.MapperProxy#invoke是代理逻辑的核心所在。最终会调用mapperMethod``.execute(``sqlSession``**, **``args)方法。
mapperMethod``.execute(``sqlSession``**, **``args)方法,最终会将交由SqlSession执行具体的数据库操作
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}// case SELECT方法中的 method.returnsMany()private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {List<E> result;Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);result = sqlSession.selectList(command.getName(), param, rowBounds);} else {result = sqlSession.selectList(command.getName(), param);}// issue #510 Collections & arrays supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result;}
public class MapperMethod {private final SqlCommand command;private final MethodSignature method;// ...}public static class SqlCommand {private final String name; // 接口全限名private final SqlCommandType type; //sql类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH// ...}public static class MethodSignature {private final boolean returnsMany;private final boolean returnsMap;private final boolean returnsVoid;private final boolean returnsCursor;private final boolean returnsOptional;private final Class<?> returnType;private final String mapKey;private final Integer resultHandlerIndex;private final Integer rowBoundsIndex;private final ParamNameResolver paramNameResolver;// ...}
