Springboot-AOP面向切面编程,上部:详解AOP切面技术!

0、前言

本文分上下两部分,上部为AOP切面技术基础,学习Spring-boot-AOP切面技术。

下部为实践,利用AOP切面技术为主流RESTFul-API接口打造一个统一的访问日志。

1、AOP切面编程简介

有这样一个场景,某公司有一个主数据管理系统目前已经上线运行,但是系统运行不稳定,有时候运行很慢,为了检测到底是哪里出了问题,开发人员需要监控每一个方法的执行时间,再判断问题所在。当问题解决后,还需要把监控移除掉。因为系统已经上线运行,如果手动修改程序方法,那么工作量会非常大,而且这些监控方法以后还要移除掉,费时费力。如果能够在系统运行时动态添加代码,就能很好的解决这个需求。目标:不修改源代码,也能实现监控输出,这时AOP切面编程就是最佳选择。

在系统运行时动态添加代码的方式称为“面向切面编程AOP”。它有非常多的应用场景:

A:系统监控,执行情况分析,性能统计。

B:设置日志,记录登录、执行、参数等信息。

C:拦截,通过AOP快速实现精细化拦截,安全控制。

D:事务处理、统一异常处理。

简单来说,AOP就像给程序照CT,将执行过程进行切片管理。

2、AOP常见概念

  • Joinpoint:连接点,类里可以被增强的方法,例如你想修改哪个方法的功能,那么这就是一个连接点。
  • Pointcut:切入点,即对Joinpoint进行拦截的定义即为切入点,如拦截所有select开始的方法,这个定义就是一个切入点。
  • Advice:通知,拦截到Joinpoint之后所要做的事情就是通知,例如打印日志、发送邮件、写入监控等。
  • Aspect:切面,Pointcut和Advice的结合。
  • Target:要增强的类称为Target。

3、Spring-boot AOP切面技术基础

3.1添加Maven依赖

        <!-- AOP面向切面编程-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

项目结构如下图所示:

3.2创建一个UserService服务类,一个UserController接口实现类用于调用UserService服务类。

我们在com.example.demohelloworld.AOP.Service包下创建UserService服务类,代码如下:

package com.example.demohelloworld.AOP.Service;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    //定义一个简单的查询方法
    public String getUserById(Integer id){
        System.out.println("get>>>"+id);
        return "getuser";
    }
    //定义一个简单的删除方法
    public void deleteUserById(Integer id){
        System.out.println("delete>>>");
    }
}

我们在com.example.demohelloworld.AOP包下创建UserController接口类,代码如下:

package com.example.demohelloworld.AOP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
//用于调用UserService和前台执行测试
@RestController
public class UserController {
    @Autowired
    UserService userservice;
    @GetMapping("/getuser/{id}")
    public String getUserById(@PathVariable Integer id){
        return userservice.getUserById(id);
    }
    @GetMapping("/deleteuser")
    public void deleteUserById(Integer id){
        userservice.deleteUserById(id);
    }
}

3.3接下来创建AOP切面类AspectLog

我们在com.example.demohelloworld.AOP包下创建AspectLog切面类,代码如下:

package com.example.demohelloworld.AOP;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;

@Component
//@Aspect注解表明这是一个切面类
@Aspect
public class AspectLog {
    //定义切入点,execution 中的第一个*表示方法返回任意值,第二个*表示AOP包下的任意类,第三个*表示类中的任意方法,括号中的两个点表示方法参数任意值
    @Pointcut("execution(* com.example.demohelloworld.AOP.Service.*.*(..))")
    public void pointcutvoid(){
    }
    //@Before注解表示这是一个前置通知,该方法在目标方法执行之前执行。通过JoinPoint参数可以获取目标方法的方法名、修饰符等信息
    @Before(value = "pointcutvoid()")
    public  void before(JoinPoint joinPoint){
        //获取传入参数
        Object[] args =joinPoint.getArgs();
        System.out.println(joinPoint.getSignature().getName()+"方法开始执行...传入参数为:"+Arrays.deepToString(args));
    }
    //@After注解表示这是一个后直通知,该方法在目标方法执行之后执行
    @After(value = "pointcutvoid()")
    public void after(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature().getName()+"方法执行结束...");
    }
    //@AfterReturning注解表示这是一个返回通知,在该方法中可以获取目标方法的返回值,returning参数是指返回值的变量名,对应方法的参数。在方法参数中定义了result的类型为Object,表示目标方法的返回值可以是任意类型,若result参数的类型为Long,则该方法只能处理目标方法返回值为Long的情况
    @AfterReturning(value = "pointcutvoid()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        System.out.println(joinPoint.getSignature().getName()+"方法返回值为:"+result);
    }
    //@AfterThrowing注解表示这是一个异常通知,即当目标方法发生异常时,该方法会被调用,异常类型为Exception表示所有异常都会进入该方法中执行,若异常类型为ArithmeticException,则表示只有目标方法抛出的ArithmeticException异常才会进入该方法中处理
    @AfterThrowing(value = "pointcutvoid()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Exception ex){
        System.out.println(joinPoint.getSignature().getDeclaringTypeName()+joinPoint.getSignature().getName()+"方法异常了,异常是:"+ex.getMessage());
    }
    //@Around注解表示这是一个环绕通知,绕通知是所有通知里功能最为强大的通知,可以实现前置通知、后置通知、异常通知以及返回通知的功能。目标方法进入环绕通知后,通过调用ProceedingJoinPoint对象的proceed方法使目标方法继续执行,开发者可以在此修改目标方法的执行参数、返回值等,并且可以在此处理目标方法的异常
    @Around("pointcutvoid()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        return proceedingJoinPoint.proceed();
    }
}

