首页 > 编程语言 >java干货 spring aop的理解和使用

java干货 spring aop的理解和使用

时间:2024-06-21 20:28:19浏览次数:15  
标签:增强 异常 java spring aop 日志 方法 public

文章目录

一、AOP 概念

1.1 aop 思想

APO (面向切面编程) 是一种编程思想,它通过将通用的横向关注点(日志、事务、权限控制等)与业务逻辑分离,实现解耦,使得代码更易于维护。核心就是将非核心代码抽取出来,在实际运行时将代码切回业务代码。如何切回?使用cglib动态代理jdk动态代理实现。

1.2 aop 应用场景

  • 日志记录:在系统中记录日志是非常重要的,可以使用aop来实现记录日志的功能,可以在业务方法执行前、执行后或者抛出异常时记录日志
  • 事务处理:在数据库操作中,使用事务可以保证数据的一致性,可以使用aop 来实现处理事务的功能,可以在方法开始前开启事务,在方法执行完毕后提交事务,或者在抛出异常时回滚事务
  • 安全控制:在系统中包含某些需要安全控制的操作,如登录、授权等,可以使用aop 实现安全控制的功能 ,在方法执行前进行权限判断,如果用户没有权限,则抛出异常或者转到错误页面,防止未经授权的访问
  • 异常处理: 系统中可能会出现各种异常,如空指针异常、数据库链接异常等,可以使用aop 来实现处理异常的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志发送邮件等)
  • 性能监控: 在系统中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈进行优化,可以使用aop 来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算执行时间并输出到日志中
  • 缓存控制:在系统中有些数据需要缓存起来以提高访问速度,可以使用aop 来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有直接返回,否则执行方法并将返回值存入缓存

二、aop 如何使用

2.1 八个核心名词

  • 横切关注点

    • 从每个方法中抽取出来的同一类非核心业务(如日志是一个业务,事务是一个业务)。在同一个项目,可以使用多个横向关注点,对同一个方法进行不同方面(日志、事务等)的的增强
    • AOP 把软件系统分为两个部分,核心关注点横切关注点,核心业务是核心关注点,与之关系不大的部分是横切关注点。
    • 横切关注点的特点是,它们经常发生在核心关注点的多处,而且各处基本相似,比如权限认证、日志、事务、异常等
  • 通知(也叫增强)

    • 每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法叫做通知方法增强方法
    • 前置通知:在被代理的目标的方法前执行
    • 返回通知: 在被代理的目标方法成功后执行,可以修改返回值
    • 异常通知: 在被代理的目标方法异常结束后执行
    • 后置通知:在被代理的目标方法最终结束后执行,不可以修改返回值
    • 环绕通知: 使用 try…catch…finally 结构围绕整个被代理的目标方法,包括上面四种通知的所有位置
  • 连接点 (joinpoint)
    这是一个纯逻辑概念,指哪些可能被拦截到的点,也就被 通知增强 的各个点

  • 切入点 (pointcut)
    定位连接点的方式,可以理解为被选中的连接点。是一个表达式,比如execution(* com.spring.service.impl…(…))指定好了切入点,框架才知道在哪里进行增强

  • 切面(aspect)
    切入点和增强的结合,是一个类。在各个切入点进行增强,好比切西瓜,形成一个切面

  • 目标 target
    被代理的目标对象

  • 代理 proxy
    为了对目标对象应用通知而创建的对象,好比中介,目标是房东

  • 织入
    是指把通知应用到目标上,生成代理对象的过程,可以在编译时织入,也可以在运行时织入,spring 采用后者,也就是动态代理

