Spring AOP 应用

1. 介绍

AOP:面向切面编程,对面向对象编程的一种补充。

AOP可以将一些公用的代码,自然的嵌入到指定方法的指定位置。

比如:

如上图,我们现在有四个方法,我们想在每个方法执行一开始,输出一个日志信息。但是这样做很麻烦,如果有100个、1000个方法,工作量会很大,而且难以维护。这时候就可以通过AOP进行解决。


2. 案例实战

2.1 需求分析及环境搭建

环境:SpringBoot + SpringBoot Web + SpringBoot AOP。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

目标:控制器业务方法,统一进行日志输出。

新建User类,包含idname属性。

新建UserController

@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/list")
public List<User> list(){
return Arrays.asList(
new User(1,"张三"),
new User(2,"李四"),
new User(3,"王五")
);
}
@GetMapping("/getById/{id}")
public User getById(@PathVariable("id") Integer id){
return new User(id,"张三");
}
@GetMapping("/deleteById/{id}")
public boolean deleteById(@PathVariable("id") Integer id){
return true;
}
}

此时,我们的目标就是使用AOP的方式,给这个listdeleteByIdgetById方法加上日志。

日志要包括调用方法的名称、返回值以及参数列表。

2.3 AOP实现

1. 首先我们要让AOP知道哪些方法需要被AOP处理 -> 通过注解方式进行处理

// 定义一个注解,来标记需要添加日志的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String value() default "";
}

定义好注解后,给需要使用日志的方法添加注解,如:

@LogAnnotation("查询用户")   // 标记目标方法
@GetMapping("/getById/{id}")
public User getById(@PathVariable("id") Integer id){
return new User(id,"张三");
}

2. 实现切面任务

新建LogAspect类,这就是生成切面对象的类。我们需要用@Component注解进行标注,交给IOC容器进行管理。此外,我们要用@Aspect注解标注其为一个切面。

然后,我们要将这个切面与我们刚刚标注的@LogAnnotation注解建立联系,让切面知道从哪个位置进行切入。实现的方法为,新建一个方法,然后给这个方法添加@Pointcut("@annotation(自定义注解的全类名)")。这样就成功建立的联系。

确定切入点后,我们就可以写切面的实际任务了。新建一个方法around。此时,我们要将确定切点的方法与切面实际处理任务的方法进行关联。实现的方法为,给实际处理任务的方法添加@Around("标记切点的方法名()")注解。

此时,我们只有一个around方法,要用这一个方法对listgetByIddeleteById三个方法进行处理。那么around方法如何分辨这三个不同的方法呢?这时就需要用到一个连接点对象ProceedingJoinPointaround的返回值为object类型,其要返回所切入方法的返回值。

然后,就可以实现日志输出功能了。

@Aspect
@Component
@Slf4j
public class LogAspect {
@Pointcut("@annotation(cn.codewei.aopstudy.annotation.LogAnnotation)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 方法名称
String name = point.getSignature().getName();
// 通过反射 获取注解中的内容
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
String value = annotation.value();
// 输出日志
log.info("方法名称:{}, 方法描述: {}, 返回值: {}, 参数列表: {}", name, value, point.proceed(), point.getArgs());
// 返回切入方法的返回值
return point.proceed();
}
}

@Around、@Before、@After区别

  • @Before前置通知,是在所拦截方法执行之前执行一段逻辑,返回值类型为void。
  • @After 后置通知,是在所拦截方法执行之后执行一段逻辑,返回值类型为void。
  • @Around 环绕通知,是可以同时在所拦截方法的前后执行一段逻辑,用这个注解的方法入参传的是ProceedingJoinPoint,返回结果类型为Object,返回结果为ProceedingJoinPoint对象的.proceed();

3. @Pointcut

  • 使用within表达式匹配

​ 匹配com.leo.controller包下所有的类的方法

@Pointcut("within(com.leo.controller..*)")
public void pointcutWithin(){
}
  • this匹配目标指定的方法,此处就是HelloController的方法
@Pointcut("this(com.leo.controller.HelloController)")
public void pointcutThis(){
}
  • target匹配实现UserInfoService接口的目标对象
@Pointcut("target(com.leo.service.UserInfoService)")
public void pointcutTarge(){
}
  • bean匹配所有以Service结尾的bean里面的方法

    注意:使用自动注入的时候默认实现类首字母小写为bean的id

