aop-jdk的动态代理

1.aop-(基于jdk的动态代理)

在这里插入图片描述
这里我们回顾一下代理模式,这里我们要去买电脑,我们联想电脑的公司厂家在北京,我们此时要去北京买电脑的话路太远了,我们就有一个代理商去帮我们买电脑,这样我们就省得跑那么远,这里北京的联想公司就是真实对象,而西安那边就是代理对象,而代理模式就是代理对象代理真实对象,达到增强真实对象功能的目的。
在这里插入图片描述
这里静态代理是有一个类文件描述模式,而动态代理是在内存中也就是在运行时形成代理类。

动态代理实现步骤是首先代理对象和真实对象实现相同的接口,这里代理对象和真实对象相当于兄弟的关系,第二步就是使用Proxy的方法newProxyInstance来获取代理对象,第三步我们使用代理对象调用方法,最后我们就可以增强方法。
在这里插入图片描述
我们首先创建一个接口,这个接口是为了代理对象和真实对象实现的,
在这里插入图片描述
接着我们创建一个真实对象去实现该接口,该类里面实现了接口的两个方法

在这里插入图片描述
分析:这里我们创建了一个代理类,我们在该类里面首先创建真实对象,接着我们调用方法(对应我们上面的第三步),可是我们期望的不是用真实对象去完成的,而是使用代理对象去完成,这里我们使用动态代理增强真实对象,我们调用Proxy的newProxyInstance方法,里面有三个参数,我们这里真实对象和代理对象使用相同的类加载器,第一个参数是真实对象的类加载器,第二个参数是真实对象实现的接口,这两个参数是固定的写法,第三个参数我们需要通过匿名内部类来完成。

这三个参数,第一个参数是类加载器(真实对象.getClass().getClassLoader()),第二个参数是接口数组(真实对象.getClass().getInterfaces()),第三个参数就是new InvocationHandler().

将来这个方法会返回一个对象,这个对象我们称为代理对象,我们知道代理对象和真实对象实现了相同接口,所以我们这里使用接口类型来接收,此时我们将来在调用方法时,就是代理对象来调用方法,这里就体现了多态,会调用实现接口的子类方法。

第三个参数中的invoke方法有个特点就是代理逻辑编写的方法,代理对象调用的所有方法都会触发该方法执行
在这里插入图片描述
这个invoke方法里面的三个参数,第一个不咋用,第二个方法是代理对象调用的方法,被封装为的对象,此时我们在invoke中返回该方法的名称时,会打印调用该方法的名称,最后一个参数args表示代理对象调用的方法时,传递的实际参数,比如我们通过代理对象调用sale方法,此时我们方法里面传了个参数8000,我们输出args[0]就会输出8000.

在这里插入图片描述
我们执行sale方法之后,发现并没有调用,因为我们在真实对象的返回值里面是联想电脑,而在这里的返回值是null。
在这里插入图片描述

我们这里注释掉上面三个输出方法,我们使用真实对象调用该方法,我们传入真实对象和参数,此时返回值是Object,我们这里invoke的返回值就是我们代理对象调用sale方法的返回值,所以我们将返回值该为obj,此时就会返回展示电脑,此时代理对象和真实对象的逻辑就是一样的了。
在这里插入图片描述
此时我们考虑来增强方法,方法有三要素,方法的形参,方法名,和返回值。

我们这里代理商为了赚钱肯定是要收费的,不能光干活,所以我们可以增强参数,我们在invoke方法里面,我们这里只有在sale方法才有与钱打交道,与另一个show方法无关,所以我们首先判断是否是sale方法,接着我们拿到参数(强转为doule),此时我们可以将money返回15%的回扣,我们此时返回方法
在这里插入图片描述
如果方法不是sale方法,我们此时就会原来一样,不做改变。

第二种增强方式可以通过返回值增强,我们此时知道代理商赚了差价之后,心里不舒服,此时我们可以在返回值里面加一个鼠标垫。

还有第三种增强方式,就是在方法体执行的时候执行一些操作,我们在买电脑之前可以专车接送,这样让买电脑心里平衡点。

上面就是aop中基于jdk的动态代理模式。

代码如下:

接口

package com.itheima.proxy.jdk;

public interface TargetInterface {
    public void save();
}

增强对象

package com.itheima.proxy.cglib;

public class Advice {

