Spring系列四:AOP切面编程 第一部分

上文中, 我们学习到了 Spring系列三:基于注解配置bean

接下来我们学习, AOP切面编程

Spring项目
在这里插入图片描述

💗AOP-官方文档

🍝AOP 讲解


AOP 讲解: spring-framework-5.3.8\docs\reference\html/core.html
在这里插入图片描述

🍝AOP APIs


AOP APIs: spring-framework-5.3.8\docs\reference\html/core.html

在这里插入图片描述
spring-framework-5.3.8/docs/javadoc-api/index.html

在这里插入图片描述

💗动态代理

🍝初探动态代理

需求说明
1.由Vehicle (交通工具接口, 有一个run方法), 下面有两个类 Car 和 Ship
2.当运行Car对象的 run 方法 和 ship对象的 run 方法时, 输入如下内容

交通工具开始运行了…
轮船在海上航行…
交通工具停止运行了…

交通工具开始运行了…
小汽车在路上跑…
交通工具停止运行了…


解决方案一: 传统方案

1.新建com.zzw.spring.aop.proxy.Vehicle接口

//接口, 该接口有run方法
public interface Vehicle {
    void run();
}

2.新建com.zzw.spring.aop.proxy.Car

public class Car implements Vehicle {
    @Override
    public void run() {
        System.out.println("交通工具开始运行了....");
        System.out.println("小汽车在路上 running....");
        System.out.println("交通工具停止运行了....");
    }
}

3.新建com.zzw.spring.aop.proxy.Ship

public class Ship implements Vehicle {
    @Override
    public void run() {
        System.out.println("交通工具开始运行了....");
        System.out.println("大轮船在路上 running....");
        System.out.println("交通工具停止运行了....");
    }
}

4.新建com.zzw.spring.aop.proxy.TestVehicle

public class TestVehicle {
    @Test
    public void run() {
        //OOP基础=>java基础
        Vehicle vehicle = new Ship();
        //动态绑定
        vehicle.run();
    }
}

来思考一下, 这个解决方案好吗? ====> 代码冗余, 其实就是单个对象的调用, 并没有很好的解决.


解决方案二: 动态代理方式
动态代理解决思路: 在调用方法时, 使用反射机制, 根据方法去决定调用哪个对象方法

1.新建com.zzw.spring.aop.proxy.VehicleProxyProvider

public class VehicleProxyProvider {
    //定义一个属性
    //target_vehicle 表示真正要执行的对象
    //该对象实现了Vehicle接口
    private Vehicle target_vehicle;

    //构造器
    public VehicleProxyProvider(Vehicle target_vehicle) {
        this.target_vehicle = target_vehicle;
    }

