大家好,我是田哥,昨天一哥们面试被问到代理模式,刚好,我也正在写《MyBatis源码分析:小白系列》专栏中的代理模式。

这里也说明一下,本文是MyBatis源码分析专栏中的一篇文章。

感兴趣的可以扫描了解一下我的《MyBatis源码分析:小白系列》:

本文目录:

两个有趣故事

老田买火车票

买火车票已经是很老的话题了。老田以前每年都会坐绿皮车回家过年,幸运的是这两年老家市区也有高铁了,少了些许奔波。现在就来回忆下当年的情景:每到年底最头疼的就是抢火车票,每次都需要去火车站排队买票。因为只要网上的票一开始出售,基本上都是手慢无,最后就只能在周末时去火车站买票了。但是,有一次无意间听说黄牛可以帮忙买票,只是要付点手续费。于是,后面每当自己抢不到票时,都会走黄牛,费用也不是很高。相比自己苦逼到火车站买票,还是轻松很多的。

ok,故事就讲到这里,下面再使用java代码来实现。

未知黄牛党之前

//火车站
public interface TrainStationService {//坐公交去火车站void byBus();//排队买票void lineUp();/*** 买票* @param idCard  身份证* @param address 去往地址* @param date    日期*/void buy(String idCard, String address, String date);
}
//绿皮车站(老火车站)
public class GreenSkinTrainStationServiceImpl implements TrainStationService {@Overridepublic void byBus() {System.out.println("坐公交去火车站");}@Overridepublic void lineUp() {System.out.println("在火车站苦逼的排队中....");}@Overridepublic void buy(String idCard, String address, String date) {System.out.println(idCard + "买到了一张通往" + address + "的绿皮车票,乘坐日期:" + date);}
}
//Client可以理解为老田
public class Client {public static void main(String[] args) {TrainStationService trainStationService = new GreenSkinTrainStationServiceImpl();trainStationService.byBus();trainStationService.lineUp();trainStationService.buy("423268199901011234", "老家", "2019年2月1日");}
}

经过一系列的折腾,老田的结果:

坐公交去火车站 在火车站苦逼的排队中.... 423268199901011234 买到了一张通往老家的绿皮车票,乘坐日期:2019年2月1日

知道黄牛党以后

//黄牛党
public class CattlePerson implements TrainStationService{private TrainStationService trainStationService;public CattlePerson() {this.trainStationService = = new GreenSkinTrainStationServiceImpl();}@Overridepublic void byBus() {trainStationService.byBus();}@Overridepublic void buy(String idCard, String address, String date) {System.out.println("收手续费");this.byBus();this.lineUp();trainStationService.buy(idCard, address, date);System.out.println("黄牛党把买到的票给老田");}@Overridepublic void lineUp() {trainStationService.lineUp();}}
//Client可以理解为老田
public class Client {public static void main(String[] args) {CattlePerson cattlePerson=new CattlePerson();cattlePerson.buy("423268199901011234", "老家", "2019年2月1日");}
}

最后老田买车票结果是这样的:

黄牛党收手续费 坐公交去火车站 在火车站苦逼的排队中.... 423268199901011234 买到了一张通往老家的绿皮车票,乘坐日期:2019年2月1日 黄牛党把买到的票给老田

最终老田还是搞到票了,但是不用折腾自己了。

老田玩王者农药

尽管前两年,乃至现在王者荣耀还是挺火的。也许有的人没玩过,但是你绝对见过别人玩过。民间传说“玩Dato的看不起玩英雄联盟的,然而玩英雄联盟的看不起玩王者荣耀”。哈哈哈,笑笑就可以了,管那么多,自己开心就好。这两个游戏本人都玩过,但是没有很投入。

但是去年在同事老王的带领下搞起了王者荣耀,疯狂的时候可以玩通宵,大号不能玩了玩小号,小号不行就换 QQ 号......一直想上王者,悲哀的是一直没上,记得最疯狂的时候还是到了差一颗星就到星耀二,但是始终是上不了王者。无意间看到一个牛逼的玩家,说给他 388 块就能上王者,才发现原来可以找人代打的,后面我细爬了一下,更恐怖的是有专门代打游戏的公司。由此可知,或许很多王者都是别人带着或者代打上去的吧。

下面用java来实现上面的两种常见:

老田玩王者农药

