前言

“面向切面编程”,这样的名字并不是非常容易理解,且容易产生一些误导。但在实际业务中,AOP有着广泛的用途,比如日志记录,性能统计,安全控制,事务处理,异常处理等等。

举些栗子

场景1、现有一个系统,已经运行了几个月,项目经理想要统计每个接口的耗时情况,用来分析系统性能,进而重构代码进行优化;

场景2、有一个管理后台,存在一些修改、删除的敏感操作,产品经理为了减少风险要求每一次的修改、删除操作都需要记录在数据库中,不仅要记录操作人,还要记录入参出参

场景3、由于系统在运行中,不可避免的会出现一些异常,但是一出现异常就会有很多报错日志,但这些日志是不能返回给前端的,需要统一处理这些异常,返回前端的只能是“系统出现一些小问题”这样的文案;

...

难点分析&解决方案

上面的场景都是真实存在的需求,但是如果不能统一处理的话,基本都是一改一大片,除了对业务代码有很强的侵入性,而且难以保证不出问题。

所以为了解决这种需求,AspectJ框架应运而生。不过SpringBoot官方也推出了 Spring AOP, 具体对比我就不赘述了,详细对比可以参https://www.jianshu.com/p/872d3dbdc2ca

开发步骤

1、引入依赖

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>

2、创建切面类并加上@Component注解

这一步相当于把切面类的管理权交给了Spring容器,让Spring容器负责该对象的创建与销毁,我们负责使用就行。

3、指定@Pointcut,可以是方法也可以是注解

这里说一下Pointcut execution规则

  • 任意公共方法的执行:

    execution(public * *(..))

    public可以省略, 第一个* 代表方法的任意返回值 第二个参数代表任意包+类+方法 (..)任意参数

  • 任何一个以“set”开始的方法的执行:

    execution(* set*(..))

  • UserService接口的任意方法:

    execution(* com.example.springbootaop.UserService.*(..))

  • 定义在com.coffee.service包里的任意方法的执行:execution(* com.example.springbootaop.*.*(..))

    第一个 .* 代表任意类, 第二个 .* 代表人以方法

  • 定义在service包和所有子包里的任意类的任意方法的执行:

    execution(* com.example.springbootaop.service..*.*(..))

    ..* 代表任意包或者子包

  • 定义在com.example.springbootaop包和所有子包里的UserService类的任意方法的执行:

    execution(* com.example.springbootaop..UserService.*(..))")

4、五大通知注解

尝试一下

1、配置文件

SpringBoot项目pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SpringBoot-aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot-aop</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

2、项目代码

项目结构

SpringBootAopApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class SpringBootAopApplication { public static void main(String[] args) {
SpringApplication.run(SpringBootAopApplication.class, args);
} }

TestController.java

import com.example.springbootaop.log.Log;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController
@RequestMapping("/aop")
public class TestController { @GetMapping("/a")
@Log(desc = "接口a的描述")
public String a() {
return "我是接口a";
} @GetMapping("/b")
@Log(desc = "接口b的描述")
public String b(String param1) {
System.out.println("打印参数:" + param1);
return "我是接口b";
} @PostMapping("/c")
@Log(desc = "接口c的描述")
public String c(Map<String, String> stringMap) {
System.out.println("打印参数:" + stringMap);
return "我是接口c";
}
}

ControllerLogAspect.java

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @Aspect
@Component
@Order(1)
public class ControllerLogAspect {
private static final Logger LOG = LoggerFactory.getLogger(ControllerLogAspect.class); @Pointcut("execution(* com.example.springbootaop.controller..*.*(..))")
private void controllerMethod() {
} @Around("controllerMethod()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
// 获取请求相关信息
String url = request.getRequestURL().toString();
String method = request.getMethod();
String uri = request.getRequestURI();
String params = request.getQueryString();
LOG.info("url:[{}];method:[{}];uri:[{}];params:[{}]", url, method, uri, params);
return joinPoint.proceed();
}
}

Log.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log { String desc() default "";
}

LogAspect.java

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Aspect
@Component
@Order(2)
public class LogAspect { private static final Logger LOG = LoggerFactory.getLogger(LogAspect.class); @Pointcut("@annotation(com.example.springbootaop.log.Log)")
private void pointCut() { } @Around("pointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取注解
Log logAnnotation = method.getAnnotation(Log.class);
LOG.info("当期方法的注解为:[{}]", logAnnotation.desc());
return joinPoint.proceed();
}
}

3、项目总结

上面的项目使用了两种切面

(1)ControllerLogAspect

ControllerLogAspect会扫描com.example.springbootaop.controller包下所有的方法,这种方式可以用在性能统计、事务处理等场景。

(2)Log注解 + LogAspect

这种方式扫描的不再是某个包,而是某个注解,Pointcut代码如下

@Pointcut("@annotation(com.example.springbootaop.log.Log)")
private void pointCut() {
}

配合注解一起使用,更加清晰且灵活,每一个方法都可以配置自己的信息,这种一般会用在日志记录、异常处理等场景。

在idea中如果是切面的话,出现如下图标,点击就可以看到所有被切入的方法:

