cglib动态代理

      JDK实现动态代理需要实现类通过接口定义业务方法,被代理的类必须实现接口;对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术(采用ASM技术),其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,由于是继承,因此使用CGLIB代理的类不能是final类。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

       cglib的实践

(1)建立一个具体实现类:

package com.my.cglibproxy;

public class HelloWorldImpl {
    
    public void sayHi() {
        
        System.out.println("hello cglib");
        
    }
    
}

(2)建立CGLIB代理类,代理类需要实现MethodInterceptor接口。

MethodInterceptor接口在API中说明如下:

General-purpose Enhancer callback which provides for "around advice".

接口中只有一个intercept接口,API描述如下:

/**
     * All generated proxied methods call this method instead of the original method.
     * The original method may either be invoked by normal reflection using the Method object,
     * or by using the MethodProxy (faster).
     * @param obj "this", the enhanced object
     * @param method intercepted Method
     * @param args argument array; primitive types are wrapped
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed
     * @throws Throwable any exception may be thrown; if so, super method will not be invoked
     * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
     * @see MethodProxy
     */    
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
(3)创建CGLIB的代理类

package com.my.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class HelloWorldCglibProxy 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();
        
    }
    
    //调用此方法的类就是callback类,也就是代理类HelloWorldCglibProxy自身,通过Enhancer的setCallback方法进行设置
    @Override
    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        
        //在Spring的AOP中,此处可以配置添加before方法
        System.out.println("before cglib");
        
        //打印类名
        System.out.println("object name: " + object.getClass().getName());
       
        System.out.println("proxy name: " + proxy.getClass().getCanonicalName());
        
        //调用super也就是被代理类的方法,本实例是sayHi方法
        proxy.invokeSuper(object, args);
        
        //打印方法名
        System.out.println("method name: " + method.getName());
        
        //在Spring中,此处可以配置添加after方法
        System.out.println("after cglib");
        return null;
    }

}

(4)定义测试类进行代理测试

package com.my.cglibproxy;

public class TestCglibProxy {
    
    public static void main(String [] args) {
        HelloWorldCglibProxy cglib = new HelloWorldCglibProxy();
        HelloWorldImpl helloWorldImpl = (HelloWorldImpl)cglib.getInstance(new HelloWorldImpl());
        helloWorldImpl.sayHi();
    }
}

测试过程中可能会报错:

Exception in thread "main" java.lang.NoClassDefFoundError: org/objectweb/asm/Type

	at net.sf.cglib.core.TypeUtils.parseType(TypeUtils.java:180)

	at net.sf.cglib.core.KeyFactory.<clinit>(KeyFactory.java:66)

	at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69)

	at com.my.cglibproxy.HelloWorldCglibProxy.getInstance(HelloWorldCglibProxy.java:17)

	at com.my.cglibproxy.TestCglibProxy.main(TestCglibProxy.java:7)

Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type

	at java.net.URLClassLoader$1.run(URLClassLoader.java:202)

	at java.security.AccessController.doPrivileged(Native Method)

	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)

	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)

	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)

	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

	... 5 more

因为CGLIB的底层字节码实现技术是ASM,因此需要导入ASM的jar包,如果导入ASM的jar版本太高,可能出现如下错误。比如我在此实例中用的是ASM 4.1,导致报错。:

Exception in thread "main" java.lang.VerifyError: class net.sf.cglib.core.DebuggingClassWriter overrides final method visit.(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
	at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
	at net.sf.cglib.core.AbstractClassGenerator.<init>(AbstractClassGenerator.java:38)
	at net.sf.cglib.core.KeyFactory$Generator.<init>(KeyFactory.java:127)
	at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:112)
	at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108)
	at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104)
	at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69)
	at com.my.cglibproxy.HelloWorldCglibProxy.getInstance(HelloWorldCglibProxy.java:17)
	at com.my.cglibproxy.TestCglibProxy.main(TestCglibProxy.java:7)

此时需要下载一个3.X版本的ASM的jar包,错误解决,打印如下:

before cglib
object name: com.my.cglibproxy.HelloWorldImpl$$EnhancerByCGLIB$$8ed02279
proxy name: net.sf.cglib.proxy.MethodProxy
hello cglib
method name: sayHi
after cglib

从打印结果可以看出:

(1)CGLIB的代理方法invoke中的第一个参数Object object,所代表的就是原始类HelloWorldImpl的CGLIB的的代理类;

(2)获得代理类之后,实际通过代理类调用的是什么方法,则invoke方法的第二个Method参数就表示什么方法,比如本实例中,通过代理类的getInstance获得对象后,调用的是sayHi方法,则invoke的第二个Method参数就表示sayHi方法。

(3)原始类的方法的调用,虽然代码表现为helloWorldImpl.sayHi();实际是通过代理类来调用的,由Enhancer的setCallback设置回调类,比如本例中参数为setCallback(this),就是代理类自身。

(4)CGLIB能够知道具体调用哪个方法(即代理类invoke中的Method参数),是通过Enhancer的setSuperclass方法设置父类(这个父类就是被代理的原始类),从而根据这个父类拦截获取获取到父类的方法,在实现了MethodInterceptor的代理类中,可以在invoke方法中调用父类的具体方法之前进行一些处理,spring中AOP的around advice就是基于此实现的。


     

你可能感兴趣的:(cglib动态代理)