SpringAOP自定义注解应用

一、AOP简述

 

什么是aop?

AOP 即 Aspect Oriented Program 面向切面编程

与OOP对比,AOP是处理一些横切行问题。这些横切性问题不会影响到主逻辑的实现,但是会散落到代码的各个部分,难以维护。AOP就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。

 传统OOP是自上而下的逻辑开发如图1,AOP是一种面向切面的编程思想。这些横切性问题,把它们抽象为一个切面,关注点在切面的编程如图2。

  图1                   图2

 

 

如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。

首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。

  • 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
  • 所谓的周边功能,比如性能统计,日志,事务管理等等

周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面

在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP

AOP 的目的

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP应用场景

日志记录,权限验证,效率检查,事务管理......

AOP 当中的概念:

  • 切入点(Pointcut)
    在哪些类,哪些方法上切入(where
  • 通知(Advice)
    在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能)
  • 切面(Aspect)
    切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
  • 织入(Weaving)
    把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)

注:开发AOP有两种方式1、注解开发 2、xml配置开发 此处只对注解开发做详细笔记。

二、使用注解来开发 Spring AOP

选择连接点

Spring 是方法级别的 AOP 框架,我们主要也是以某个类额某个方法作为连接点,另一种说法就是:选择哪一个类的哪一方法用以增强功能。

@GetMapping(value = "/test1")
    public String test1(HttpServletRequest request, String var1) {
        LOG.info("============打印日志开始============");
        LOG.info("URL: " + request.getRequestURL().toString());
        LOG.info("============打印日志结束============");
        return "test1";
    }

创建切面

@Aspect
@Component
@Log4j2
//@Profile("dev") //指定dev环境该注解有效,其他环境无效(未测试)
public class DemoAspect {
    private static final Logger logger = LoggerFactory.getLogger(DemoAspect.class);

    /**
     * 切入点
     *
     * @Pointcut("@annotation(cn.van.annotation.annotation.WebLog)")
     **/

    @Pointcut("execution(public * com.example.demo2.controller.*.*(..))")
    public void addAdvice() {
    }

    @Before("addAdvice()")
    public void before(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        HttpServletRequest requests = (HttpServletRequest) args[0];
        log.info("============打印日志开始============");
        log.info("URL: " + requests.getRequestURL().toString());
        log.info("============打印日志结束============");
//        LOG.info("before....");
    }

}
选择好了连接点就可以创建切面了,我们可以把切面理解为一个拦截器,当程序运行到连接点的时候,被拦截下来,在 Spring 中只要使用 @Aspect 注解一个类,那么 Spring IoC 容器就会认为这是一个切面了。
注:被定义为切面的类仍然是一个 Bean ,需要 @Component 注解标注。
Spring中AspectJ注解如下:
  1. @Before 前置通知,在连接点方法前调用
  2. @Around 环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法,后面会讲
  3. @After 后置通知,在连接点方法后调用
  4. @AfterReturning 返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常
  5. @AfterThrowing 异常通知,当连接点方法异常时调用

定义切点

切面中用到的@Pointcut注解即为切入点。
@Pointcut("execution(public * com.example.demo2.controller.*.*(..))")

环绕通知

这是 Spring AOP 中最强大的通知,因为它集成了前置通知和后置通知,它保留了连接点原有的方法的功能,所以它及强大又灵活。如下代码:

 //  使用 @Around 注解来同时完成前置和后置通知
    @Around("execution(* pojo.Landlord.service())")
    public void around(ProceedingJoinPoint joinPoint) {
        System.out.println("开始");

        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        System.out.println("结束");
    }

测试的结果为:

开始
业务层中进行的处理
结束

简单来说:环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的。

ps:

 

SpringBoot 配置 AOP 记录日志 可以通过切面的方式打印控制器层的日志,但是可能存在以下问题:

 

不够灵活,由于是以所有 Controller 方法中的方法为切面,也就是说切死了,如果说我们不想让某个接口打印出入参日志,就办不到了;

 

Controller 包层级过深时,导致很多包下的接口切不到。

 

所以,可以通过指定某些方法打印日志,即通过自定义注解打印日志。

 

AOP官网:AOP
 
posted @ 2020-06-04 17:59  噗噗噗i丶  阅读(238)  评论(0编辑  收藏  举报