    //编写一个方法, 可以返回一个代理对象
    public Vehicle getProxy() {

        //得到类加载器
        ClassLoader classLoader =
                target_vehicle.getClass().getClassLoader();

        //得到要代理对象/被执行对象 的接口信息, 底层是通过接口来完成调用
        Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();

        //创建InvocationHandler 对象
        //因为 InvocationHandler 是接口, 所以我们可以通过匿名对象的方式来创建该对象
        /**
         * public interface InvocationHandler {
         *     public Object invoke(Object proxy, Method method, Object[] args)
         *          throws Throwable;
         * }
         * invoke 方法是将来执行target_vehicle的方法时, 会调用到
         */

        InvocationHandler invocationHandler = new InvocationHandler() {
            /*
                class VehicleProxyProvider$01 implements InvocationHandler {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("交通工具开始运行了....");
                        //这里是我们的反射基础 => OOP
                        Object result = method.invoke(target_vehicle, args);
                        System.out.println("交通工具停止运行了....");
                        return result;
                    }
                }
                InvocationHandler invocationHandler = new VehicleProxyProvider$01();
             */

            /**
             * invoke 方法是将来执行我们的target_vehicle的方法时, 会调用到
             *
             * @param proxy  表示代理对象
             * @param method 就是通过代理对象调用方法时, 的那个方法 代理对象.run()
             * @param args   表示调用 代理对象.run(xx) 传入的参数
             * @return 表示 代理对象.run(xx) 执行后的结果.
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("交通工具开始运行了....");
                //这里是我们的反射基础 => OOP
                //method 是 public abstract void com.zzw.spring.aop.proxy.Vehicle.run()
                //target_vehicle 是 Ship对象
                //args 是 null
                //这里通过反射+动态绑定机制, 就会执行到被代理对象的方法
                //执行完毕就返回
                Object result = method.invoke(target_vehicle, args);
                System.out.println("交通工具停止运行了....");
                return result;
            }
        };

        /*
            public static Object newProxyInstance(ClassLoader loader,
                                                  Class<?>[] interfaces,
                                                  InvocationHandler h)
         解读
         1.Proxy.newProxyInstance() 可以返回一个代理对象
         2.ClassLoader loader: 类加载器,
         3.Class<?>[] interfaces 就是将来要代理的对象的接口信息
         4.InvocationHandler h 调用处理器/对象, 有一个非常重要的方法invoke
        */
        Vehicle proxy =
                (Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

        return proxy;
    }
}

2.修改com.zzw.spring.aop.proxy.Vehicle

public interface Vehicle {
    void run();

    public String fly(int height);
}

3.修改com.zzw.spring.aop.proxy.Ship

public class Ship implements Vehicle {
    @Override
    public void run() {
        //System.out.println("交通工具开始运行了....");
        System.out.println("大轮船在路上 running....");
        //System.out.println("交通工具停止运行了....");
    }

    @Override
    public String fly(int height) {
        return "轮船可以飞行 高度=" + height + "米";
    }
}

4.com.zzw.spring.aop.proxy.TestVehicle

public class TestVehicle {

    @Test
    public void proxyRun() {
        //创建Ship对象
        Ship ship = new Ship();
        //创建VehicleProxyProvider对象, 并且我们要传入代理的对象
        VehicleProxyProvider vehicleProxyProvider
                = new VehicleProxyProvider(ship);

        //获取代理对象, 该对象可以代理执行方法
        //解读
        //1.proxy 编译类型Vehicle,
        //2.运行类型 是代理类型, 即 class com.sun.proxy.$Proxy8
        Vehicle proxy = vehicleProxyProvider.getProxy();

        System.out.println("proxy的编译类型是 Vehicle");
        System.out.println("proxy的运行类型是" + proxy.getClass());
        //下面解读/debug怎么执行到 代理对象的 public Object invoke(Object proxy, Method method, Object[] args)
        //梳理完毕, proxy的编译类型是Vehicle, 运行类型是Proxy  class com.sun.proxy.$Proxy8
        //所以当执行run方法时, 会执行到 代理对象的invoke
        //如果体现动态 [1.被代理的对象 2.方法]
        //proxy.run();
        String result = proxy.fly(10000);
        System.out.println("result=" + result);
        System.out.println("ok");

    }

5.debug
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

🍝动态代理深入

需求说明
1.有一个SmartAnimal 接口, 可以完成简单的加减法, 要求在执行 getSum()getSub() 时, 输出执行前, 执行过程, 执行后的日志结果. 输出内容如下:

日志-方法名-getSum-参数 1.5 4.5
方法内部打印result = 6.0
日志-方法名-getSum-结果result= 6.0
=================================
日志-方法名-getSub-参数 1.4 3.3
方法内部打印result = -1.9
日志-方法名-getSub-结果result= -1.9


解决方案一: 传统方案

1.新建com.zzw.spring.aop.proxy2.SmartAnimalAble接口

public interface SmartAnimalAble {
    //求和
    float getSum(float i, float j);

    //求差
    float getSub(float i, float j);
}

2.com.zzw.spring.aop.proxy2.SmartCat

public class SmartCat implements SmartAnimalAble {
    @Override
    public float getSum(float i, float j) {
        System.out.println("日志-方法名-getSum-参数 " + i + " " + j);
        float result = i + j;
        System.out.println("方法内部打印result = " + result);
        System.out.println("日志-方法名-getSum-结果result= " + (i + j));
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        System.out.println("日志-方法名-getSub-参数 " + i + " " + j);
        float result = i - j;
        System.out.println("方法内部打印result = " + result);
        System.out.println("日志-方法名-getSub-结果result= " + (i - j));
        return result;
    }
}

3.com.zzw.spring.aop.proxy2.AopTest

public class AopTest {
    @Test
    public void run() {
        SmartAnimalAble smartAnimalAble = new SmartCat();
        smartAnimalAble.getSum(1.5f, 4.5f);
        System.out.println("=================================");
        smartAnimalAble.getSub(1.4f, 3.3f);
    }
}

解决方案二: 动态代理方式
考虑代理对象调用方法(底层是反射调用)时, 可能出现的异常- [横切关注点]

1.新建com.zzw.spring.aop.proxy2.MyProxyProvider

//可以返回一个动态代理对象, 可以执行SmartCat对象的方法
public class MyProxyProvider {
    //这是一个属性, 是我们要执行的目标对象
    //该对象实现了SmartAnimalAble接口
    private SmartAnimalAble target_obj;

    //构造器
    MyProxyProvider(SmartAnimalAble target_obj) {
        this.target_obj = target_obj;
    }

    //编写一个方法, 可以返回一个代理对象
    //该代理对象可以执行目标方法
    public SmartAnimalAble getProxy() {
        //1.得到类加载器
        ClassLoader classLoader =
                target_obj.getClass().getClassLoader();
        //2.得到要执行的目标对象的接口信息
        Class<?>[] interfaces = target_obj.getClass().getInterfaces();
        //3.创建InvocationHandler 对象
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();//方法名
                Object result = null;
                try {
                    System.out.println("方法执行前-日志-方法名-" + name + "-参数 "
                            + Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知
                    //使用反射调用方法
                    result = method.invoke(target_obj, args);
                    System.out.println("方法执行正常结束-日志-方法名-" + name + "-结果result= "
                            + result);//这里从aop的角度看, 也是一个横切关注点-返回通知
                    return result;
                } catch (Exception e) {
                    e.printStackTrace();
                    //如果反射执行方法时, 出现异常, 就会进入到catch{}
                    System.out.println("方法执行异常-日志-方法名-" + name + "-异常类型="
                            + e.getClass().getName());//这里从aop的角度看, 又是一个横切关注点-异常通知
                } finally {//不管你是否出现了异常, 最终都会执行到 finally {}
                    //这里从aop的角度看, 还是一个横切关注点-最终通知
                    System.out.println("方法最终结束-日志-方法名-" + name);
                }
                return result;
            }
        };

        //创建代理对象
        SmartAnimalAble proxy =
                (SmartAnimalAble) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}

2.修改com.zzw.spring.aop.proxy2.SmartCat

public class SmartCat implements SmartAnimalAble {
    @Override
    public float getSum(float i, float j) {
        //System.out.println("日志-方法名-getSum-参数 " + i + " " + j);
        
        float result = i + j;
        System.out.println("方法内部打印result = " + result);
        
        //System.out.println("日志-方法名-getSum-结果result= " + (i + j));
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        //System.out.println("日志-方法名-getSub-参数 " + i + " " + j);
        
        float result = i - j;
        System.out.println("方法内部打印result = " + result);
        //
        System.out.println("日志-方法名-getSub-结果result= " + (i - j));
        return result;
    }
}

3.com.zzw.spring.aop.proxy2.AopTest

public class AopTest {

    @Test
    public void smartCatTestProxy() {
        //创建SmartCat对象
        SmartAnimalAble smartAnimalAble = new SmartCat();
        MyProxyProvider myProxyProvider
                = new MyProxyProvider(smartAnimalAble);
        //获取代理对象, 该对象可以代理执行方法
        SmartAnimalAble proxy = myProxyProvider.getProxy();

        System.out.println("proxy的编译类型是 SmartAnimalAble");
        System.out.println("proxy的运行类型是 " + proxy.getClass());
        //proxy的编译类型是SmartAnimalAble, 运行类型是 Class com.sun.proxy.$Proxy8
        //所以当执行getSum方法时, 会执行到 代理对象的invoke
        proxy.getSum(1.2f, 2.4f);
        System.out.println("=================================");
        proxy.getSub(1.3f, 4.5f);
        System.out.println("ok");
    }
}

🍝AOP问题提出

MyProxyProvider.java中, 我们的输出语句功能比较弱, 在实际开发中, 我们希望是以一个方法的形式, 嵌入到真正执行的目标方法前.

如图分析
在这里插入图片描述

📗使用土方法解决

需求分析
使用土方法解决前面的问题, 后面使用Spring的AOP组件完成

1.先建一个包, 把相关文件拷贝过来, 进行修改完成. ----这里只是模拟, 并没有真的新建包

//我们的一个方法, 在目标对象执行前执行
public void before(Method method, Object[] args) {
    System.out.println("before方法执行前-日志-方法名-" + method.getName() + "-参数 "
            + Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知
}

//我们的一个方法, 在目标对象执行后执行
public void after(Method method, Object result) {
    System.out.println("after方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "
            + result);//这里从aop的角度看, 也是一个横切关注点-返回通知
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String name = method.getName();//方法名
    Object result = null;
    before(method, args);
    //使用反射调用方法
    result = method.invoke(target_obj, args);
    after(method, result);
    return result;
} 

2.该方法问题分析: 耦合度高

📗 对土方法解耦-开发最简单的AOP类

1.新建com.zzw.spring.aop.proxy2.ZzwAOP

public class ZzwAOP {
    //我们的一个方法, 在目标对象执行前执行
    public static void before(Method method, Object[] args) {
        System.out.println("ZzwHsp-方法执行前-日志-方法名-" + method.getName() + "-参数 "
                + Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知
    }

    //我们的一个方法, 在目标对象执行后执行
    public static void after(Method method, Object result) {
        System.out.println("ZzwHsp-方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "
                + result);//这里从aop的角度看, 也是一个横切关注点-返回通知
    }
}

2.修改com.zzw.spring.aop.proxy2.MyProxyProvider

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String name = method.getName();//方法名
    Object result = null;
    try {
        //before(method, args);
        ZzwAOP.before(method, args);

        //使用反射调用方法
        result = method.invoke(target_obj, args);

        //after(method, result);
        ZzwAOP.after(method, result);
        
        return result;
    } catch (Exception e) {
    }
}

📗 土方法缺点

土方法 不够灵活;
土方法 复用性差;
土方法 是一种硬编码 (因为没有注解和反射支撑)

Spring AOP 闪亮登场 - 底层是ASPECTJ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值