动态代理、CGLIB 与 切面编程

来自Wikioe
Eijux讨论 | 贡献2020年10月29日 (四) 01:54的版本 →‎常用的API
跳到导航 跳到搜索


关于代理

代理,简单说就是:不直接操作对象,而使用代理对象来完成操作;

  • 通过代理对象,可以对原对象进行方法扩展;


代理又可以分为

  1. 静态代理:通过聚合、继承的方式生成代理对象;
    • 代理关系在编译时就确定了
  2. 动态代理:
    • 运行期动态生成代理对象;
    1. Java动态代理:(针对实现了接口的类)
    2. CGLIB:(针对没有实现接口的类)
    3. AspectJ:(springframework的aop基于此实现)

Java动态代理

AspectJ

CGLIB

CGLIB是一个功能强大,高性能的代码生成包。

  • 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制
  • CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。

原理

  1. 动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。
  2. 在子类中采用方法拦截的技术,拦截所有父类方法的调用,并织入横切逻辑。
  • 为没有实现接口的类提供代理,比于JDK动态代理(只能代理接口)更加强大;
  • 不能代理final方法;

CGLIB组成结构

CGLIB组成结构.png

CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。

  • 因为使用字节码技术生成代理类,所以CGLIB比java反射的JDK动态代理更快;
  • 关于“ASM”:
    • 除了CGLIB库外,脚本语言(如:“Groovy”、“BeanShell”)也使用ASM生成字节码;
    • ASM使用类似SAX的解析器来实现高性能;
    • (如果直接使用ASM,需要对Java字节码非常了解)

jar包

  • “cglib-nodep-2.2.jar”:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
  • “cglib-2.2.jar”:使用此jar包需要关联asm的jar包,否则运行时报错.

类库

CGLIB类库:

  • “net.sf.cglib.core”:底层字节码处理类,他们大部分与ASM有关系;
  • “net.sf.cglib.transform”:编译期或运行期类和类文件的转换;
  • “net.sf.cglib.proxy”:实现创建代理和方法拦截器的类;
  • “net.sf.cglib.reflect”:实现快速反射和C#风格代理的类;
  • “net.sf.cglib.util”:集合排序等工具类;
  • “net.sf.cglib.beans”:JavaBean相关的工具类;

常用的API

命令 说明 示例
Enhancer Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)
  • 作用类似Proxy类;但Enhancer既能够代理普通的class,也能够代理接口;(不能代理final方法)
  1. Enhancer.setSuperclass():设置父类型;
  2. Enhancer.create(Object…):创建增强对象;
  3. Enhancer.createClass():创建字节码(.class);
  4. setCallback():设置回调;
    • “InvocationHandler”可以作为回调,但必须防止死循环
      (invoke中调用的任何原代理类方法,均会重新代理到invoke方法中)
    • “MethodInterceptor”可以作为回调,且不会出现死循环


public class SampleClass {
    public void test(){
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SampleClass.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("before method run...");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("after method run...");
                return result;
            }
        });
        SampleClass sample = (SampleClass) enhancer.create();
        sample.test();
    }
}

输出:

before method run...
hello world
after method run...


使用

  1. public class TargetObject {
        public String method1(String paramName) {
            return paramName;
        }
     
        public int method2(int count) {
            return count;
        }
     
        public int method3(int count) {
            return count;
        }
     
        @Override
        public String toString() {
            return "TargetObject []"+ getClass();
        }
    }