《从零开始的java从入门到入土的学习生活——JavaWeb后端篇》Chapter18——JavaWeb后端篇学习记录——AOP 面向切面编程

一、AOP

1、什么是AOP

AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。

使用AOP的优势:

|------------|------------------------------------------------|
| 减少重复代码 | 不需要在业务方法中定义大量的重复性的代码,只需要将重复性的代码抽取到AOP程序中即可。 |
| 代码无侵入 | 在基于AOP实现这些业务功能时,对原有的业务代码是没有任何侵入的,不需要修改任何的业务代码。 |
| 提高开发效率 | |
| 维护方便 | |

2、AOP快速入门

①导入依赖项

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

②编写代码

java 复制代码
@Slf4j
@Aspect
@Component
public class RecordTimeAspect {
    @Around("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
    public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
        // 1. 记录开始时间
        long begin = System.currentTimeMillis();

        //执行原始方法
        Object result = pjp.proceed();

        //记录方法执行结束时间
        long end = System.currentTimeMillis();

        //计算方法执行耗时
        log.info("方法执行耗时 {} ms", end - begin);
        //返回原始方法执行结果
        return result;
    }
}

打开网页访问部门页面,结果如下:

由此可以得知,AOP的常见应用场景:记录系统的操作日志、权限控制、事务管理(下一章就更)。

3、AOP的核心概念

①连接点:JoinPoint

可以被AOP控制的方法(暗含方法执行时的相关信息),在SpringAOP提供的JoinPoint当中,封装了连接点方法在执行时的相关信息。

②通知-Advice

指哪些重复的逻辑,也就是共性功能(最终体现为一个方法),在AOP面向切面编程当中,我们只需要将这部分重复的代码逻辑抽取出来单独定义。抽取出来的这一部分重复的逻辑,也就是共性的功能。

③切入点-PointCut

匹配连接点的条件,通知仅会在切入点方法执行时被应用,在aop的开发当中,我们通常会通过一个切入点表达式来描述切入点.

④切面-Aspect

描述通知与切入点的对应关系(通知+切入点)

⑤目标对象

目标对象指的就是通知所应用的对象

注意: Spring的AOP底层是基于动态代理技术来实现的,也就是说在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强。

4、AOP进阶

①通知类型

Spring提供了**@PointCut**注解,该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可

java 复制代码
@Slf4j
@Component
@Aspect
public class MyAspect1 {

    @Pointcut("execution(* com.itheima.service.*.*(..))")
    private void pt() {}

    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint) {
        log.info("before.....");
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("around before.....");
        Object result = pjp.proceed();
        log.info("around after.....");
        return result;
    }

    //后置通知
    @After("pt()")
    public void after(JoinPoint joinPoint) {
        log.info("after.....");
    }

    //返回后通知(程序正常执行的情况下,会执行的后置通知)
    @AfterReturning("pt()")
    public void afterReturning(JoinPoint joinPoint) {
        log.info("afterReturning.....");
    }

    //异常通知(程序执行过程中出现异常,会执行的后置通知)
    @AfterThrowing("pt()")
    public void afterThrowing(JoinPoint joinPoint) {
        log.info("afterThrowing.....");
    }
}

启动服务后,结果如下:

若程序中出现了异常,将触发异常通知,如下:

注意:@AfterReturning标识的通知方法不会执行,@AfterThrowing标识的通知方法执行了;@Around环绕通知中原始方法调用时有异常,通知中的环绕后的代码逻辑也不会在执行了 (因为原始方法调用已经出异常了)

②通知顺序

在不同切面类中,默认按照切面类的类名字母排序。

控制通知的执行顺序有两种方式:修改切面类的类名(这种方式非常繁琐、而且不便管理);

使用Spring提供的@Order注解(前置通知:数字越小先执行; 后置通知:数字越小越后执行)。

定义几个切面类:

java 复制代码
@Slf4j
@Component
@Aspect
@Order(2)
public class MyAspect2 {
    //前置通知
    @Before("execution(* com.itheima.service.impl.*.*(..))")
    public void before(){
        log.info("MyAspect2 -> before ...");
    }

    //后置通知
    @After("execution(* com.itheima.service.impl.*.*(..))")
    public void after(){
        log.info("MyAspect2 -> after ...");
    }
}
java 复制代码
@Slf4j
@Component
@Aspect
@Order(8)
public class MyAspect3 {
    //前置通知
    @Before("execution(* com.itheima.service.impl.*.*(..))")
    public void before(){
        log.info("MyAspect3 -> before ...");
    }

    //后置通知
    @After("execution(* com.itheima.service.impl.*.*(..))")
    public void after(){
        log.info("MyAspect3 ->  after ...");
    }
}
java 复制代码
@Slf4j
@Component
@Aspect
@Order(5)
public class MyAspect4 {
    //前置通知
    @Before("execution(* com.itheima.service.impl.*.*(..))")
    public void before(){
        log.info("MyAspect4 -> before ...");
    }

    //后置通知
    @After("execution(* com.itheima.service.impl..*(..))")
    public void after(){
        log.info("MyAspect4 -> after ...");
    }
}

启动服务后:

**总结:**不同的切面类当中,默认情况下通知的执行顺序是与切面类的类名字母排序是有关系的。可以在切面类上面加上@Order注解,来控制不同的切面类通知的执行顺序。

③切入点表达式

描述切入点方法的一种表达式

常见形式:

|------------------------------|
| execution(......):根据方法的签名来匹配 |
| @annotation(......) :根据注解匹配 |

execution
java 复制代码
execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

其中带?的表示可以省略的部分:

  • 访问修饰符:可省略(比如: public、protected)

  • 包名.类名: 可省略

  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

@annotation自定义注解

实现步骤:编写自定义注解,然后在业务类要做为连接点的方法上添加自定义注解。

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogOperation {
}

切面类:

java 复制代码
@Slf4j
@Component
@Aspect
@Order(5)
public class MyAspect5 {
    //前置通知
    @Before("@annotation(com.itheima.anno.LogOperation)")
    public void before(){
        log.info("MyAspect5 -> before ...");
    }

    //后置通知
    @After("@annotation(com.itheima.anno.LogOperation)")
    public void after(){
        log.info("MyAspect5 -> after ...");
    }
}

启动服务后:

总结:

相关推荐
周末也要写八哥7 分钟前
多进程和多线程的特点和区别
java·开发语言·jvm
惜茶1 小时前
vue+SpringBoot(前后端交互)
java·vue.js·spring boot
diablobaal1 小时前
云计算学习100天-第102天-Azure入门4
学习·云计算·azure
AI_零食1 小时前
Flutter 框架跨平台鸿蒙开发 - 自定义式按钮设计应用
学习·flutter·ui·华为·harmonyos·鸿蒙
小陈phd1 小时前
多模态大模型学习笔记(三十)—— 基于YOLO26 Pose实现车牌检测
笔记·学习
杰克尼2 小时前
springCloud_day07(MQ高级)
java·spring·spring cloud
野指针YZZ2 小时前
XV6操作系统:trap机制学习笔记
笔记·学习
diygwcom2 小时前
学习开源数据采集与监视控制SCADA-即工业组态开源框架FUXA
学习·开源
zl_dfq3 小时前
Python学习5 之【字符串】
python·学习
NHuan^_^3 小时前
SpringBoot3 整合 SpringAI 实现ai助手(记忆)
java·人工智能·spring boot