之前章节讲的Java面试知识点(七十九)设计模式之代理模式(上) 的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib 代理
Cglib 代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对 final 修饰的类进行代理。JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。
Cglib 子类代理实现方法:
- 需要引入 cglib 的 jar 文件。
- 引入功能包后,就可以在内存中动态构建子类
- 代理的类不能为 final, 否则报错
- 目标对象的方法如果为 final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法.
【代码示例】
- 目标对象
package designpatterns.proxy.Cglib;
/**
* 目标对象
* */
public class Child {
public void school() {
System.out.println("娃娃去上学");
}
public void money() {
System.out.println("娃娃交学费");
}
}
- 代理类(代理类会生成目标对象的子类)
package designpatterns.proxy.Cglib;
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 Proxy implements MethodInterceptor {
// 维护目标对象
private Object object;
public Proxy(Object object) {
this.object = object;
}
// 给目标创建一个代理对象
public Object getProxy() {
// 创建工具类
Enhancer en = new Enhancer();
// 设置父类
en.setSuperclass(object.getClass());
// 设置回调函数,回调函数调用的是重写的interce方法
en.setCallback(this);
// 创建子类(代理对象)
return en.create();
}
/**
* 这里的拦截是指,子类代理会拦截所有父类方法的调用,并把调用更给成在拦截方法内的逻辑
* */
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 执行目标对象的方法
Object result = null;
if ("school".equals(method.getName())) {
result = method.invoke(this.object, objects);
}
if ("money".equals(method.getName())) {
System.out.println("父母去交钱");
}
return result;
}
}
- 测试环境
package designpatterns.proxy.Cglib;
public class Demo {
public static void main(String[] args) {
Child proxy = (Child) new Proxy(new Child()).getProxy();
proxy.school();
proxy.money();
}
}
- 运行结果
娃娃去上学
父母去交钱
【总结】
1.报错java.lang.NoClassDefFoundError: org/objectweb/asm/Type
导入的jar包不完整或者不匹配,代理模式需要导入两个jar包,一个是cglib.jar;另一个是asm.jar(处理字节码);只导入cglib可以编译通过,但是运行的时候会存在问题,同时,导入的版本不对,也会报错。我用的是cglib3.1.jar 和字节码 asm4.0.jar;并且经过代码测试,直接可用。大家可以网上找或者点击cglib相关jar包下载
2.分析 invoke 和 invokeSuper 的差别
实现MethodIntercept接口的时候同时也要实现intercept方法,这个方法是用来拦截父类的方法的,也就是代理子类继承的目标父类的方法,代理子类拦截之后重写逻辑从而实现代理的作用,而获取父类的方法有以下几种方式
result = method.invoke(this.object, objects);
result = methodProxy.invokeSuper(o,objects);
result = methodProxy.invoke(this.object,objects);
intercept方法的参数如下:
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- Object o:子类对象
- Method method:父类方法对象
- Object[] objects:方法参数数组
- MethodProxy methodProxy:子类方法对象
invoke和invokeSuper
invoke 方法调用的对象没有增强过,invokeSuper 方法调用的对象已经是增强了的(拦截之后更改过逻辑),所以会再走一遍 MyMethodInterceptor 的 interceptor 方法,如果是个拦截器链条,就会重新在走一次拦截器链;
由于invokeSuper是增强之后的方法,所以invokeSuper的参数是子类的
- Object o:子类对象