cglib动态代理

来源:互联网 发布:随机森林算法简单实例 编辑:程序博客网 时间:2024/05/16 08:31

      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/Typeat 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.Typeat 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;)Vat 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 cglibobject name: com.my.cglibproxy.HelloWorldImpl$$EnhancerByCGLIB$$8ed02279proxy name: net.sf.cglib.proxy.MethodProxyhello cglibmethod name: sayHiafter 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就是基于此实现的。


     

原创粉丝点击