CGLib动态代理的底层原理

  • JDK实现动态代理需要实现类通过接口定义业务方法。
  • CGLib采用了非常底层的字节码技术,其原理是通过目标类的字节码为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
  • 底层使用字节码处理框架ASM,来转换字节码并生成新的类。
  • 更详细一点说,代理类将目标类作为自己的父类并为其中的每个非final委托方法创建两个方法
    • 一个是与目标方法签名相同的方法,它在方法中会通过super调用目标方法
    • 另一个是代理类独有的方法,称之为Callback回调方法,它会判断这个方法是否绑定了拦截器(实现了MethodInterceptor接口的对象),若存在则将调用intercept方法对目标方法进行代理,也就是在前后加上一些增强逻辑。intercept中就会调用上面介绍的签名相同的方法。

CGLib动态代理的底层原理_第1张图片

一、使用CGLib创建代理类

(1)引入CGLib依赖

CGLib动态代理的底层原理_第2张图片

(1)目标类

public class HelloServiceImpl {  
 public void say(){  
  System.out.println("hello everyone");  
 }  
} 

(2)生成方法拦截器

CGLib动态代理的底层原理_第3张图片

(3)生成代理对象

CGLib动态代理的底层原理_第4张图片

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对目标类进行扩展。

  • 首先将目标类HelloServiceImpl设置成父类;
  • 然后设置拦截器HelloMethodInterceptor;
  • 最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型HelloServiceImpl;
  • 最后,在代理类上调用方法。

二、对不同方法执行不同的回调逻辑

2.1 JDK动态代理的方式

在JDK动态代理中,通过InvocationHandler接口方法的调用对代理类内的所以方法都有效。我们往往需要对Method的Name进行判断,然后针对不同的Method编写不同的逻辑,如下:

class MyInvocationHandler implements InvocationHandler {
   //具体的调用类
    Object target;

    public MyInvocationHandler(Object obj) {
        target = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //当前所持有的proxy对象
        //method表示当前别调用的方法
        //args表示方法中传递的参数
        System.out.println("method name:"+method.getName());
        if(method.getName().equals("add")){
            if(args[0].equals("apple")){
                return false;
            }
        }
        return method.invoke(target, args);
    }
}

2.2 CGLib的方式

首先介绍两个概念:

  • 回调数组Callback[]:不同的Method的调用可以对应不同的回调函数。在动态代理中,回调函数就是拦截器需要执行的函数,所以我们说回调函数,可以约等于拦截器。拦截器通过实现MethodInterceptor接口定义。
  • 回调过滤器CallbackFilterint accept(Method method) 返回的值为数字代表了Callback数组中的索引位置,即Method对应的Callback。

(1)定义实现过滤器CallbackFilter接口的类:

import java.lang.reflect.Method;   
import net.sf.cglib.proxy.CallbackFilter;  

public class TargetMethodCallbackFilter implements CallbackFilter {  

    /** 
     * 过滤方法 
     * 返回的值为数字,代表了Callback数组中的索引位置,要到用的Callback 
     */  
    @Override  
    public int accept(Method method) {  
        if(method.getName().equals("method1")){  
            System.out.println("filter method1 ==0");  
            return 0;  
        }  
        if(method.getName().equals("method2")){  
            System.out.println("filter method2 ==1");  
            return 1;  
        }  
        if(method.getName().equals("method3")){  
            System.out.println("filter method3 ==2");  
            return 2;  
        }  
        return 0;  
    }  
}  

(2)为代理类设置回调数组和回调过滤器:

import net.sf.cglib.proxy.Callback;  
import net.sf.cglib.proxy.CallbackFilter;  
import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.NoOp;  

public class TestCglib {  
    public static void main(String args[]) {  
        Enhancer enhancer =new Enhancer();  
        enhancer.setSuperclass(TargetObject.class);  
        CallbackFilter callbackFilter = new TargetMethodCallbackFilter();  

        /** 
         * (1)callback1:方法拦截器 
           (2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。 
           (3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。 
         */  
        Callback noopCb=NoOp.INSTANCE;  
        Callback callback1=new TargetInterceptor();  
        Callback fixedValue=new TargetResultFixed();  
        Callback[] cbarray=new Callback[]{callback1,noopCb,fixedValue};  
        //enhancer.setCallback(new TargetInterceptor());  
        enhancer.setCallbacks(cbarray);  
        enhancer.setCallbackFilter(callbackFilter);  
        TargetObject targetObject2=(TargetObject)enhancer.create();  
        System.out.println(targetObject2);  
        System.out.println(targetObject2.method1("mmm1"));  
        System.out.println(targetObject2.method2(100));  
        System.out.println(targetObject2.method3(100));  
        System.out.println(targetObject2.method3(200));  
    }  
}  
import net.sf.cglib.proxy.FixedValue;  

public class TargetResultFixed implements FixedValue{  
    /**  
     * 该类实现FixedValue接口,同时锁定回调值为999  
     * (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。  
     */  
    @Override  
    public Object loadObject() throws Exception {  
        System.out.println("锁定结果");  
        Object obj = 999;  
        return obj;  
    }  
}  

你可能感兴趣的:(Java,JavaWeb)