1、代理模式
1.1、介绍
概念
代理模式(Proxy Pattern)给某一个对象提供一个代理,并由代理对象控制原对象的引用。代理对象在客户端和目标对象之间起到中介作用 。
代理模式是常用的结构型设计模式之一,当直接访问某些对象存在问题时可以通过一个代理对象来间接访问。
用途:
- 当提供服务方不想让用户访问真正角色时,采用代理模式
- 当需要横切一些业务时,为了不破坏原有的类,也可采用代理模式
作用:
- 功能增强: 在你原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。
- 控制访问: 代理类不让你访问目标,例如商家不让用户访问厂家。
实现代理的方式
- 静态代理
- 动态代理(两种JDK和CGLIB)
1.2 JDK动态代理
在Java的动态代理机制中,有一个接口InvocationHandler
(Interface)和另一个类 Proxy
(Class),二者可谓之中流砥柱。
InvocationHandler
在使用动态代理的时候,每一个代理类需要实现InvocationHandler
接口,并且重写其接口中唯一的方法invoke()。
动态代理工具类
public class DynamicProxy implements InvocationHandler { // 实现调用处理接口
//被代理的接口对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//创建动态代理对象用Proxy类中的newProxyInstance()方法
// 参数信息:
// 参数一:通过反射得到类加载器
// 参数二:需要实现的接口
// 参数三:处理者 这个参数需要一个InvocationHandler(接口)的对象,
// 我们这个自定义代理类实现了InvocationHandler接口,所以用this调用自己
// 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
//Proxy.newProxyInstance因为与IllegalArgumentException相同的原因而Proxy.getProxyClass
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// 处理代理实例,并返回结果
// method这个参数其实就是我们要增强的方法,也就是需要代理类去调用的方法,
// 通过这个参数调用invoke()方法 invoke翻译:调用
// method.invoke()通过反射去调用我们target接口里的方法 动态之所以就在这
// 该方法会在动态代理过程中通过反射被执行,具体执行过程在下面解释
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
return method.invoke(target, args);
}
private void log(String msg){
System.out.println("我是代理类添加的消息,使用了"+msg+"方法!!!");
}
}
动态代理的步骤
-
通过实现 InvocationHandler 接口创建自己的调用处理器(动态代理类);
-
通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
-
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
-
//获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h)) final Constructor<?> cons = cl.getConstructor(constructorParams);
-
-
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
-
//生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法,InvocationHandler h return cons.newInstance(new Object[]{h})
-
JDK动态代理步骤
JDK动态代理分为以下几步:
- 拿到被代理对象的引用,并且通过反射获取到它的所有的接口。
- 通过JDK Proxy类重新生成一个新的类,同时新的类要实现被代理类所实现的所有的接口。
- 动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用。
- 编译新生成的 Java 代码.class。
- 将新生成的Class文件重新加载到 JVM 中运行。
参考:https://blog.csdn.net/jiankunking/article/details/52143504
1.3 CGLIB动态代理
JDK动态代理是通过重写被代理对象实现的接口中的方法来实现,而CGLIB是通过继承被代理对象来实现,和JDK动态代理需要实现指定接口一样,CGLIB也要求代理对象必须要实现MethodInterceptor
接口,并重写其唯一的方法intercept
。
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。(利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理)
注意:因为CGLIB是通过继承目标类来重写其方法来实现的,故而如果是final和private方法则无法被重写,也就是无法被代理。
1、JDK动态代理具体实现原理:
通过实现InvocationHandler接口创建自己的调用处理器;
通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、两者对比:
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。
4、使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
2、AOP的实现
基于动态代理