public interface PlayerService {//登录void login();//挑战排位赛void challenge();//升级void upgrade();
}
public class PlayerServiceImpl implements PlayerService {private String userName;private String password;public PlayerServiceImpl(String userName, String password) {this.userName = userName;this.password = password;}@Overridepublic void login() {//校验用户名和密码System.out.println("用户:" + this.userName + ",密码:" + this.password);System.out.println(this.userName + "登录了");}@Overridepublic void challenge() {System.out.println("挑战排位赛比赛中...");}@Overridepublic void upgrade() {System.out.println(this.userName + "又升级了");}
}//Client当做老田
public class Client{public static void main(String[] args) {PlayerService playerService = new PlayerServiceImpl("老田", "123456");playerService.login();playerService.challenge();playerService.upgrade();}
}

老田玩王者农药情况:

用户:老田,密码:123456 老田登录了 挑战排位赛比赛中... 老田又升级了

老田找人代打上王者

也是要使用老田的账号登录,也是要一场一场的排位,最后达上王者。不知道能代打游戏之前老田是自己傻傻的一场一场打的。

//替人打游戏的人或公司
public class PlayerProxy implements PlayerService {private PlayerService playerService;/*** 费用*/private BigDecimal fee;private String userName;private String password;public PlayerProxy(String userName, String password, BigDecimal fee) {this.playerService = new PlayerServiceImpl(userName, password);this.fee = fee;this.userName = userName;this.password = password;}@Overridepublic void login() {playerService.login();}@Overridepublic void player() {System.out.println("代理商赚取手续费" + fee);this.login();playerService.player();this.upgrade();}@Overridepublic void upgrade() {playerService.upgrade();}
}
//老王
public class Client {public static void main(String[] args) {//告诉代理商或者代打的人账户名+密码+费用PlayerProxy playerProxy = new PlayerProxy("老田", "123456", BigDecimal.valueOf(100));playerProxy.player();}
}

最后老田只要等着别人给他打好上王者。

代理商收手续费100 用户:老田,密码:123456 老田登录了 挑战排位赛比赛中... 老田满五星升级

上面两个故事中分别有两个很重要的角色:黄牛党和代打游戏的人。

有黄牛党后老田不用关心票是怎么买的、有了代打游戏的人以后老田也不用关系是怎么上王者的,都全权交给他们去干。

同样的生活中的例子:相亲找媒婆,租房子找中介等,都是中间有个代你办事干活的人。

以上举的例子就是传说中的代理模式。

代理模式的定义

Provide a surrogate or placeholder for another object to control access to it.

代理模式就是由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式是GOF的23种设计模式之一(结构型模式)。

代理模式也叫委托模式,它是一种基本设计技巧,许多其他的设计模式,比如:状态模式,策略模式,访问者模式本质上是在更特殊的场合采用了委托模式。

角色

Subject:抽象主题,抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,没有特殊要求。

RealSubject:真实主题,也叫被委托角色,被代理角色。他是业务逻辑的具体执行者。

Proxy:代理,也叫委托类,代理类。它负责对真实角色的应用,把所有抽象主题定义的方法限制委托给真实主题去实现,并且在真实主题角色处理完毕的前后做预处理和善后处理工作。

通用UML

通用java代码

以上三个角色的java代码实现

public interface Subject {void request();
}
public class RealSbject implements Subject {@Overridepublic void request() {System.out.println("访问具体主题角色方法...");}
}
public class Proxy implements Subject {private RealSubject realSubject;public Proxy() {this.realSubject = new RealSubject();}@Overridepublic void request() {preRequest();realSubject.request();postRequest();}public void preRequest() {System.out.println("访问真实主题之前的预处理。");}public void postRequest() {System.out.println("访问真实主题之后的后续处理。");}
}
public class ProxyClient {public static void main(String[] args) {Proxy proxy=new Proxy();proxy.request();}
}

运行结果:

访问真实主题之前的预处理。访问具体主题角色方法... 访问真实主题之后的后续处理。

优缺点

优点:效率高,只要获取代理对象并执行就结束了

缺点:如果接口增加新方法,被代理类得改,代理也得改;每一个被代理类都得有一个代理类。如果系统很大,那么关于代理类的维护的代价是很大的。

装饰器模式

装饰器模式是结构性模式之一,装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。也可以称之为包装模式

特点:动态添加或者覆盖被装饰的接口/抽象类行为。

关系:装饰者和被装饰者有着接口/抽象类层次关系。

角色

抽象组件(Component)角色:定义一个将要接收附加责任的类,即继承该抽象类的类都有了装饰和被装饰的能力。

具体组件(ConcreteComponent)角色:可以被动态加上新行为,被装饰者修饰的类。

装饰者(Decorator)角色:装饰者抽象类,继承该类都具有装饰者的能力。

具体装饰者(ConcreteDecorator)角色:为具体组件添加新行为。

通用UML图

通用java代码

//抽象构件
public abstract class Component {public abstract void operate();
}
//具体构件(最终被装饰的类)
public class ConcreteComponent extends Component {@Overridepublic void operate() {System.out.println("doSomething");}
}
//抽象装饰者
public class Decorator extends Component {private Component component;//通过构造函数传递被装饰者public Decorator(Component component) {this.component = component;}//委托给被装饰者执行@Overridepublic void operate() {this.component.operate();}
}
//具体装饰类
public class ConcreteDecorator extends Decorator {//定义被装饰者public ConcreteDecorator(Component component) {super(component);}private void before() {System.out.println("在调用operate方法前给你添加点东西");}@Overridepublic void operate() {//调用前就行装饰this.before();//调用具体被装饰类的方法super.operate();//调用后进行装饰this.after();}private void after() {System.out.println("在调用operate方法后给你添加点东西");}
}
//测试
public class Client {public static void main(String[] args) {Component component = new ConcreteComponent();Decorator decorator = new ConcreteDecorator(component);decorator.operate();}
}

运行结果:

在调用operate方法前给你添加点东西 doSomething 在调用operate方法后给你添加点东西

demo 案例

简单支付场景,请看代码实现

public interface PayService {//支付void pay();
}
//被装饰的类--支付场景
public class PayServiceImpl implements PayService {@Overridepublic void pay() {System.out.println("执行PayServiceImpl--的--支付--支付方法");}
}
public interface PayParamsMsgService extends PayService {//支付@Overridevoid pay();//发站内信void sendMsg();//参数校验void checkParams();
}
//装饰类
public class PayParamsMsgServiceImpl implements PayParamsMsgService {private PayService payService;public PayParamsMsgServiceImpl(PayService payService) {this.payService = payService;}//没有被装饰的支付=支付//装饰后的支付=支付前进行参数校验-->支付-->支付后发生信息@Overridepublic void pay() {checkParams();payService.pay();sendMsg();}@Overridepublic void sendMsg() {System.out.println("发送支付成功站内信");}@Overridepublic void checkParams() {System.out.println("校验余额是否足够,校验密码是否非法登录等");}
}
public class DemoClient {public static void main(String[] args) {PayService payService=new PayServiceImpl();PayService payService1=new PayParamsMsgServiceImpl(payService);payService1.pay();}
}

运行结果:

校验余额是否足够,校验密码是否非法登录等 执行PayServiceImpl--的--支付--支付方法 发送支付成功站内信

优缺点

优点

