Spring(2)-粗解横切关注点

本文是Spring AOP的前置内容,过渡作用。备注:本文核心内容是韩顺平老师的课程,在此基础上整理的笔记和个人理解

需求

  1. 有一个 SmartAnimal 接口,可以完成简单的加减法, 要求在执行 getSum()和 getSub()时,输出执行前,执行过程,执行后的日志输出,请思考如何实现.
日志--方法名--getSum方法开始--参数:1.2,2.2
方法内部打印:result=3.4
日志--方法名--getSum 方法结束--结果:result=3.4
日志--方法名--getSub 方法开始--参数:1.0,2.0
方法内部打印:result=-1.0
日志--方法名--getSub 方法结束--结果:result=-1.0

传统方式实现

传统方式就是OOP,很简单,先写接口,再实现,最后测试,没有难度。

  1. SmartAnimalable 接口
public interface SmartAnimalable {  
    float getSum(float i, float j);  
    float getSub(float i, float j);  
}
  1. 实现接口类 SmartDog
public class SmartDog 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=" + result);  
        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=" + result);  
        return result;  
    }  
}
  1. 测试
public class AopTest {  
    public static void main(String[] args) {  
        SmartDog dog = new SmartDog();  
        dog.getSum(1.2f, 2.2f);  
        dog.getSub(1, 2);  
    }  
}

传统方式优点是实现起来简单直接,但是缺点是日志代码维护不方便,复用性很差

动态代理实现

我们在之前Spring(1)-粗解动态代理 - marigo - 博客园讲解了如何使用动态代理解决这个问题,不做过多解释直接上代码。此外,我们的getSub和getSum也有可能出现异常,所以在此还进行了异常处理,从而引出后面的横切关注点

  1. MyProxyProvider
public class MyProxyProvider {  
    private SmartAnimalable target_obj;  
  
    public 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 = new InvocationHandler(){  
            @Override  
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                Object result = null;  
                String methodName = method.getName();  
                try {  
                    // 在调用目标对象的方法之前,我们可以添加一些自己的操作  
                    System.out.println("日志--方法名--" + methodName + "方法开始--参数:" + Arrays.asList(args));  
                    // 调用目标对象的方法,反射  
                    result = method.invoke(target_obj, args);  
                    // 在调用目标对象的方法之后,我们可以添加一些自己的操作  
                    System.out.println("日志--方法名:" + methodName+ "--方法正常结束--结果:result=" + result);  
                }catch (Exception e){  
                    // 如果目标方法抛出异常,打印“方法异常”日志  
                    System.out.println("日志--方法名:" + methodName+ "--方法异常结束--异常:" + e);  
                }finally {  
                    // 如果目标方法正常结束或者异常结束,打印“方法结束”日志  
                    System.out.println("日志--方法名:" + methodName+ "--方法结束");  
                }  
                return result;  
            }  
        };  
        // 4.创建代理对象  
        SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);  
        return proxy;  
    }  
}
  1. SmartDog类,我们可以将其中冗余的内容删除
public class SmartDog implements SmartAnimalable{  
  
    @Override  
    public float getSum(float i, float j) {  
        float result = i + j;  
        return result;  
    }  
  
    @Override  
    public float getSub(float i, float j) {  
        float result = i - j;  
        return result;  
    }  
}
  1. 测试
public class AopTest {  
    public static void main(String[] args) {  
        SmartAnimalable smartDog = new SmartDog();  
        MyProxyProvider myProxyProvider = new MyProxyProvider(smartDog);  
        smartDog = myProxyProvider.getProxy();  
        smartDog.getSum(1, 2);  
        smartDog.getSub(1, 2);  
    }  
}

动态代理存在的问题:我们的输出语句功能比较弱,实际开发过程中,我们是希望通过一个方法的形式插入到执行目标方法的前后。

老韩微微一笑(土办法)

解决上面动态代理存在的问题,我们用土办法尝试解决。
需要一提的是,在前面动态代理部分其实有四个横切关注点,目标方法也就是method.invoke() 之前的部分叫前置通知,后面的是返回通知,catch处理部分是异常通知,finally部分是最终通知。那么是谁来关注,是我们的以后要学的AOP编程关注。
image.png
这四个横切关注点的日志输出功能很弱,我们将其拿下,换成方法。比如,我们对前置通知部分,拿下后,新建一个方法 before() ,参数如何确定的呢,看方法体需要什么参数就填什么。

public void before(Object proxy, Method method, Object[] args){  
    // 调用目标方法前打印日志  
    System.out.println("before 日志--方法名:" + method.getName() + "--方法开始--参数:" + Arrays.asList(args));  
}

同理,后置通知拿下,换成对应的 after() 方法:

public void after(Method method, Object result){  
    // 调用目标方法后打印日志  
    System.out.println("after 日志--方法名:" + method.getName()+ "--方法正常结束--结果:result=" + result);  
}

于是,前置通知和返回通知都是以方法体的形式嵌入,异常通知和最终通知也可以被替换:
image.png
比较好理解,没有难度,那么这种土方法有什么问题呢?这些方法都放在了一个类中,耦合度高。

AOP类雏形

我们需要对以上的土办法进行解耦。

  1. before()after() 拿到一个类中:
public class MyAOP {  
    public static void before(Object proxy, Method method, Object[] args){  
        // 调用目标方法前打印日志  
        System.out.println("before 日志--方法名:" + method.getName() + "--方法开始--参数:" + Arrays.asList(args));  
    }  
  
    public static void after(Method method, Object result){  
        // 调用目标方法后打印日志  
        System.out.println("after 日志--方法名:" + method.getName()+ "--方法正常结束--结果:result=" + result);  
    }  
}
  1. 修改 MyProxyProvider中before()after() 的调用方法
InvocationHandler invocationHandler = new InvocationHandler(){  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        Object result = null;  
        String methodName = method.getName();  
        try {  
            // 切在目标方法之前  
            MyAOP.before(proxy, method, args);  
            // 调用目标对象的方法,反射  
            result = method.invoke(target_obj, args);  
            // 切在目标方法之后  
            MyAOP.after(method, result);  
        }catch...
    }  
};

由此,一个简单得不能简单的AOP雏形产生了:写了一个类,其中有很多静态方法,这些静态方法再切入到其他某个类的方法中。
当然这还算不上AOP,存在很多问题:不够灵活,复用性差,比如SmartDog类中有两个方法,如果before或者after方法只对其中一个类进行控制就显得不顺。
我们后续会对Spring AOP进行更具体地讲解。

posted @ 2024-04-27 19:59  marigo  阅读(3)  评论(0编辑  收藏  举报