欢迎关注微信公众号:Coding我不配
获取更多干货,一起每天进步一点点
代理的目标是为某一个对象提供一个代理,并由代理对象来控制对原对象的访问。
依据代理类的创建时间,分为静态代理和动态代理。
静态代理是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前已经确定好。
动态代理是程序在运行期间动态构建代理对象和动态调用代理方法的一种机制。在程序运行时,通过反射机制动态创建而成。
简单地说,代理是用来在被代理对象基础上增加一些额外处理的机制,比如权限校验,日志处理等。
动态代理的实现方式有两种:JDK Proxy 和 CGLib 代理方式。
动态代理的常见使用场景有 RPC 框架的封装、Spring AOP(面向切面编程)的实现、JDBC 的连接等。
JDK Proxy 动态代理的实现无需引用第三方类,其动态代理类位于 java.lang.reflect 包中。
实操撸一把代码:
package com.coding.wbp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyDemo {
// 需要动态代理的接口
static interface Sister{
void sing();
}
// 需要动态代理的实际对象
static class Wan implements Sister{
@Override
public void sing() {
System.out.println("I am wanqian.");
}
}
// 需要动态代理的实际对象
static class Jin implements Sister{
@Override
public void sing() {
System.out.println("I am Jinsha.");
}
}
//JDK proxy
static class ProxyCheng implements InvocationHandler{
//要代理的真实对象
private Object target;
//构造方法,给要代理的真实对象赋初值
ProxyCheng (Object obj){
this.target = obj;
}
public Object getInstance(){
Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("introduce yourself...");
// 执行调用方法(此方法执行前后,可以进行相关业务处理)
Object result = method.invoke(target,args);
System.out.println("score...");
return result;
}
}
public static void main(String[] args) {
//创建被代理类的对象
Sister wanqian = new Wan();
//创建代理类的对象
ProxyCheng proxyCheng = new ProxyCheng(wanqian);
//获取动态代理类实例
Sister proxySister = (Sister) proxyCheng.getInstance();
System.out.println("JDK Dynamic object name: " + proxySister.getClass().getName());
proxySister.sing();
}
}
以上程序运行结果:
JDK Dynamic object name: com.coding.wbp.$Proxy0
introduce yourself...
I am wanqian.
score...
结果分析:
当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的 handler 对象的 invoke 方法来进行调用;即当执行 proxySister.sing();会自动调用 ProxyCheng 的 invoke 方法。
关键在于生成代理对象是用的 Proxy 类的静态方 newProxyInstance 获得代理对象。查看 JDK1.8 Proxy 类源码发现最终生成代理类字节码,部分关键代码:
//生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
使用反编译工具将生成的字节码反编译,部分代码如下:
public final class ProxySister extends Proxy
implements Sixter{
private static Method m1;
public final void sing(){
try{
return this.h.invoke(this, m1, null);
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
//...其他
}
这个代理类继承了 Proxy 类,并实现了之前定义的 Sister 接口。通过带有 InvocationHandler 参数的构造方法来创建实例,并把 InvocationHandler 传递到了 Proxy 类中,它覆写了接口中所有的方法,同时覆写了 Object 类的 equals、hashCode、toString 方法。到此就明白来龙去脉了。
JDK 动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理。
使用 CGLib 实现动态代理,完全不受代理类必须实现接口的限制。
CGLib 的原理是对指定目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理。
使用 CGLib 实现,需要第三方库,通过 pom.xml 引入:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.0</version>
</dependency>
实操撸一把代码:
package com.coding.wbp;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibDemo {
// 需要动态代理的实际对象
static class Sister {
public void sing() {
System.out.println("I am Jinsha, a little sister.");
}
}
static class CGLibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
// 设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("introduce yourself...");
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("score...");
return result;
}
}
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
//获取动态代理类实例
Sister proxySister = (Sister) cgLibProxy.getInstance(new Sister());
System.out.println("CGLib Dynamic object name: " + proxySister.getClass().getName());
proxySister.sing();
}
}
以上程序运行结果:
CGLib Dynamic object name: com.coding.wbp.CGLibDemo$Sister$$EnhancerByCGLIB$$d49b0c3e
introduce yourself...
I am Jinsha, a little sister.
score...
结果分析:
CGLib 的调用流程就是通过调用拦截器的 intercept 方法来实现对被代理类的调用。而拦截逻辑可以写在 intercept 方法的 invokeSuper(o, objects);的前后实现拦截。
通过查看 Enhancer 类源码,最终也是生成动态代理类的字节码,动态代理类继承要被代理的类,然后实现其方法。
和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。
JDK Proxy 和 CGLib 的区别主要体现在以下方面:
使用 CGLib 实现动态代理,CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。
对 JDK 动态代理与 CGlib 动态代理的代码进行测试,1W 次执行下,JDK 1.8 的动态代理性能比 CGlib 要好 20%左右。
1. AOP 思想
2. AOP 的作用
3. AOP 使用场景
4. AOP 的实现原理
Spring 中 AOP 的有两种实现方式:JDK proxy 和 CGLib 动态代理
当 Bean 实现接口时,Spring 就会用 JDK 的动态代理。
当 Bean 没有实现接口时,Spring 使用 CGlib 实现。
可以强制使用 CGlib 代理(在 spring 配置中加入
欢迎关注微信公众号:Coding我不配
获取更多干货,一起每天进步一点点