  • 装饰类和被装饰类可以独立发展,而不会相互耦合。说明白了就是Component类无须知道Decorator类,Decorator类是从外部来扩展Component,而Decorator也不知道具体的构件。

  • 装饰模式是继承关系的一种替代方案。咱们看装饰类,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。

  • 装饰模式可以动态扩展一个实现类的功能,这不需多说,装饰模式定义就是这么讲的。

缺点:多层装饰比较复杂。

使用场景

1、扩展一个类的功能

2、动态增加功能,动态撤销。

开发过程中,一般是针对老系统或者已经正常使用的功能之外添加一些新的功能。不建议新系统使用装饰器设计模式。

代理模式 VS 装饰器模式

这两个模式在实现上很容易陷入难分难解的地步,但是请记住他们的本质区别:装饰模式重在装饰、增强,而代理模式重在访问的权限控制。

如果你写了一个方法,后续考虑要给这个方法添加一些功能,可以选择装饰模式。如果你写了一堆方法,后续考虑统一给这些方法添加功能(比如日志记录,资源回收),可以选择代理模式。

静态代理

字面意义就是静态的代理模式,在上面的几个代理模式案例中,所有的代理类都是咱们自己手写的,所以是已经定好的,也就可以理解为是死的、静态的,由此可知上面案例都是静态代理模式。

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

代理模式就是静态代理模式。就是被代理对象和代理类都要实现同一个接口。

既然有静态代理,不妨假设一下,这个代理不需要咱们来写,那岂不就是动态代理吗?

动态代理

何为动态代理?

动态代理是在实现阶段不用关心代理谁,而是在运行阶段才指定代理哪一个对象。上面得案例中的代理类必须是自己创建的,人都知道偷懒,所以完全不想写那么多代码了(代理类),所以得动态生成。JDK动态代理能解决这个问题;

JDK动态代理

JDK动态代理是一种代理模式实现之一,只能代理接口。

通用java代码实现
//第一步
public interface Subject {void request();
}
//第二步
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("访问具体主题角色方法...");}
}
//第三步
public class JDKDynamicProxy implements InvocationHandler {/*** 将目标对象传入进行代理*/private Object target;public JDKDynamicProxy(Object target) {this.target = target;}/*** 获取被代理接口实例对象*/public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}/***实现增强逻辑和对真实对象方法的调用*@param proxy 生成的动态代理对象*@param method 动态代理在客户端执行的方法*@param args 该方法的参数列表*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result = method.invoke(target, args);after();return result;}private void before() {//方法执行前System.out.println("方法执行前");}private void after() {//方法执行后System.out.println("方法执行后");}
}
//第四步
public class Client {public static void main(String[] args) {//保存生成的代理类的字节码文class文件:$Proxy0.class//下面会细说$Proxy0.classSystem.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Subject target = new RealSubject();JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(target);Subject proxy = jdkDynamicProxy.getProxy();proxy.request();}
}

运行结果:

方法执行前 访问具体主题角色方法... 方法执行后

现在有了JDKDynamicProxy,那么我们就再也不需要手动写代理类了,而是交给JDK去帮我们自动创建。细心的人你应该会发现JDK的动态代理通用java代码和装饰模式几乎一毛一样,但是请记住他们的目的不同,装饰器设计模式是给一个对象动态的增加方法(功能),而动态代理的目的是控制被代理对象的访问权。

JDK动态代理四大步骤
  1. 定义业务接口

  2. 被代理对象实现业务接口

  3. 通过Proxy的静态方法newProxyInstance( ClassLoader loader, Class[] interfaces, InvocationHandler h)创建一个代理对象

  4. 使用代理对象

JDK动态代理源码分析

JDK动态代理四个步骤中最让人迷惑的就是第三步,第三步到底做了什么?(源码分析挺乏味的,但是请记住你不读源码很多东西永远只停留在了解或者皮毛阶段,阅读源码至少会让你知道怎么写出高质量的代码)

进入java.lang.reflect.Proxy类中的

public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,                                            InvocationHandler h) throws IllegalArgumentException {// 判断参数h是否为空,咱们这里的h=new JDKDynamicProxy()Objects.requireNonNull(h);//接口数组先拷贝一份final Class<?>[] intfs = interfaces.clone();//安全检查final SecurityManager sm = System.getSecurityManager();if (sm != null) {//检查创建代理类所需的权限checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}//最关键的地方//查询(在缓存中已经有)或生成指定的代理类的class对象。后面细说Class<?> cl = getProxyClass0(loader, intfs);try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}//用invocationHandler生成构造函数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);}
}
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {限定代理的接口不能超过65535个if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类return proxyClassCache.get(loader, interfaces);}//Proxy的静态内部类ProxyClassFactory
private static final class ProxyClassFactoryimplements 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();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) {//校验类加载器是否能通过接口名称加载该类Class<?> interfaceClass = null;try {//根据接口全限定类名和classLoader,获取到接口class对象interfaceClass = Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}//如果两次接口class对象不一致,直接抛出异常,说明创建错误if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}//校验该类是否是接口类型,这里就是证明为什么JDK动态代理是代理接口的。if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}//if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}//代理类包名String proxyPkg = null;     int accessFlags = Modifier.PUBLIC | Modifier.FINAL;//非public接口,代理类的包名与接口的包名相同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");}}}//如果都是public接口设定全限定类名com.sun.proxy.if (proxyPkg == null) {//public代理接口,使用ReflectUtil.PROXY_PACKAGE=com.sun.proxy.包名proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}//为代理类生成名字long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;//真正生成代理类的字节码文件的地方:ProxyGenerator类中byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {//使用类加载器将代理类的字节码文件加载到JVM中return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}}}    //ProxyGenerator类中public static byte[] generateProxyClass(final String var0, Class<?>[] var1, 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");}//如果找不到生成$Proxy0.class可以write方法一直追中下去,将会看到具体的保存路径//WindowsFileSystemProviderFiles.write(var2, var4, new OpenOption[0]);return null;} catch (IOException var4x) {throw new InternalError("I/O exception saving generated file: " + var4x);}}});}return var4;}
//具体生成class字节码文件的方法private byte[] generateClassFile() {//将object类当中的 hashcode,equals,toString方法添加到动态代理类当中//addProxyMethod方法后面有分析this.addProxyMethod(hashCodeMethod, Object.class);this.addProxyMethod(equalsMethod, Object.class);this.addProxyMethod(toStringMethod, Object.class);Class[] var1 = this.interfaces;int var2 = var1.length;int var3;Class var4;//遍历父接口数据for(var3 = 0; var3 < var2; ++var3) {var4 = var1[var3];//获取每个接口当中的方法Method[] var5 = var4.getMethods();int var6 = var5.length;//遍历接口当中的方法,将接口当中的方法都添加至动态代理类当中for(int var7 = 0; var7 < var6; ++var7) {Method var8 = var5[var7];this.addProxyMethod(var8, var4);}}Iterator var11 = this.proxyMethods.values().iterator();//检查代理类当中的返回类型List var12;while(var11.hasNext()) {var12 = (List)var11.next();checkReturnTypes(var12);}Iterator var15;try {// 将构造方法添加至代理类当中的方法集合中this.methods.add(this.generateConstructor());var11 = this.proxyMethods.values().iterator();//遍历代理类当中的方法,此处使用两层循环,是因为方法签名相同的,可能有多个方法while(var11.hasNext()) {var12 = (List)var11.next();var15 = var12.iterator();while(var15.hasNext()) {ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));this.methods.add(var16.generateMethod());}}// 将静态代码块添加进去this.methods.add(this.generateStaticInitializer());} catch (IOException var10) {throw new InternalError("unexpected I/O Exception", var10);}//方法个数不能超过65535if (this.methods.size() > 65535) {throw new IllegalArgumentException("method limit exceeded");} else if (this.fields.size() > 65535) {//属性不能超过65535throw new IllegalArgumentException("field limit exceeded");} else {//编写最终类文件//在开始编写最终类文件之前,确保为下面的项目保留常量池索引this.cp.getClass(dotToSlash(this.className));this.cp.getClass("java/lang/reflect/Proxy");var1 = this.interfaces;var2 = var1.length;for(var3 = 0; var3 < var2; ++var3) {var4 = var1[var3];this.cp.getClass(dotToSlash(var4.getName()));}//设置只读,在这之前不允许在常量池中增加信息,因为要写常量池表this.cp.setReadOnly();ByteArrayOutputStream var13 = new ByteArrayOutputStream();DataOutputStream var14 = new DataOutputStream(var13);try {//magic魔法数字var14.writeInt(-889275714);//次版本var14.writeShort(0);//主版本var14.writeShort(49);this.cp.write(var14);//访问表示var14.writeShort(this.accessFlags);//本类名称var14.writeShort(this.cp.getClass(dotToSlash(this.className)));var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));//接口var14.writeShort(this.interfaces.length);Class[] var17 = this.interfaces;int var18 = var17.length;for(int var19 = 0; var19 < var18; ++var19) {Class var22 = var17[var19];var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));}//字段var14.writeShort(this.fields.size());var15 = this.fields.iterator();while(var15.hasNext()) {ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();var20.write(var14);}//方法var14.writeShort(this.methods.size());var15 = this.methods.iterator();while(var15.hasNext()) {ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();var21.write(var14);}//类文件属性:对于代理类来说没有类文件属性(no ClassFile attributes for proxy classes)var14.writeShort(0);return var13.toByteArray();} catch (IOException var9) {throw new InternalError("unexpected I/O Exception", var9);}}}//给代理类添加方法private void addProxyMethod(Method var1, Class<?> var2) {//方法名String var3 = var1.getName();//方法参数类型Class[] var4 = var1.getParameterTypes();//返回值类型Class var5 = var1.getReturnType();//异常类型Class[] var6 = var1.getExceptionTypes();//方法签名String var7 = var3 + getParameterDescriptors(var4);//根据方法签名却获得proxyMethods的ValueObject var8 = (List)this.proxyMethods.get(var7);//处理多个代理接口中重复的方法的情况if (var8 != null) {Iterator var9 = ((List)var8).iterator();while(var9.hasNext()) {ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();if (var5 == var10.returnType) {//归约异常类型以至于让重写的方法抛出合适的异常类型,我认为这里可能是多个接口中有相同的方法,//而这些相同的方法抛出的异常类型又不同,//所以对这些相同方法抛出的异常进行了归约ArrayList var11 = new ArrayList();collectCompatibleTypes(var6, var10.exceptionTypes, var11);collectCompatibleTypes(var10.exceptionTypes, var6, var11);var10.exceptionTypes = new Class[var11.size()];//将ArrayList转换为Class对象数组var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);return;}}} else {var8 = new ArrayList(3);this.proxyMethods.put(var7, var8);}((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));}

自此源码分析完毕。现在回过头去看自动生成的代理类里到底有什么东东?

package com.sun.proxy;import com.tian.swagger.proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//继承了Proxy,实现了Subject
public final class $Proxy0 extends Proxy implements Subject {private static Method m1;private static Method m2;private static Method m3;private static Method m0;//代理类的构造函数,其参数正是是InvocationHandler实例,//Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的public $Proxy0(InvocationHandler var1) throws  {super(var1);}public final boolean equals(Object var1) throws  {try {return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws  {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws  {try {return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}//上面三个方法对应的是hashCode、toString、equals//重写接口Subject中定义的方法requestpublic final void request() throws  {try {//h就是Proxy类中的变量protected InvocationHandler h;//this就是当前$Proxy0对象;//m3就是Class.forName("com.tian.swagger.proxy.Subject").getMethod("request", new Class[0]);//即是通过全路径名,反射获取的目标对象中的真实方法加参数super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}// 静态代码块对变量进行一些初始化工作static {try {//这里每个方法对象 和类的实际方法绑定m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.tian.swagger.proxy.Subject").getMethod("request");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

当代理对象生成后,最后由InvocationHandler的invoke()方法调用目标方法:在动态代理中InvocationHandler是核心,每个代理实例都具有一个关联的调用处理程序(InvocationHandler)。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序(InvocationHandler)的invoke()方法。所以对代理方法的调用都是通InvocationHadler的invoke来实现中,而invoke方法根据传入的代理对象,方法和参数来决定调用代理的哪个方法。具体方法签名如下:

invoke(Object Proxy,Method method,Object[] args)

从反编译源码分析调用invoke()过程:从反编译后的源码看$Proxy0类继承了Proxy类,同时实现了Subject接口,即代理类接口,所以才能强制将代理对象转换为Subject接口,然后调用$Proxy0中的request()方法。$Proxy0中request()源码:

//接口Subject中定义的放任requestpublic final void request() throws  {try {//h就是Proxy类中的变量protected InvocationHandler h;//this就是当前$Proxy0对象;//m3就是Class.forName("com.tian.swagger.proxy.Subject").getMethod("request", new Class[0]);//即是通过全路径名,反射获取的目标对象中的真实方法加参数//后面那个null是方法参数,因为Subject的方法request参数为空super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}

所以成功的调到了InvocationHandler中的invoke()方法,但是invoke()方法在我们自定义的JDKDynamicProxy中实现,JDKDynamicProxy中的invoke()方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result = method.invoke(target, args);after();return result;
}

终于回到咱们写的代码里了。不容易吧,简单JDK动态代理绕了这么大一圈。

通过以上分析应该收获:

1,JDK动态代理只能代理接口,源码里有判断,不是接口就直接抛异常

//校验该类是否是接口类型,这里就是证明为什么JDK动态代理是代理接口的。if (!interfaceClass.isInterface()) {throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface");}

2,代理类是如何生成的

3,代理类结构

4,是如何调到自己定义的invoke方法的

JDK动态代理源码总结

1,生成代理类$Proxy.class字节码

Proxy.newProxyInstance()-->getProxyClass()--->ProxyClassFactory.apply()--->ProxyGenerator.getGenerateProxyClass()

2,调用Constructor.newInstance()实例化

3,调用第2步生成的代理对象中的invoke方法,最后调到JDKDynamicProxy中的invoke方法,最后调到被代理对象的实现方法中

通过以上的分析我们知道了JDK动态代理的使用和原理,也领略Java动态代理的强大之处,但是不难看出来使用JDK动态代理也有着它的局限性,JDK动态代理是在JVM内部动态的生成class字节码对象(代理类.class),但是JDK动态代理只能针对接口进行操作,也就说它只适用于对接口的实现类去进行代理。因为有时候咱们希望对普通类进行代理,使用JDK动态代理就无法搞下去,于是另外一个强大的动态代理CGlib出现了,CGlib就能解决对普通类进行代理的问题。

CGlib动态代理

定义
Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.

CGlib(Code Generation Library)是一个开源项目;是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,它被AOP、测试、数据访问框架用于生成动态代理对象和拦截字段访问。

CGlib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类;CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理;所以CGlib可以为无接口的类直接做代理,当然有接口的类也是可以的并无影响。

pom

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.12</version>
</dependency>
Java代码
public class TicketConcrete {public void buy() {System.out.println("买到火车票了");}
}
public class CattlePersonMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("黄牛去买火车票");Object res = methodProxy.invokeSuper(o, objects);System.out.println("黄牛把火车票给老田");return res;}
}
public class Client {public static void main(String[] args) {CattlePersonMethodInterceptor cattlePerson = new CattlePersonMethodInterceptor();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(TicketConcrete.class);enhancer.setCallback(cattlePerson);TicketConcrete person = (TicketConcrete) enhancer.create();person.buy();}
}

运行结果:

黄牛去买火车票 买到火车票了 黄牛把火车票给老田

上面已经提到,CGlib库是基于ASM的上层应用。对于代理没有实现接口的类,CGlib非常实用。本质上来说,对于需要被代理的类,它只是动态生成一个子类以覆盖非final的方法,同时绑定钩子回调自定义的拦截器。值得说的是,它比JDK动态代理还要快。

CGlib 部分源码分析

cglib.jar包目录:

本文只关心proxy目录(CGlib动态代理相关主要类目录).

net.sf.cglib.core:底层字节码操作类;大部分与ASP相关。transform:编译期、运行期的class文件转换类。proxy:代理创建类、方法拦截类。reflect:更快的反射类、C#风格的代理类。util:集合排序工具类beans:JavaBean相关的工具类

源码从enhancer.create()开始看,因为这里返回的Object直接强转为TicketConcrete(被代理的类)

Enhancer类中

//这里没有具体什么
public Object create() {classOnly = false;argumentTypes = null;return createHelper();
}
//构建帮助器
private Object createHelper() {//提前验证回调类型和filterpreValidate();//生成keyObject key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,ReflectUtils.getNames(interfaces),filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),callbackTypes,useFactory,interceptDuringConstruction,serialVersionUID);//复制个当前keythis.currentKey = key;//使用key构建代理对象,个人认为这里代码其实可以直接写成 return super.create(key);//看得出来这里的create方法很关键,AbstractClassGenerator.create()Object result = super.create(key);return result;
}

AbstractClassGenerator类中

private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>();
protected Object create(Object key) {try {//获取类加载器AppClassLoaderClassLoader loader = getClassLoader();//这里和jdk动态代理类似,也用到了缓存,classloader为keyMap<ClassLoader, ClassLoaderData> cache = CACHE;ClassLoaderData data = cache.get(loader);//data==nullif (data == null) {synchronized (AbstractClassGenerator.class) {cache = CACHE;data = cache.get(loader);//data==nullif (data == null) {//没有数据则构建新缓存Map<ClassLoader, ClassLoaderData> newCache = new                         WeakHashMap<ClassLoader, ClassLoaderData>(cache);//关键点:这里生成了class并存入缓存data = new ClassLoaderData(loader);newCache.put(loader, data);CACHE = newCache;}}}this.key = key;//从缓存中取数据Object obj = data.get(this, getUseCache());//根据返回类型不同,然后进行对应的实例化if (obj instanceof Class) {return firstInstance((Class) obj);}return nextInstance(obj);} catch (RuntimeException e|Error e|Exception e) {throw new CodeGenerationException(e);}}public ClassLoaderData(ClassLoader classLoader) {//类加载器是否存在if (classLoader == null) {throw new IllegalArgumentException("classLoader == null is not yet supported");}// 弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,//WeakReference无论内存是否充足,都会回收被弱引用关联的对象this.classLoader = new WeakReference<ClassLoader>(classLoader);Function<AbstractClassGenerator, Object> load =new Function<AbstractClassGenerator, Object>() {public Object apply(AbstractClassGenerator gen) {//生成Class对象Class klass = gen.generate(ClassLoaderData.this);return gen.wrapCachedClass(klass);}};generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);}
//生成Class对象
protected Class generate(ClassLoaderData data) {Class gen;Object save = CURRENT.get();CURRENT.set(this);try {ClassLoader classLoader = data.getClassLoader();if (classLoader == null) {throw new IllegalStateException("ClassLoader is null while trying to define class " + getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +  "Please file an issue at cglib's issue tracker.");}synchronized (classLoader) {//生成代理类的名字//com.tian.swagger.proxy.cglib.TicketConcrete$$FastClassByCGLIB$$56a92ac1//三部分组成:被代理对象的类全名+FastClassBYCGLIB+hashcodeString name = generateClassName(data.getUniqueNamePredicate());              data.reserveName(name);this.setClassName(name);}if (attemptLoad) {try {gen = classLoader.loadClass(getClassName());return gen;} catch (ClassNotFoundException e) {// ignore}}//根据策略生成不同的字节码byte[] b = strategy.generate(this);String className = ClassNameReader.getClassName(new ClassReader(b));ProtectionDomain protectionDomain = getProtectionDomain();//利用反射生成class对象synchronized (classLoader) { // just in caseif (protectionDomain == null) {gen = ReflectUtils.defineClass(className, b, classLoader);} else {gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);}}return gen;} catch (RuntimeException e|Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);} finally {CURRENT.set(save);}}
//生成字节码
public byte[] generate(ClassGenerator cg) throws Exception {DebuggingClassWriter cw = getClassVisitor();transform(cg).generateClass(cw);return transform(cw.toByteArray());
}
protected ClassGenerator transform(ClassGenerator cg) throws Exception {return cg;
}
public interface ClassGenerator {void generateClass(ClassVisitor v) throws Exception;
}

ClassGenerator实现类AbstractClassGeneratorAbstractClassGenerator的子类主要有

Enhancer
FastClass.Generator
KeyFactory.Generator

把生成的class文件保存,会生成以上三个class文件

public class Client {public static void main(String[] args) {//把生成的class文件保存,会生成以上三个class文件System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");long s=System.currentTimeMillis();CattlePersonMethodInterceptor cattlePerson = new CattlePersonMethodInterceptor();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(TicketConcrete.class);enhancer.setCallback(cattlePerson);TicketConcrete person = (TicketConcrete) enhancer.create();person.buy();System.out.println(System.currentTimeMillis()-s);}
}

最终生成相关的三个Class

com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f
com.tian.swagger.proxy.cglib.TicketConcrete$$FastClassByCGLIB$$56a92ac1
com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f$$FastClassByCGLIB$$ba31b84d

重点关注TicketConcrete$$EnhancerByCGLIB$$e39d634f文件中主要内容:

package com.tian.swagger.proxy.cglib;import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//继承了TicketConcrete,证明了CGlib是针对被代理类进行生成一个子类来实现代理的
//和jdk动态代理不一样,动态代理是集成Proxy类实现了我们的接口,
//而cglib是集成了我们的类重写了父类中的方法
public class TicketConcrete$$EnhancerByCGLIB$$e39d634f extends TicketConcreteimplements Factory {private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$buy$0$Method;private static final MethodProxy CGLIB$buy$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];//var0为代理对象com.tian.swagger.proxy.cglib.TicketConcreteClass var0 = Class.forName("com.tian.swagger.proxy.cglib.TicketConcrete$$EnhancerByCGLIB$$e39d634f");Class var1;Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());CGLIB$equals$1$Method = var10000[0];CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = var10000[1];CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = var10000[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = var10000[3];CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");//被代理的方法被代理方法buy()CGLIB$buy$0$Method = ReflectUtils.findMethods(new String[]{"buy", "()V"}, (var1 = Class.forName("com.tian.swagger.proxy.cglib.TicketConcrete")).getDeclaredMethods())[0];//代理方法CGLIB$buy$0$Proxy = MethodProxy.create(var1, var0, "()V", "buy", "CGLIB$buy$0");}//这个方法在fastClass中调用,此调用是执行真正的逻辑final void CGLIB$buy$0() {super.buy();}//方法重写public final void buy() {//判断目标类是否有设置回调:enhancer.setCallback(this);MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (this.CGLIB$CALLBACK_0 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}//设置了方法的回调则调用拦截器方法interceptif (var10000 != null) {var10000.intercept(this, CGLIB$buy$0$Method, CGLIB$emptyArgs, CGLIB$buy$0$Proxy);} else {super.buy();}}public static MethodProxy CGLIB$findMethodProxy(Signature var0) {//省略相关的代码}//省略调equals hashCode clone等方法
}
CGlib总结
  • 首先生成代理对象。【创建增强类enhancer,设置代理类的父类,设置回调拦截方法,返回创建的代理对象】

  • 调用代理类中的方法。【这里调用的代理类中的方法实际上是重写的父类的拦截。重写的方法中会去调用intercept方法】

  • 调用intercept,方法中会对调用代理方法中的invokeSuper方法。而在invokeSuper中维护了一个FastClassInfo类,其包含四个属性字段,分别为FastClass f1(目标类);FastClass f2 (代理类); int i1(目标类要执行方法的下标); int i2(代理类要执行方法的下标); invokeSuper中会调用的为代理类中的对应方法(代理类继承父类的时候,对于其父类的方法,自己会生成两个方法,一个是重写的方法,一个是代理生成的方法,这里调用的即是代理生成的方法)

  • 调用代理类中的代理方法,代理方法中通过super.method来真正地调用要执行被代理类的方法。

JDK VS CGlib

JDK动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;JDK动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。

动态代理典型场景的应用

Mybatis中的使用场景

Mybatis应该算持久化框架中使用率最多之一,细心的小伙伴有没有发现,比如我们在使用UserInfoMapper.java是接口,接口不是能实例化的,那为什么我们还能注入呢?

@Resourceprivate UserInfoMapper userInfoMapper;

为什么还能userMapper.insert()这么使用呢?先看看Mybatis没有集成Spring是如何实例化的。

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" /><property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

上面的MapperFactoryBean就可以实例化出mapperInterface类型的Bean,因为MapperFactoryBean实现了FactoryBean接口的getObject方法,可以实例化出我们想要的Bean,实际上是通过Jdk动态代理得到的Bean。

public T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);
}

调用方式:

SqlSession session = sqlSessionFactory.openSession();
try {  User user= (User) session.selectOne("org.mybatis.example.UserMapper.selectBlog", 1);
} finally {  session.close();
}

上面的UserMapper接口是一个个的配置来实例化的,每次需要openSession,close Session,重复工作太多,这样肯定比较麻烦,下面看看项目中常用的Mybatis与Spring集成的配置方式。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="mapperLocations" value="classpath*:com/**/mapper/**/*Mapper*.xml" /><property name="dataSource" ref="dataSource" />
</bean>

