springboot从零实现一个AOP

在我们开发的过程中经常会有这样的需求,每一个请求需要判断权限,校验参数,打印日制等,但是每一个方法都写这样重复的代码无疑会让代码很冗余,于是可以想到用AOP切面的方式可以一次性解决所有的烦劳。

其实我们通过AOP机制可以实现:Authentication 权限检查、Caching 缓存、参数校验、Context passing 内容传递、Error handling 错误处理、日志打印等功能,这里我们讲一下怎么从0开始手写一个Spring AOP来实现前置通知或后置通知。

一:添加pom依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二:新建一个aop包,定义一个切面
一般会专门新建一个包用来处理aop,我这里新建一个package org.sang.org.sang.advice包,并且新建一个LogAdvice类。

package org.sang.org.sang.advice;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * create by 86159 on 2020/11/2
 */
@Aspect
@Component
public class LogAdvice {
   
}

这里注意要加上注解@Aspect,申明是个切面类,@Component能够扫描到。

三:定义一个切面

package org.sang.org.sang.advice;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * create by 86159 on 2020/11/2
 */
@Aspect
@Component
public class LogAdvice {
    //定义一个切点,所有被PostMapping注解修饰的方法会织入advice
    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    private void logAdvicePoint(){

    }
}

定义切面是用@Pointcut注解,括号里为织入切面的条件,或者范围,里面可以是包,类,也可以是注解,我这里用的是注解,表示凡是被PostMapping注解修饰的方法会织入advice切面。logAdvicePoint为切面名称。

四:前置通知

package org.sang.org.sang.advice;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * create by 86159 on 2020/11/2
 */
@Aspect
@Component
public class LogAdvice {
    //定义一个切点,所有被PostMapping注解修饰的方法会织入advice
    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    private void logAdvicePoint(){

    }
    @Before("logAdvicePoint()")
    public void beforeAdvice(){
        System.out.println("post请求advice被触发了。。。。。。。。。。。。,校验权限,参数有效性");
    }
}

在切面的基础上构建前置通知,前置通知是用@Before注解,括号里的参数为切面的名称,前置通知的方法体内为在调用方法前要处理的业务逻辑,比如权限校验,参数校验等等。

五:后置通知

package org.sang.org.sang.advice;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * create by 86159 on 2020/11/2
 */
@Aspect
@Component
public class LogAdvice {
    //定义一个切点,所有被PostMapping注解修饰的方法会织入advice
    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    private void logAdvicePoint(){

    }
    @Before("logAdvicePoint()")
    public void beforeAdvice(){
        System.out.println("post请求advice被触发了。。。。。。。。。。。。,校验权限,参数有效性");
    }

    @After("logAdvicePoint()")
    public void  afterAdvice(){
        System.out.println("请求完毕,打印日制记录");
    }
}

后置通知用@After注解,同样括号里为切面名称,方法体内为调用方法完成后需要处理的业务逻辑,如打印日志记录。

下面我们写个方法测试下效果:

package org.sang.controller;

import org.sang.model.entity.User;
import org.sang.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 新增用户
     * @param user
     * @return
     */
    @PostMapping("/insert")
    @ResponseBody
    public User insert(@RequestBody @Validated(User.valid1.class) User user){
        System.out.println("保存用户开始执行。。。");
        return userService.insert(user);
    }

我们用postman调用方法测试结果如下:

post请求advice被触发了。。。。。。。。。。。。,校验权限,参数有效性
保存用户开始执行。。。
请求完毕,打印日制记录

可以看到,在调用方法前和方法后都执行相应的前置通知和后置通知的逻辑代码,这样一个简单的aop就实现了。

上面定义@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")就是将所有标有@PostMapping注解的请求方法织入切面,也可以定义类,例如将所有contoller类织入切面。
@Pointcut("@annotation(org.sang.controller.*)") 将所有controller包下的类都织入切面,也就可以处理所有的web请求。

另外还有其他的通知:@Around,@AfterReturning,@Afterthrowing

这里简单介绍一下,切面的执行方法和其执行顺序:

  1. @Around 通知方法将目标方法封装起来
  2. @Before 通知方法会在目标方法调用之前执行
  3. @After 通知方法会在目标方法返回或者异常后执行
  4. @AfterReturning 通知方法会在目标方法返回时执行
  5. @Afterthrowing 通知方法会在目标方法抛出异常时执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初夏0811

你的鼓励将是我创作最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值