    public void before(){
        System.out.println("前置增强.....");
    }
    public void afterReturning(){
        System.out.println("后置增强.....");
    }

}

目标对象

package com.itheima.proxy.cglib;

import com.itheima.proxy.jdk.TargetInterface;

public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running....");
    }
}

测试类

package com.itheima.proxy.jdk;

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

public class ProxyTest {
    public static void main(String[] args) {

        //目标对象
        final Target target = new Target();
        //增强对象
        final Advice advice = new Advice();

        //返回值  就是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  //目标对象类加载器
                target.getClass().getInterfaces(),   //目标对象相同的接口字节码对象数组
                new InvocationHandler() {
                    //调用代理对象的任何方法  实质执行的都是invoke方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.before();  //前置增强
                        Object invoke = method.invoke(target, args); //执行目标方法
                        advice.afterReturning();  //后置增强
                        return invoke;
                    }
                }
        );

        //调用代理对象的方法
        proxy.save();
    }
}

2.aop-基于cglib的动态代理

使用cglib的动态代理来实现aop这种方式与前面基于jdk接口的动态代理方式不一样,cglib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。

区别:

JDK动态代理是面向接口的。

CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。

性能:

关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。

主要体现在如下的两个指标中:

CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;

但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;

因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
在这里插入图片描述
基于cglib的动态代理由于是由spring框架提供的,所以我们这里要导入坐标,

但是从上面我们发现,spring-core当中有cglib这个包,早期我们需要去导入坐标,但是现在随着版本的更新,我们的cglib已经集成到了我们的spring框架中了,
在这里插入图片描述
我们这个cglib要完成动态代理测试要三个角色,我们与jdk的动态代理不同的是我们不需要创建接口了,我们的目标对象和增强对象的类其实和前面是一样的,我们直接从jdk的动态代理那边复制即可,
在这里插入图片描述
我们只需要改变测试类里面即可,我们在类里面创建目标对象和增强对象,接着我们首先创建一个增强器,这个是cglib提供的,第二步就是设置父类(目标对象),第三步就是设置回调,第四步就是创建代理对象,最后就是测试了

第一步的增强器是Enhancer,第二步是为增强器设置它的父类,在该setSuperclass方法内部传入的是一个字节码文件,第三步就是设置回调,调用增强器的setCallback(),该方法内部需要的是Callback接口,但是我们可以设置一个匿名内部类,该类是Callback下面的子接口,new MethodInterceptor(),该方法内部有四个参数,前三个参数和前面jdk的动态代理的invoke方法一样,我们在该方法内部,执行前置,执行后置,还有执行目标
在这里插入图片描述
第四步我们通过增强器的create方法创建一个代理对象,我们使用一个父类接收,最后我们进行测试

代码:
增强对象

package com.itheima.proxy.cglib;

public class Advice {

    public void before(){
        System.out.println("前置增强.....");
    }
    public void afterReturning(){
        System.out.println("后置增强.....");
    }

}

目标对象

package com.itheima.proxy.cglib;

import com.itheima.proxy.jdk.TargetInterface;

public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running....");
    }
}

动态代理的测试类

package com.itheima.proxy.cglib;

import com.itheima.proxy.jdk.TargetInterface;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

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

public class ProxyTest {
    public static void main(String[] args) {

        //目标对象
        final Target target = new Target();
        //增强对象
        final Advice advice = new Advice();

        //返回值  就是动态生成的代理对象  基于cglib
        //1.创建增强器
        Enhancer enhancer = new Enhancer();
        //2.设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3.设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                advice.before();   //执行前置
                Object invoke = method.invoke(target, objects);   //执行目标方法
                advice.afterReturning();   //执行后置
                return invoke;
            }
        });

        //4.创建代理对象
        Target proxy = (Target) enhancer.create();

        //调用代理对象的方法
        proxy.save();
    }
}

总结
Spring AOP的实现是通过动态代理,并且有两种实现方式,分别是JDK动态代理和CGLib动态代理。Spring默认使用JDK动态代理,只有在类没有实现接口时,才会使用CGLib。
Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。
JDK的动态代理存在限制,那就是被代理的类必须是一个实现了接口的类,代理类需要实现相同的接口,代理接口中声明的方法。若需要代理的类没有实现接口,此时JDK的动态代理将没有办法使用,于是Spring会使用CGLib的动态代理来生成代理对象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值