在介绍MyBatis动态代理前。我们先介绍一下什么是代理模式。
代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性。
代理模式结构图
动态代理
代理模式分为两种:静态代理和动态代理。静态代理是程序编译阶段确定代理类,而动态代理是在程序运行时确定代理类,代理类在程序运行时创建的代理方式被成为动态代理,动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
JDK自带的动态代理方式(必须有接口)
先实现一个接口和具体的实现类
#####接口
```
public interface IUser {
void talk();
}
```
#####实现类
```
public class User implements IUser {
@Override
public void talk() {
System.out.println("doing User.talk");
}
}
```
要是先动态代理,首先创建一个实现InvocationHandler这个接口的类
```
public class UserProxy implements InvocationHandler {
private Object object;
public UserProxy(Object object){
super();
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("doing UserProxy.invoke");
method.invoke(object, args);
System.out.println("doing UserProxy.invoke end");
return null;
}
/**
* 实现InvocationHandler接口要重写invoke方法,方法的三个参数分别:
* proxy:就是动态代理生成的代理类对象
* method:就是调用的方法
* args:表示该方法的参数
*/
}
```
在main函数中创建代理对象,通过该对象,调用委托类的方法,每个方法的调用JVM都会给我们调用上面实现
InvocationHandler接口的类对象的invoke方法
```
public static void main(String[] args) {
IUser user = (IUser) Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(), new Class[]{IUser.class}, new UserProxy(new User()));
user.talk();
}
```
输出结果
```
doing UserProxy.invoke
doing User.talk
doing UserProxy.invoke end
```
JDK动态代理原理
main中调用了代码user.talk()方法后,JVM会帮助自动实现invoke调用呢?在上面main添加一个监控
public static void main(String[] args) {
//可以查看JDK生成的动态代理类
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
IUser user = (IUser) Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(), new Class[]{IUser.class}, new UserProxy(new User()));
user.talk();
}
生成一个$Proxy().class文件,代理类的源码,public final class $Proxy0 extends Proxy implements IUser$Proxy()的定义,确实实现了IUser接口,和代理模式下的代理类完全一样,因此当user.talk()调用时,根据JAVA的多态原理,调用的应该是代理对象$Prox