这个SqlSessionFactoryBean会做哪些事呢,主要是把Mapper.xml文件与Mapper.java加载进来,根据namespace加载对应的接口类到MapperRegistry,把方法名与Mapper.xml里的Select id对应起来等等。MapperRegistry相当于是一个缓存,后面创建代理对象是会用到。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="annotationClass" value="javax.annotation.Autowire"></property> <property name="basePackage" value="com.***.mapper" /><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

这里的值设置,只要是注解就行,不一定是Autowird,它代表只对有注解的接口类创建代理对象,否则,对basePackage下所有接口创建代理对象。

MapperScannerConfigurer可以对basePackage下所有Mapper接口创建代理对象,而不是像上面一个个配置。因为他实现了接口BeanDefinitionRegistryPostProcessor,它就是用来自定义Bean的,看看源码它是如何实现的。

上面设置mapperInterface,BeanClass其实就是上面讲的Mybatis没有集成Spring的配置方式。

我们知道了代理对象是通过MapperFactoryBean创建的。具体看看是如何创建的。

根据接口类创建代理对象:

/*** {@inheritDoc}*/public T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}

这里就用到了之前说的MapperRegistry,只是用来校验该接口是否存在。这里又用到了MapperProxy,是一个代理类,实现了InvocationHandler接口。

