《从零开始的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 ...");
    }
}

启动服务后:

总结:

相关推荐
sjg200104141 小时前
GoFrame学习随便记4(待续)
学习
BUG?不,是彩蛋!1 小时前
从零到一掌握 K 线与技术指标:Java 实战教程 | MA, RSI, MACD 全解析
java·开发语言·spring boot·量化投资
lucky_chaichai1 小时前
学习《以openclaw为例介绍AI Agent的运作原理》
人工智能·学习
前端小趴菜~时倾1 小时前
python爬虫学习第二课-流程控制
爬虫·python·学习
qq_427940341 小时前
java-UI自动化selenium+TestNG
java·selenium·自动化
biter down2 小时前
C++ 设计不可被继承的类
java·开发语言·c++
于先生吖2 小时前
Java 智慧社区本地生活系统:上门服务 + 商城模块完整开发
java·大数据·生活
摇滚侠2 小时前
Java 项目教程《尚庭公寓-下》,单体架构项目,从开发到部署
java·开发语言·架构
浅念-2 小时前
C++ 异常
开发语言·数据结构·数据库·c++·经验分享·笔记·学习