向大神求教,Spring 使用 cglib动态代理问题

向大神求教,Spring 使用 cglib动态代理问题,第1张

原因是使用了:

<aop:aspectj-autoproxy proxy-target-class="true"/>

配置proxy-target-class="true"则强制使用了CGLIB生成代理,mybatis的mapper没有默认构造方法,会报错:

Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible classnested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22

修改成

<aop:aspectj-autoproxy/>

这个的意思我原本以为是完全不使用cglib的代理,现在发现应该是spring会自动在JDK动态代理和CGLIB之间转换。

statement:本篇内容只是建立在我目前经验的基础之上,必然有不完善甚至是不正确的地方,请谨慎阅读,如果能指出错误与不足之处,更是不甚感激

附:本篇内容旨在基本探究一下CGLib提供了哪些功能,并提供基础示例和使用方法,本文主要内容来自于Rafael Winterhalter的文章《CGLib: The Missing Manual》,并在此基础上进行一些添加与修正。本篇内容将可能会被多次更新(如果有更深的理解,或者是需要添加的说明之处)

CGLib是一个基于ASM字节码 *** 纵框架之上的类生成工具,相比于底层的ASM,CGLib不需要你理解那么多必需的和字节码相关底层知识,你可以很轻松的就在运行时动态生成一个类或者一个接口。CGLib广泛应用于AOP方面和代理方面,比如Spring、Hibernate,甚至也可以用于Mock测试方面。

CGLib有些地方需要提前引起注意,至于原因为何,我将会在最后一节说明。

参考文档:

[1] CGLib: The Missing Manual

[2] https://github.com/cglib/cglib/wiki/How-To

CGLIB 和 JDK生成动态代理类区别

jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。

cglib这种第三方类库实现的动态代理应用更加广泛,并不需要目标类基于统一的接口,且在效率上更有优势。

下面分别用2个具体例子来详细阐述

jdk生成代理类

为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提

public interface Service {  

    /** 

     * add方法 

     */  

    public void add()  

  

    /** 

     * update方法 

     */  

    public void update()  

}

2.目标类AService

public class AService implements Service {  

    /* 

     * (non-Javadoc) 

     *  

     * @see jdkproxy.Service#add() 

     */  

    public void add() {  

        System.out.println("AService add>>>>>>>>>>>>>>>>>>")  

    }  

  

    /* 

     * (non-Javadoc) 

     *  

     * @see jdkproxy.Service#update() 

     */  

    public void update() {  

        System.out.println("AService update>>>>>>>>>>>>>>>")  

    }  

}

3.实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。在

invoke方法中加入一些代理功能。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。

public class MyInvocationHandler implements InvocationHandler {  

    private Object target  

  

    MyInvocationHandler() {  

        super()  

    }  

  

    MyInvocationHandler(Object target) {  

        super()  

        this.target = target  

    }  

  

    public Object invoke(Object proxy, Method method, Object[] args)  

            throws Throwable {  

        // 程序执行前加入逻辑,MethodBeforeAdviceInterceptor  

        System.out.println("before-----------------------------")  

        // 程序执行  

        Object result = method.invoke(target, args)  

        // 程序执行后加入逻辑,MethodAfterAdviceInterceptor  

        System.out.println("after------------------------------")  

        return result  

    }  

}

4.测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler)来生成的。

public class Test {  

    public static void main(String[] args) {  

        Service aService = new AService()  

        MyInvocationHandler handler = new MyInvocationHandler(aService)  

        // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例  

        Service aServiceProxy = (Service) Proxy.newProxyInstance(aService  

                .getClass().getClassLoader(), aService.getClass()  

                .getInterfaces(), handler)  

        // 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口  

        aServiceProxy.add()  

        System.out.println()  

        aServiceProxy.update()

        }

cglib动态代理实现AOP拦截

被代理目标类,cglib不需要目标类实现接口

public class Base {  

    /** 

     * 一个模拟的add方法 

     */  

    public void add() {  

        System.out.println("add ------------")  

    }  

}

实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。

public class CglibProxy implements MethodInterceptor {  

  

    public Object intercept(Object object, Method method, Object[] args,  

            MethodProxy proxy) throws Throwable {  

        // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。  

        System.out.println("before-------------")  

        // 执行目标类add方法  

        proxy.invokeSuper(object, args)  

        // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。  

        System.out.println("after--------------")  

        return null  

    }  

  

}

获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的

public class Factory {  

    /** 

     * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类 

     *  

     * @param proxy 

     * @return 

     */  

    public static Base getInstance(CglibProxy proxy) {  

        Enhancer enhancer = new Enhancer()  

        enhancer.setSuperclass(Base.class)  

        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法  

        enhancer.setCallback(proxy)  

        // 此刻,base不是单纯的目标类,而是增强过的目标类  

        Base base = (Base) enhancer.create()  

        return base  

    }  

}

测试类

public class Test {  

    public static void main(String[] args) {  

        CglibProxy proxy = new CglibProxy()  

        // base为生成的增强过的目标类  

        Base base = Factory.getInstance(proxy)  

        base.add()  

    }  

}

从上面2个例子,可看出cglib中目标类Base并没有实现接口,而jdk生成代理类例子中AService 实现了Service接口,所以CGLIB 和 JDK生成动态代理类的区别最大的区别就是目标类是否需要实现接口。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11562180.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-17
下一篇 2023-05-17

发表评论

登录后才能评论

评论列表(0条)

保存