首页 > 其他分享 >Spring(2)-粗解横切关注点

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

时间:2024-04-27 20:13:38浏览次数:22  
标签:粗解 -- Spring float result 日志 横切 方法 public

本文是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进行更具体地讲解。

标签:粗解,--,Spring,float,result,日志,横切,方法,public
From: https://www.cnblogs.com/marigo/p/18162431

相关文章

  • Spring(3)-AOP快速入手
    经过前面Spring(1)-粗解动态代理-marigo-博客园Spring(2)-粗解横切关注点-marigo-博客园两篇内容,我们可以引入AOP了。AOP的简单理解AOP的全称(aspectorientedprogramming),面向切面编程。我们在此之前接触的更多是OOP,也就是面向对象编程。OOP和AOP有什么异同,网上有......
  • Spring(4)-AOP使用细节
    有了Spring(3)-AOP快速入手-marigo-博客园的学习,大体知道AOP的使用,接下来我们对AOP的细节进行展开。AOP-切入表达式作用:通过表达式定位一个或者多个连接点连接点可以理解成我们要切入到哪个类的哪个具体方法语法:execution([权限修饰符][返回值类型][简单类名/全类名][......
  • (一)spring beans
    1.beanDefinition首先是承载class的载体,里面包含了许多如是否单例,属性值等内容。以下只是建议代码,重在理解概念packageorg.springframework.spring.beans.factory.config;/***@ClassName:BeanDefinition//类名*@Description://描述*@Author:10300//作者......
  • Java(1)-粗解动态绑定
    Java的动态绑定机制是OOP中一个非常核心的概念。要理解动态绑定需要从Java的对象和类说起。当我们在创建一个类的时候,实际上就是在定义一种新的数据类型。类中可以包含属性和方法,基于这个类创建一个对象的时候,这个对象就有拥有该类所有的属性和方法。在引出动态绑定的概念之前,可......
  • Java(2)-粗解类加载器
    Java的类加载器是Java运行时环境中的重要组件,核心功能是将类的字节码加载到Java虚拟机中。举个例子可以通过一个图书馆的比喻来形象地解释类加载器的作用、用法和使用场景。想象一下,有一个巨大的图书馆(JVM),其中有非常多的藏书(类)。当你(程序)需要阅读一本书(使用一个类)时,你首先需要......
  • SpringBoot集成minio前后端联调
    基本配置初始化项目新建一个SpringBoot项目,集成lombokmybatis-plusminiohutool-core(可有可无)。新建一个数据表attachement,用于存储文件上传后在minio中的位置。droptableifexistsattachment;createtableattachment(idintauto_increment......
  • spring-securty-oauth2使用例子
    oauth2概念https://www.cnblogs.com/LQBlog/p/16996125.html环境搭建1.引入依赖<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></depen......
  • spring boot
    链接:https://pan.baidu.com/s/1quiC-bqO5s3KgoLT5MWX7Q?pwd=412p提取码:412p1.Springboot入门springboot-简化了开发-比如-我们之前导入依赖--到需要自己写配置类-返回Beanspringboot帮我们简化了这个工程SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想sp......
  • 有意思!一个关于 Spring 历史的在线小游戏
    发现SpringOne的官网上有个好玩的彩蛋,分享给大家!进到SpringOne的官网,可以看到右下角有个类似马里奥游戏中的金币图标。点击该金币之后,会打开一个新的页面,进入下面这样一个名为:TheHistoryOfSpring的在线小游戏你可以使用上下左右的方向键来控制Spring的Logo一步步经历......
  • 一个通用的SpringBoot项目响应实体类Response
    packagecom.luky.vo;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importlombok.ToString;importorg.springframework.http.HttpStatus;@Data@ToString@AllArgsConstructor@NoArgsConstructorpublicclassResponse&......