2.2 代码实现

  • 加入依赖
    spring-context 已经集成了aop ,aop 只为 ioc 容器中的对象创建代理对象
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.6</version>
        </dependency>
 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>6.0.6</version>
        </dependency>
  • 步骤

    • 编写业务类
    • 定义增强类,在类中编写增强方法,获取核心业务方法信息(方法名,参数,访问修饰符,所属的类信息等),编写非核心业务逻辑,如日志输出等
    • 在增强方法加注解,指定切入点(通过切点表达式指明)以及增强的时机(前置,后置等),这样就知道在哪个类的哪个方法上进行怎么样的增强
    • 增强类上加**@Component注解**,注入ioc容器,加**@Aspect注解**,指定为切面
    • 编写配置类加@Configuration注解,指定为配置类,指定扫描包(加@ComponentScan注解),增强类也必须注入ioc 容器,开启aspectj aop支持(加@EnableAspectJAutoProxy注解)
    • 在junit 测试类上加上@SpringJUnitConfig(value = JavaConfig.class),指定配置类,那么spring 将自动为我们创建ioc 容器,而不用我们手动 new ClassPathXmlApplication
  • 补充

    • 配置类相当于一个.xml文件,含有扫描包的路径。
    • @SpringJUnitConfig(value = JavaConfig.class) 相当于 new ClassPathXmlApplication(JavaConfig.class)
    • @SpringJUnitConfig(locations = {“xxx.xml”}) 相当于 new ClassPathXmlApplication(“xxx.xml”)
    • @EnableAspectJAutoProxy 相当于在xml 中配置aop 标签,@ComponentScan注解 相当于 component-sacn标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.binbin.service"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • 编写业务类
    接口
public interface Calculator {
    public double div(int i, int j);
}

实现类

@Component
public class CalculatorImpl implements Calculator {
    @Override
    public Double div(int i, int j){
        try {
            System.out.println("业务方法--正在计算除法");
            double r = i / j;
            return r;
        }catch (Exception e){
            System.out.println("业务方法--出现异常");
            throw e; // 抛异常
        }finally {
            // 回收资源
            System.out.println("业务方法----finally!");
        }
    }

}
  • 编写增强类,也就是切面
@Component
@Aspect
public class LogAdvice {
    @Before(value = "execution(* com.binbin.service.impl.*.*(..))")
    public void atBefore(JoinPoint joinPoint){
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Method Name: " + methodName);
        System.out.println("Before-前置增强!");
    }

    @After(value = "execution(* com.binbin.service.impl.*.*(..))")
    public void atAfter(JoinPoint joinPoint){
        System.out.println("After-后置增强!");
    }

    @AfterReturning(value = "execution(* com.binbin.service.impl.*.*(..))",returning="res")
    public void atAfterReturning(JoinPoint joinPoint,Object res){
        System.out.println("AfterReturning -后置返回增强! 返回结果 res = " + res.toString());
    }

    @AfterThrowing(value = "execution(* com.binbin.service.impl.*.*(..))",throwing = "e")
    public void error(Exception e){
        System.out.println("AfterThrowing-异常处理增强!" + e);
    }
}
  • 编写配置类
@Configuration
@ComponentScan(basePackages = {"com.binbin.service","com.binbin.advice"})
@EnableAspectJAutoProxy // 开启aop 功能
public class JavaConfig {
}
  • 编写测试类
@SpringJUnitConfig(value = JavaConfig.class) // 由spring 创建ioc 容器,相当于 new ClassPathXmlApplication("xxx.xml") 或者 new ClassPathXmlApplication(xxx.class)
public class SpringTest {
    @Autowired
    Calculator calculator;
    @Test
    public void  test() {
        try {
            Double r = calculator.div(1, 1);
        }catch (Exception e){
            System.out.println();
        }

    }
}

调用calculator.div(1, 1):
依次触发 Before、AfterReturning、After,无异常

Method Name: div
Before-前置增强!
业务方法--正在计算除法
业务方法----finally!
AfterReturning -后置返回增强! 返回结果 res = 1.0
After-后置增强

调用calculator.div(1, 0):
依次触发 Before、AfterThrowing、After有异常

Method Name: div
Before-前置增强!
业务方法--正在计算除法
业务方法--出现异常
业务方法----finally!
AfterThrowing-异常处理增强!java.lang.ArithmeticException: / by zero
After-后置增强!

  • 小结
    执行顺序:前置增强、业务核心、返回值后置或异常增强、后置增强
    异常还是会传播到方法调用处

