Java中两种动态代理:
1、JDK自带的动态代理(mybatis使用的是JDK自带的 包路径:java.lang.reflet)
2、CGLib提供的动态代理
代理模式:
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性。
动态代理:
代理类在程序运行时创建的代理方式被称为动态代理
动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,
1、JDK自带的动态代理
先实现一个接口和具体的实现类
A.接口:
public interface IUser {
void talk();
}
B.实现类
public class User implements IUser {
@Override
public void talk() {
System.out.println("doing User.talk");
}
}
C.创建一个实现InvocationHandler这个接口的类
public class UserProxy implements InvocationHandler {
private Object object;
public UserProxy(Object object){
super();
this.object = object;
}
/**
* 实现InvocationHandler接口要重写invoke方法,方法的三个参数分别:
* proxy:就是动态代理生成的代理类对象
* method:就是调用的方法
* args:表示该方法的参数
*/
@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;
}
}
D.在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动态代理原理
mian中调用了代码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的多态原理,调用的应该是代理对象 P r o x y ( ) 的 t a l k ( ) 方 法 , 看 Proxy()的talk()方法,看 Proxy()的talk()方法,看Proxy()重写的talk方法
public final void talk() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
当调用IUser接口的引用变量user调用talk的时候,其实调用代理对象重写的talk方法,用你传入的InvocationHandler对象,并调用了他的invoke方法
2、CGLib动态代理
代理提供了一种可扩展的机制来控制被代理的对象的访问,就是在对象访问的时候加了一层封装,JDK从1.3版本开始提供了上述动态代理机制,使用简单,缺点显而易见,需要目标对象实现一个或者多个接口
当代理没有接口的类,此时Proxy和InvocationHandler机制不能使用了,此时可以使用CGLib库
CGLib采用非常底层的字节码技术,原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术,拦截所有父类方法的调用,顺势织入横切逻辑,JDK动态代理与CGLib动态代理均是实现Spring AOP的基础
A.创建父类
public class CGLibSuper {
public void doing() {
System.out.println("CGLibSuper.doing");
}
}
B.创建子类的方法与代理的方法
/**
* 该类实现了创建子类的方法与代理的方法,getProxy(CGLibSuper.class)方法通过入参即父类的字节码,
* 通过扩展父类的class来创建代理对象,intercept()方法拦截所有目标类方法的调用,Object表示目标类的实例,
* method为目标类方法的反射对象,objects为方法的动态入参,methodProxy为方法代理
* methodProxy.invokeSuper(o, objects)通过代理类来调用父类的方法
*/
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public <T> T getProxy(Class<T> clazz) {
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码结束动态的创建子类实例
return (T)enhancer.create();
}
//实现MethodInterceptor
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLibProxy.intercept");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("CGLibProxy.intercept end");
return o1;
}
}
C.Main
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
CGLibSuper proxy = cgLibProxy.getProxy(CGLibSuper.class);
proxy.doing();
}
CGLib参考文献:https://blog.csdn.net/xiaohai0504/article/details/6832990