Spring aop 基于JDK动态代理和CGLIB代理的原理以及为什么JDK代理需要基于接口

Posted yvan1115

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring aop 基于JDK动态代理和CGLIB代理的原理以及为什么JDK代理需要基于接口相关的知识,希望对你有一定的参考价值。

本文是根据《深入分析Java Web技术内幕》一书第十三章探讨


Spring Aop是什么

简单来说就是面向切面编程。Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。

JDK动态代理

JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。代理的目的是调用目标方法时可以转而执行InvocationHandler的invoke方法,实际上spring aop也是在这里做文章。这也是典型的代理模式
下面简单代码说明这个问题

  • 实现自己的InvocationHandler
/**
 * 
 * @author yvan
 *
 */
public class AopProxy implements InvocationHandler 

    private Object realObject;


    public AopProxy(Object realObject) 
        super();
        this.realObject = realObject;
    


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        System.out.println("do something before execute invoke method...");
        Object invokeResult= method.invoke(realObject, args);
        System.out.println("do something after execute invoke method...");
        return invokeResult;
    


  • 制定接口以及其实现类
/**
 * 
 * @author yvan
 *
 */
public interface SubObject 
    public String execute(String... param);


/**
 * 
 * @author yvan
 *  */
public class RealObject implements SubObject 

    @Override
    public String execute(String... param) 
        for (int i = 0; i < param.length; i++) 
            System.out.println(param[i]);
        
        return "execute method";
    

  • 测试动态代理
/**
 * 
 * @author yvan
 *  */
public class AppMain 
    public static void main(String[] args) 
        SubObject realObject = new RealObject();
        AopProxy aopProxy = new AopProxy(realObject);
        // 通过jdk提供的proxy生成代理
        SubObject subObject = (SubObject) Proxy.newProxyInstance(aopProxy.getClass().getClassLoader(),
                realObject.getClass().getInterfaces(), aopProxy);
        System.out.println(subObject.getClass().getName());
        System.out.println(subObject instanceof Proxy);
        String result =  subObject.execute("参数一","参数二");
        System.out.println(result);
    
  • 打印结果
com.sun.proxy.$Proxy0
true
do something before execute invoke method...
参数一
参数二
do something after execute invoke method...
execute method

着重说明一下:看到com.sun.proxy.$Proxy0是jdk动态生成的代理类,subObject instanceof Proxy打印为ture说明这个代理类继承了Proxy。
这里遇到一个疑问:为什么jdk动态代理必须基于接口
原因如下:
1、生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现
2、从代理模式的设计来说,充分利用了java的多态特性,也符合基于接口编码的规范
当然,jdk在生成代理的参数中也说明了,需要传入对应接口

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

CGLIB代理

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类
CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。

如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理。

添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

以上是关于Spring aop 基于JDK动态代理和CGLIB代理的原理以及为什么JDK代理需要基于接口的主要内容,如果未能解决你的问题,请参考以下文章

Spring aop 基于JDK动态代理和CGLIB代理的原理以及为什么JDK代理需要基于接口

Spring框架的AOP实现(JDK+CGLIB)

Spring------AOP

Spring的AOP总结

基于JDK动态代理实现的Spring AOP源码学习

Spring 静态代理+JDK动态代理和CGLIB动态代理