标签:增强,异常,java,spring,aop,日志,方法,public
From: https://blog.csdn.net/qq_51305563/article/details/139861076

相关文章

  • Java 抽象类
    目录1、什么是抽象类2、定义抽象类3、抽象类特性4、抽象类的作用1、什么是抽象类抽象类,顾名思义就是抽象的。该类没有包含足够的信息去描绘一个具体的对象,这样的类称为抽象类。抽象类着一种优化了的概念组织方式,它是所有子类的公共属性的集合,抽象类用来描述对象的一......
  • 【Java获取天气信息】
    获取天气信息需求源代码返回信息详解天气预报API城市代号需求页面需要显示天气信息,所以需要调用天气API获取天气信息,找了好多api,还是SOJSON的最方便最实惠(免费),不需要注册和申请秘钥。源代码直接贴代码,拿着即用(为了减少访问接口压力,这里使用了redis缓存4小时)......
  • JAVA每日总结day6.21
    ok了家人们,今天我们学习了面向对象中关键字的使用和抽象类,话不多说,我们一起看看吧,(今天终于星期五了,芜湖!!!)一,this和super关键字1,this关键字的三种用法1.1this的意义 this:表示当前对象this可以访问:本类的成员属性、成员方法、构造方法;1.2 this.成员变量(第一种)......
  • JavaScript算法之龟兔赛跑
    简介:龟兔赛跑算法,又称弗洛伊德循环检测算法,是一种在链表中非常常用的算法。它基于运动学和直觉的基本定律。本文旨在向您简要介绍该算法,并帮助您了解这个看似神奇的算法。假设高速公路上有两辆车。其中一辆的速度为x,另一辆的速度为2x。它们唯一能相遇的条件是它们都在循环......
  • SpringBoot配置Druid连接池
    简介:    连接池的作用是为了提高性能,将已经创建好的连接保存在池中,当有请求来时,直接使用已经创建好的连接对Server端进行访问。这样省略(复用)了创建连接和销毁连接的过程(TCP连接建立时的三次握手和销毁时的四次握手),从而在性能上得到了提高。Druid是一个JDBC组件,它包括三部......
  • SpringBoot+AOP+Redis自定义注解实现防重复提交
    1.哪些因素会引起重复提交?开发项目过程中可能会出现下面这些情况:前端下单按钮重复点击导致订单创建多次网速等原因造成页面卡顿,用户重复刷新提交请求黑客或恶意用户使用Postman等http工具重复恶意提交表单2.重复提交会带来哪些问题?重复提交带来的问题:会导致数据......
  • Java——访问修饰符
    一、访问修饰符是什么Java中的访问修饰符用于控制类、接口、构造函数、方法和数据成员(字段)的可见性和访问级别。Java提供了四种访问修饰符:访问修饰符同一类内同一包内不同包的子类不同包的非子类适用对象public可见可见可见可见类、接口、变量、方法protected可见可见可见......
  • OA自动化办公: springboot集成Activiti7
    一.场景引入    我在实际开发中遇到过这样一个需求:公民登录建议征集系统向大冶市某个人大代表提建议,但建议提出后人大代表未能及时响应,此时如果建议越来越多,就会导致建议被搁置。很多建议不会被处理,公民自然也就得不到反馈,这对于汇聚民生民意和征集建议显然是不利的。......
  • Java-英语字符串进行单词分割
    (摘自头歌)任务描述将一段英语字符串进行单词分割。相关知识需要掌握:如何将字符串进行分割。String.split()拆分字符串lang包String类的split()方法publicString[]split(Stringregex)publicString[]split(Stringregex,intlimit)//limit参数控制模式应用的次数,因......
  • java面向对象三大特征
     免责声明:java基础资料均来自于韩顺平老师的《循序渐进学Java零基础》教案,具体视频内容可以去B站观看,这些资料仅用于学习交流,不得转载用于商业活动1.Java面向对象三大特征Java面向对象编程有三大特征:封装、继承、多态1.1封装封装(encapsulation)就是把抽象出的数据【属性】......