3.3.1  @Aspect注解表明这是一个切面类。

3.3.2  pointcutvoid()方法使用了@Pointcut注解,表明这是一个切入点。

本文中切入点为@Pointcut("execution(* com.example.demohelloworld.AOP.Service.*.*(..))")

execution 中的第一个*表示方法返回任意值,第二个*表示Service包下的任意类,第三个*表示类中的任意方法,括号中的两个点表示方法参数任意值。

我们可以根据需要,从包、类、方法三个层次去定义所要进行切面的内容。

3.3.3  我们还需要先了解一下切面注解的含义:

切面注解注解含义详细含义
@Before前置表示这是一个前置通知,该方法在目标方法执行之前执行。通过JoinPoint参数可以获取目标方法的方法名、修饰符等信息
@AfterThrowing异常抛出表示这是一个异常通知,即当目标方法发生异常时,该方法会被调用,异常类型为Exception表示所有异常都会进入该方法中执行,若异常类型为ArithmeticException,则表示只有目标方法抛出的ArithmeticException异常才会进入该方法中处理
@After后置表示这是一个后直通知,该方法在目标方法执行之后执行
@AfterReturning后置增强,执行顺序在@After之后表示这是一个返回通知,在该方法中可以获取目标方法的返回值,returning参数是指返回值的变量名,对应方法的参数。在方法参数中定义了result的类型为Object,表示目标方法的返回值可以是任意类型,若result参数的类型为Long,则该方法只能处理目标方法返回值为Long的情况
@Around环绕表示这是一个环绕通知,绕通知是所有通知里功能最为强大的通知,可以实现前置通知、后置通知、异常通知以及返回通知的功能。目标方法进入环绕通知后,通过调用ProceedingJoinPoint对象的proceed方法使目标方法继续执行,开发者可以在此修改目标方法的执行参数、返回值等,并且可以在此处理目标方法异常

3.3.4  连接点JoinPoint对象封装了AOP切面方法的执行信息(这个就是我们需要提炼的信息,可以作为监控、日志等),所以我们还需了解一下连接点JoinPoint中getSignature()常用方法的含义

方法方法含义
joinPoint.getSignature().getDeclaringTypeName()

获取被切面类的路径及名称,例如在本文中为:com.example.demohelloworld.AOP.UserService

joinPoint.getSignature().getName()获取被切面类中方法的名称,例如在本文中为:getUserById或者deleteUserById
joinPoint.getArgs()获取传入目标方法的参数对象
joinPoint.getTarget()获取被代理的对象

 3.4启动项目开始测试切面效果

打开浏览器输入http://localhost:8080/getuser/1,对getUserById进行AOP切面,后台效果如下:

输入http://localhost:8080/deleteuser,对deleteUserById进行AOP切面,后台效果如下:

4、 AOP总结

AOP面向切面编程,指扩展功能且不修改源代码,将功能代码从业务逻辑代码中分离出来。进而实现日志记录,性能统计,安全控制,事务处理,异常处理等功能。

切面技术的应用大大提升了程序的可扩展性、安全性、操作便捷性,且对开发者非常友好,使用较少的代码就能完成监控、日志、拦截等功能,大大提升开发效率。

下一讲,我将通过一个实际案例:利用AOP切面技术为RESTFul-API接口打造一个统一的登录日志而不修改任何原接口代码,让大家进一步了解AOP切面技术的优势。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot实现AOP面向切面编程的具体方法如下: 1. 首先,你需要在项目的pom.xml文件中添加spring-boot-starter-aop依赖。可以参考以下代码: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 2. 然后,你需要编写一个用于拦截的bean。这个bean将包含你想要在目标方法执行前后执行的逻辑。你可以使用注解或者编程方式来定义切面。例如,你可以使用@Aspect注解来定义一个切面,然后在切面的方法上使用@Before、@After等注解来定义具体的拦截行为。 3. 接下来,你需要将切面应用到目标对象上,创建代理对象。这个过程称为织入(Weaving)。在Spring Boot中,你可以使用@EnableAspectJAutoProxy注解来启用自动代理,它会根据切面定义自动创建代理对象。 总而言之,Spring Boot实现AOP面向切面编程的具体方法包括:添加依赖、编写用于拦截的bean,以及启用自动代理。这样就能实现在目标方法执行前后执行特定逻辑的效果了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot整合aop面向切面编程过程解析](https://download.csdn.net/download/weixin_38689551/12743012)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot实现AOP面向切面编程](https://blog.csdn.net/weixin_52536274/article/details/130375560)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值