public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {ClassLoader classLoader = mapperInterface.getClassLoader();Class<?>[] interfaces = new Class[]{mapperInterface};MapperProxy proxy = new MapperProxy(sqlSession);return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}final Class<?> declaringInterface = findDeclaringInterface(proxy, method);final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);final Object result = mapperMethod.execute(args);if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}

当我们调用Mpper接口方法时,会被MapperProxy代理类拦截调用,主要调用实现就是mapperMethod.execute(args),看看底层实现。

public Object execute(Object[] args) {Object result = null;if (SqlCommandType.INSERT == type) {Object param = getParam(args);result = sqlSession.insert(commandName, param);} else if (SqlCommandType.UPDATE == type) {Object param = getParam(args);result = sqlSession.update(commandName, param);} else if (SqlCommandType.DELETE == type) {Object param = getParam(args);result = sqlSession.delete(commandName, param);} else if (SqlCommandType.SELECT == type) {if (returnsVoid && resultHandlerIndex != null) {executeWithResultHandler(args);} else if (returnsMany) {result = executeForMany(args);} else if (returnsMap) {result = executeForMap(args);} else {Object param = getParam(args);result = sqlSession.selectOne(commandName, param); //跟上面没有集成Spring调用方式一样的。}} else {throw new BindingException("Unknown execution method for: " + commandName);}return result;}

以上就是代理模式、动态代理(JDK、CGLib)、MyBatis中的动态代理的使用。

好了,就这么多了。

推荐阅读:

MySQL一共可以创建多少张表

出乎意料,刷新认知:读者朋友们藏龙卧虎

欢迎关注微信公众号:互联网全栈架构,收取更多有价值的信息。

面试官:策略模式和代理模式有什么区别?相关推荐

  1. 装饰器模式和代理模式的区别

    转载自 装饰器模式和代理模式的区别 学习AOP时,教材上面都说使用的是动态代理,可是在印象中代理模式一直都是控制访问什么的,怎么又动态增加行为了,动态增加行为不是装饰器模式吗?于是找了很多资料,想弄清 ...

  2. 装饰器模式与代理模式的区别_JS设计模式(三):装饰器模式、代理模式

    Do more 做的更多,比你上级给你安排的任务! 前言 在<不止代码>中提到了几个程序员典型的思维误区: 「 拜大牛为师 」「 业务代码一样很牛逼 」「 上班太忙没时间学习 」 我之前也 ...

  3. 什么是工厂模式、代理模式?

    什么是工厂模式.代理模式? 工厂模式 :实例化对象,用工厂方法代替new操作的一种模式. 代理模式 : 代理 :分为静态代理和动态代理:现有对象不能满足正常的开发需求,需要有一个能够代替现有对象提供服 ...

  4. 从一碗小米粥谈装饰器模式,代理模式的区别

    代理与装饰器 场景描述 代理即代替意思,可替代原类的所有功能,即和原类实现相同的规范.代理模式和装饰器模式很像. 每天清晨起来,紧张的洗漱之后,来到楼下的早餐店,我通常都会要一碗小米粥,这时候盛粥的大 ...

  5. Java设计模式(四):结构性模式(适配器模式、桥接模式、装饰模式、组合模式、外观模式、亨元模式、代理模式)

    目录 一· 适配器设计模式 1.1 现实生活中的适配器例子 1.2 基本介绍 1.3 工作原理 1.4 类适配器模式 1.5 对象适配器模式 1.6 接口适配器模式 1.7 适配器模式在 Spring ...

  6. 中介者模式、代理模式和外观模式的Pk

       在学习设计模式的时候,发现这三个模式在一定程度上很是相似.所以总结一下,加以区分. (一)中介者模式.     所谓中介,在我们生活中很是常见,我们买房子可以有中介公司,找兼职也可以有中介公司. ...

  7. 适配器模式(包装模式)和代理模式的区别

    适配器模式(包装模式)和代理模式的区别 一.简介 适配器模式:适配器模式(英语:adapter pattern)有时候也称包装样式或者包装.将一个类的接口转接成用户所期待的.一个适配使得因接口不兼容而 ...

  8. 设计模式之结构型模式:适配器模式、桥接模式、组合模式、装饰器模式、代理模式、

    文章目录 什么是结构型模式 适配模式 适配器的数据结构 适配器的实现 缺省适配器 适配器优缺点 适配器模式的使用环境 桥接模式 桥接模式数据结构 桥接模式的实现 桥接模式和适配器模式的联用 桥接模式的 ...

  9. 装饰器模式和代理模式

    1.装饰器模式:动态地给一个对象添加一些额外的职责.重点是调用者主动给目标对象动态新增功能 2.代理模式:为其他对象提供一种代理以控制对这个对象的访问.重点是调用者不用知道目标对象是什么,是怎么创建的 ...

最新文章

  1. Only POT texture can be compressed to PVRTC format
  2. 菜鸟Vue学习笔记(三)
  3. 【直播】王茂霖:二手车交易价格预测 Baseline 提高(河北高校数据挖掘邀请赛)
  4. 单调谐回路谐振放大器等效电路分析_谐振回路的工作原理
  5. 求n!,C(n,m)和A(n,m)最后的非零位。
  6. Linux学习笔记-消息队列概念
  7. elixir开发的项目_我对Elixir的介绍:学习另一种编程语言如何使您成为更好的开发人员...
  8. Java 数组转成集合List三种方法和(数组、集合List、Set相互转换)
  9. 2017.6.27 树上操作 思考记录
  10. java拦截器跳转页面跳转页面跳转_java Struts2 在拦截器里的跳转问题
  11. ftok() Linux Linux函数
  12. 基于mysql的全文索引
  13. PHP重置static静态变量
  14. IATF16949:2016汽车质量管理体系认证办理流程
  15. es 修改拼音分词器源码实现汉字/拼音/简拼混合搜索时同音字不匹配
  16. 【wp_code】快递简写列表,erp接口 ,淘宝erp接口,拼多多erp接口,京东erp接口,抖音erp接口,发货接口,打单接口
  17. 阿里巴巴国际站详情页怎么装修热点链接一张图片多个链接代码询盘链接和客服代码制作教程装修代码
  18. 创业者防坑手册:面对强大的资本力量,你该如何正当防卫?
  19. OpenCV空间人工智能竞赛:第一部分
  20. linux 命令:less详解

热门文章

  1. 在Word2010中交叉引用插入参考文献
  2. 单独按戴尔笔记本f11键和f12键无法调节亮度了怎么办?用(Fn+F11键或者Fn+F12键就好)
  3. 计算机视觉之姿态识别(原理+代码实操)
  4. 泛型方法的定义与使用
  5. LTE学习:PHICH
  6. Ubuntu、stm32下的C程序各变量的分配地址分析
  7. 动态代理IP有什么用?
  8. python邮件管理
  9. Leetcode 684: 冗余连接 Redundant Connection
  10. 【 javascript】JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性