SpringBoot整合aspectj实现面向切面编程(即AOP)
前言
“面向切面编程”,这样的名字并不是非常容易理解,且容易产生一些误导。但在实际业务中,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)的更多相关文章
- 依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦(转good)
依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦.所谓横切关注点,即影响应用多处的功能,这些功能各个应用模块都需要,但又不是其主要关注点,常见 ...
- Spring——面向切面编程(AOP)详解
声明:本博客仅仅是一个初学者的学习记录.心得总结,其中肯定有许多错误,不具有参考价值,欢迎大佬指正,谢谢!想和我交流.一起学习.一起进步的朋友可以加我微信Liu__66666666 这是简单学习一遍之 ...
- Spring(三)面向切面编程(AOP)
在直系学长曾经的指导下,参考了直系学长的博客(https://www.cnblogs.com/WellHold/p/6655769.html)学习Spring的另一个核心概念--面向切片编程,即AOP ...
- Spring 面向切面编程(AOP)
Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of ...
- 面向切面编程(AOP)及其作用
在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用. 1.面向切面编程(AOP) 面向切面编程(AOP)就是对软件系统不同关注点的分离,开发者通过拦截方法调用并在方法调用前后添加辅助代码. ...
- C# 中使用面向切面编程(AOP)中实践代码整洁
1. 前言 最近在看<架构整洁之道>一书,书中反复提到了面向对象编程的 SOLID 原则(在作者的前一本书<代码整洁之道>也是被大力阐释),而面向切面编程(Aop)作为面向对象 ...
- C# 中使用面向切面编程(AOP)中实践代码整洁(转)
出处:https://www.cnblogs.com/chenug/p/9848852.html 1. 前言 最近在看<架构整洁之道>一书,书中反复提到了面向对象编程的 SOLID 原则( ...
- (转存)面向切面编程(AOP)的理解
面向切面编程(AOP)的理解 标签: aop编程 2010-06-14 20:17 45894人阅读 评论(11) 收藏 举报 分类: Spring(9) 在传统的编写业务逻辑处理代码时,我们通常 ...
- 面向切面编程 (AOP )
什么是面向切面编程? 面向切面编程就是(AOP --- aspect-oriented programming), 在百科上说: 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一 ...
- 解耦与分离 —— 面向切面编程(AOP)
家里的电表总结起来有两大特性: 电视机需要(电量管理),空调需要(电量管理),热水器也需要电量管理,即一组对象都需要某一功能特性: 电视机根据信号输出画面,空调吹出冷风,热水器将水加热,这些业务功能的 ...
随机推荐
- [转帖]Linux的缓存内存(cache memory)
PS:为什么Linux系统没运行多少程序,显示的可用内存这么少?其实Linux与Win的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做Cache Memory. 为什么Linux系统没运行多少程 ...
- [转帖]renice和nice
https://www.cnblogs.com/qiynet/p/17555881.html 将行程 id 为 987 及 32 的行程与行程拥有者为 daemon 及 root 的优先序号码加 1 ...
- [转帖]AMD处理器ZEN一代之国产化海光
https://huataihuang.gitbook.io/cloud-atlas-draft/os/linux/kernel/cpu/amd_hygon 2020年国产化处理器受到了广泛的关注 ...
- [转帖]Linux之bash反弹shell原理浅析
环境 攻击机:kali ip:192.168.25.144 靶 机:centos ip:192.168.25.142 过程 kali 监听本地8888端口 靶机 ...
- [转帖]CentOS7完美升级gcc版本方法
https://blog.whsir.com/post-4975.html 在某些应用场景中,需要特定的gcc版本支持,但是轻易不要去编译gcc.不要去编译gcc.不要去编译gcc,我这里推荐使用红帽 ...
- Codeforces round 919 (div2)
Problem - A - Codeforces 暴力枚举 就可以: #include <bits/stdc++.h> #define int long long using namesp ...
- pycharm提交代码到gitee
1.在pycharm中下载gitee插件,打开pycharm进入settings页面,查看当前页面version control下是否 有gitee,要是没有点击plugins,在搜索框中搜索gite ...
- 谈JVM参数GC线程数ParallelGCThreads合理性设置
作者:京东零售 刘乐 导读:本篇文章聚焦JVM参数GC线程数的合理配置,从ParallelGCThreads参数含义.参数设置,到参数实验以及修改意见进行解析. 1. ParallelGCThread ...
- web字体小于12px的解决办法
大家都知道,web端的字体在正常情况下,最小只能够是12px; 但是有些时候,可能需要字体小于12px 那么如何解决这个办法了 可以使用css3的缩放属性scale 如果字体的大小是10px; 那么我 ...
- 【发现一个问题】使用 fastcgo 导致额外的 `runtime._System` 调用的消耗
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 为了避免 cgo 调用浪费太多资源,因此使用了 fastc ...