spring-aop 两种代理方式 JDK动态代理和CGLIB动态代理

spring-aop 两种代理方式 JDK动态代理和CGLIB动态代理

Spring的两大特性是IOC和AOP:
Spring的核心特性就是IOC和AOP,IOC(Inversion of Control),即“控制反转”;AOP(Aspect-OrientedProgramming),即“面向切面编程”。下面分享我个人对AOP特性的理解。
AOP的实际应用:
面向切面编程,通常被定义为系统关注点的一种分离技术。系统是许多不同组件组成,组件的作用除了要实现自身的业务逻辑,通常都会被要求添加日志,事务管理,和缓存或安全等问题。这些系统服务进程被称为横切关注点,在系统中多个组件都会相继使用。

AOP实现的关键在于AOP框架自动创建AOP代理,AOP代理主要分为静态代理(AspectJ)和动态代理(StringAop)
AspectJ:会被称为编译时增强,AspectJ是静态代理的增强,采用编译时生成AOP代理类,会具有更好的性能,就是需要使用特定的编译器去处理,使用不太方便。

Spring-Aop:是以动态代理的方式,运行时生成AOP代理类,不会改变字节码,在内存中生成一个方法的AOP临时对象。在运行过程中需要每次生成AOP代理,性能会相对受到影响。
Spring-Aop动态代理实现有两种方式,jdk动态代理和CGIB动态代理。

JDK动态代理:是通过反射接收被代理的类。
一般主要涉及到以下两个类:
(1)Interface InvocationHandler:该接口中仅定义了一个方法
(2)Proxy:该类即为动态代理类,(详情看实例代码注解)

下面就来演示下jdk代理的实现原理。
1、jdk动态实现aop拦截
自定义要实现的接口MyInv,需要实现的方法add();

/**
 * Created by zzm on 2020/04/19
 * desc: jdk动态aop代理需要实现的接口
 */
public interface MyInv {
    public void add();
}

用要代理的目标类MyInvImpl实现上面我们定义的接口,在目标类的add方法的前后实现拦截,加入自定义切面逻辑。这就是aop的魅力所在:代码与代码之间没有耦合。

/**
 * Created by zzm on 2020/04/19
 * desc: 被代理的类,即目标类target
 */
public class MyInvImpl implements MyInv {
    @Override
    public void add() {
        System.out.println("目标类的add方法");
        System.out.println("时间和机会才是最后的本钱!");
    }
}

用MyInvoQuote类去实现InvocationHandler接口,并且实现接口中的invoke方法。在该方法中加入切面逻辑,实现自己的目标接口是在method.invoke里完成。


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by zzm on 2020/04/19
 * desc:这里加入切面实现InvocationHandler 接口的类
 */
public class MyInvoQuote implements InvocationHandler {

    private Object target;