@Pointcut("bean(*ServiceImpl)")
public void pointcutBean(){
}
  • args匹配第一个入参是String类型的方法
@Pointcut("args(String, ..)")
public void pointcutArgs(){
}
  • @annotation匹配是@Controller类型的方法
@Pointcut("@annotation(org.springframework.stereotype.Controller)")
public void pointcutAnnocation(){
}
  • @within匹配@Controller注解下的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.CLASS)
@Pointcut("@within(org.springframework.stereotype.Controller)")
public void pointcutWithinAnno(){
}
  • @target匹配的是@Controller的类下面的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.RUNTIME)
@Pointcut("@target(org.springframework.stereotype.Controller)")
public void pointcutTargetAnno(){
}
  • @args匹配参数中标注为@Sevice的注解的方法
@Pointcut("@args(org.springframework.stereotype.Service)")
public void pointcutArgsAnno(){
}
  • 使用excution表达式
@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")
public void pointCut() {
}

Spring AOP 应用的更多相关文章

  1. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  2. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  3. spring aop

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...

  4. spring aop注解方式与xml方式配置

    注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...

  5. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  6. Spring AOP详解

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  7. Spring AOP实例——异常处理和记录程序执行时间

    实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...

  8. 从零开始学 Java - Spring AOP 实现用户权限验证

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...

  9. 从零开始学 Java - Spring AOP 实现主从读写分离

    深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...

  10. 从零开始学 Java - Spring AOP 拦截器的基本实现

    一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...

随机推荐

  1. Python3网络爬虫开发实战阅读笔记

    基本库的使用 网络请求库 urllib(HTTP/1.1) Python自带请求库,繁琐 基础使用:略 requests(HTTP/1.1) Python常用第三方请求库,便捷 基础使用:略 http ...

  2. 题解:AT_abc389_d [ABC389D] Squares in Circle

    假定原点为圆心. 我们只考虑点在第一象限的情况,剩下的情况同理. 因为圆心是原点,所以在圆内的点的横坐标一定在 \(r\) 之内. 枚举点的横坐标 \(x + \frac{1}{2}\),二分最大的 ...

  3. CV高手是怎么炼成的?

    你平时都怎么复制粘贴的?是否每次都是复制一段粘贴一段?是否厌倦了每次只能复制粘贴一次的限制?那这篇文章就是为你量身订做的. CopyQ简介 CopyQ is clipboard manager – a ...

  4. spring boot配置mybatis-plus

    一.maven配置 <mybatis-plus.version>3.2.0</mybatis-plus.version> <mysql-connector.version ...

  5. Amis坑

    一.特殊字符 1.输入框.多行输入框输入$s字符默认替换成空. 如调用后端接口的输入框输入123$BB123,实际请求的参数为123,$后面的参数消息 解决办法:如确实需要输入$的话,在$前面加\即可 ...

  6. Shell脚本常用写法

    一.变量定义 | 赋值 | 输出 1.debugmap #!/bin/bash source /etc/profile # hive_json_tuple_params_orignal.tmp # e ...

  7. 历时两天半由浅入深总结了20道Vue高频面试题

    作为一个程序员如果你想要找到你心仪的工作,不可避免的就会问到很多八股文,虽然有的和工作没有半毛钱关系,但是你如果想要通过面试还必须得会.所以我最近开始总结一些面试题,一是为了加强自己的理解能够找到一份 ...

  8. CentOS7搭建CDH5.16.2集群 HA高可用(包含Spark2等组件部署)

    CentOS7搭建CDH5.16.2集群 HA高可用   机器准备:(这里HA是对HDFS和YARN的高可用) 主机名 cpu内存 bigdata-master01 8c32G bigdata-mas ...

  9. Scala高阶函数 2(以函数作为返回值,函数柯里化,应用函数)

    package com.wyh.day01 object ScalaFun4 { def main(args: Array[String]): Unit = { /** * 以函数作为返回值 */ d ...

  10. Vulnhub-FristiLeaks_1.3

    一.靶机搭建 选择扫描虚拟机 选择路径即可 二.信息收集 靶机信息 产品名称:Fristileaks 1.3 作者:Ar0xA 发布日期: 2015 年 12 月 14 日 目标:获取root(uid ...