SpringBoot整合aspectj实现面向切面编程(即AOP)的更多相关文章

  1. 依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦(转good)

    依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦.所谓横切关注点,即影响应用多处的功能,这些功能各个应用模块都需要,但又不是其主要关注点,常见 ...

  2. Spring——面向切面编程(AOP)详解

    声明:本博客仅仅是一个初学者的学习记录.心得总结,其中肯定有许多错误,不具有参考价值,欢迎大佬指正,谢谢!想和我交流.一起学习.一起进步的朋友可以加我微信Liu__66666666 这是简单学习一遍之 ...

  3. Spring(三)面向切面编程(AOP)

    在直系学长曾经的指导下,参考了直系学长的博客(https://www.cnblogs.com/WellHold/p/6655769.html)学习Spring的另一个核心概念--面向切片编程,即AOP ...

  4. Spring 面向切面编程(AOP)

    Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of ...

  5. 面向切面编程(AOP)及其作用

    在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用. 1.面向切面编程(AOP) 面向切面编程(AOP)就是对软件系统不同关注点的分离,开发者通过拦截方法调用并在方法调用前后添加辅助代码. ...

  6. C# 中使用面向切面编程(AOP)中实践代码整洁

    1. 前言 最近在看<架构整洁之道>一书,书中反复提到了面向对象编程的 SOLID 原则(在作者的前一本书<代码整洁之道>也是被大力阐释),而面向切面编程(Aop)作为面向对象 ...

  7. C# 中使用面向切面编程(AOP)中实践代码整洁(转)

    出处:https://www.cnblogs.com/chenug/p/9848852.html 1. 前言 最近在看<架构整洁之道>一书,书中反复提到了面向对象编程的 SOLID 原则( ...

  8. (转存)面向切面编程(AOP)的理解

    面向切面编程(AOP)的理解 标签: aop编程 2010-06-14 20:17 45894人阅读 评论(11) 收藏 举报  分类: Spring(9)  在传统的编写业务逻辑处理代码时,我们通常 ...

  9. 面向切面编程 (AOP )

    什么是面向切面编程? 面向切面编程就是(AOP --- aspect-oriented programming), 在百科上说: 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一 ...

  10. 解耦与分离 —— 面向切面编程(AOP)

    家里的电表总结起来有两大特性: 电视机需要(电量管理),空调需要(电量管理),热水器也需要电量管理,即一组对象都需要某一功能特性: 电视机根据信号输出画面,空调吹出冷风,热水器将水加热,这些业务功能的 ...

随机推荐

  1. [转帖]k8s对接ceph,ceph-csi方式

    1.上传ceph-csi-yaml和ceph-csi-image 两个文件夹到服务器 2.加载 ceph-csi-image里面的镜像 3.将加载好的镜像上传到本地harbor上. 4.修改ceph- ...

  2. [转帖]jemalloc 性能测试

    https://wenfh2020.com/2020/07/30/jemalloc/   jemalloc 是一个优秀的内存分配器,通过与系统默认的内存分配器进行比较:jemalloc 内存分配性能比 ...

  3. [转帖]Linux常用的一些命令,看你知道多少?

    https://zhuanlan.zhihu.com/p/115279009 Linux中命令有很多,而Linux系统中使用命令也是它的一大特点.在Linux系统中使用命令处理问题灵活,高效,所以熟知 ...

  4. Stream的简单学习

    Stream的简单学习 前言 https://github.com/jeffhammond/STREAM unzip STREAM-master.zip cd /STREAM-master/ make ...

  5. [转帖]Systemd 指令

    一.由来 历史上,Linux 的启动一直采用init进程. 下面的命令用来启动服务. $ sudo /etc/init.d/apache2 start # 或者 $ service apache2 s ...

  6. [转帖]写给想了解"集成电路"的朋友

    https://zhuanlan.zhihu.com/p/602627000 寒假和朋友小聚,每当就专业问题展开谈话,很容易形成"一边热火朝天,一边大脑宕机"的局面.俗话说隔行如隔 ...

  7. 在线获取所有依赖rpm包的方法

    背景 现在经常有一些不能上网的LInux机器但是需要安装一些软件. 但是有时候经常因为有依赖关系找不到的情况比较麻烦. 或者是一些公司的网络总是受限,网络速度非常慢. 下载安装非常折磨人. 这个时候就 ...

  8. 金蝶Cosmic虚拟机简单使用与总结

    背景 知己知彼 简单学习下友商发出来的测试软件 看看有否对自己现在的工作有所指导 也看看对方的部署方式有啥优缺点 当然了仅是测试, 不是生产软件可能有失真. 注意 我没有测试序列号, 登录系统耗时很久 ...

  9. Oracle12c 快速启动命令设置

    Oracle12c 安装完成之后 一般不会自动启动需要进行一下简单的设置才可以. 方法也比较简单. 可以使用 oracle 自带的 dbstart的命令执行服务启动 需要注意的事项是: 第一修改一个参 ...

  10. Ant Design Vue 中Drawer自定头部的样式、内容部分的样式、弹出层的样式

    <a-drawer :title="myTitle" placement="right" :visible="visible" @cl ...