    public MyInvoQuote(Object target) {   // 构造函数
        this.target = target;
    }
    /**
     * 这个抽象方法在代理类中动态实现。
     *
     * @param proxy  第一个参数obj一般是指代理类
     * @param method  method是被代理的方法
     * @param args  args为该方法的参数数组
     * @return
     *
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before---》切面执行逻辑");
        Object invoke = method.invoke(target, args);//通过反射执行,目标类的方法
        System.out.println("after---》切面执行逻辑");
        return invoke;
    }
}

最后写个测试方法。实现aop方式之一的:jdk动态代理


public class test {

    public static void main(String[] args) {
        MyInvImpl myInvImpl = new MyInvImpl();  // 实现需要代理的类
        MyInvoQuote handler = new MyInvoQuote(myInvImpl);  //构造实现InvocationHandler接口的实现类
        // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
        //这里的proxyInstance就是我们目标类的增强代理类
        MyInv proxyInstance = (MyInv) Proxy.newProxyInstance(myInvImpl.getClass().getClassLoader(), // getClassLoader() 取得该Class对象的类装载器
                myInvImpl.getClass()
                        .getInterfaces(), handler); //getInterfaces()获取该class对象下的方法可通过索引找到对应方法
                                                    // 列: myInvImpl.getClass().getInterfaces()[0] 获得myInvImpl对象所实现的第一个接口
        //Proxy.newProxyInstance()返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
        proxyInstance.add();
        //打印增强过的类类型
        System.out.println("打印增强过的类类型:" + proxyInstance.getClass());
    }
}

结果在这里插入图片描述
在实现过程中证实了jdk动态代理的核心是InvocationHandler接口和Proxy类
注意:jdk动态代理的应用前提,反射机制在生成类的过程中比较高效,不过必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性

cglib动态代理:底层则是借助asm来实现的
下面演示下cglibProxy动态代理
目标类,cglib不需要定义目标类的统一接口


/**
 * Created by zzm on 2020/04/19
 * desc: 被代理的类,即目标类target
 */
public class MyInvImpl  {
    public void myBase() {
        System.out.println("目标类的myBase方法");
        System.out.println("时间和机会才是最后的本钱!");
    }
}

实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在myBase方法前后加入了自定义的切面逻辑,目标类myBase方法执行语句为proxy.invokeSuper(object, args);


import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * Created by zzm on 2020/04/19
 * desc:这里加入切面实现MethodInterceptor 接口的类
 */
public class MyInvoQuote implements MethodInterceptor {


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before添加切面,方法执行前");
        methodProxy.invokeSuper(o, objects);
        System.out.println("after添加切面,方法执行后");
        return null;
    }
}

测试类:


public class test {

    public static void main(String[] args) {
        MyInvoQuote proxy = new MyInvoQuote();   //构造实现 MethodInterceptor接口的类
        Enhancer enhancer = new Enhancer();
        //设置代理目标
        enhancer.setSuperclass(MyInvImpl.class);
        //回调方法的参数为代理类对象CglibProxy,
        // 最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
        enhancer.setCallback(proxy);
        //此刻,base不是单车的目标类,而是增强过的目标类
        MyInvImpl base = (MyInvImpl) enhancer.create();
        base.myBase();
        Class<? extends MyInvImpl> baseClass = base.getClass();  //java 泛型 上界通配符(Upper Bounds Wildcards)
        //查看增强过的类的父类是不是未增强的Base类
        System.out.println("增强过的类的父类:"+baseClass.getSuperclass().getName());
        System.out.println("============打印增强过的类的所有方法==============");
        FanSheUtils.printMethods(baseClass);  //自定义获取类下面方法的工具类
//        没有被增强过的base类
        MyInvImpl base2 = new MyInvImpl();
        System.out.println("未增强过的类的父类:"+base2.getClass().getSuperclass().getName());
        System.out.println("=============打印增未强过的目标类的方法===============");
        FanSheUtils.printMethods(base2.getClass());//打印没有增强过的类的所有方法
    }
}

查看结果

before添加切面,方法执行前
目标类的myBase方法
时间和机会才是最后的本钱!
after添加切面,方法执行后
增强过的类的父类:com.billiards.service.impl.MyInvImpl
============打印增强过的类的所有方法==============
  public final equals(java.lang.Object);
  public final toString();
  public final hashCode();
 CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;);
  public static CGLIB$SET_STATIC_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;);
  public getCallback(int);
  public getCallbacks();
  public static CGLIB$findMethodProxy(org.springframework.cglib.core.Signature);
  final CGLIB$equals$1(java.lang.Object);
  final CGLIB$myBase$0();
  final CGLIB$hashCode$3();
  final CGLIB$clone$4();
  final CGLIB$toString$2();
  static CGLIB$STATICHOOK1();
  private static final CGLIB$BIND_CALLBACKS(java.lang.Object);
未增强过的类的父类:java.lang.Object
=============打印增未强过的目标类的方法===============
  public myBase();

自此,cglib动态代理实现的AOP拦截机制已经基本实现
asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

spring aop的两个核心方式已经简单实现。后续实现Spring-boot中使用aop切面,使用注解的形式更加简化了代码量,通过注解是使用springaop两种形式中的(AspectJ)方式实现的。掌握原理在使用注解的形式去做,才不会一头雾水。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值