通过@Aspect实现aop

前言

大家好!这里是小小!因为自己之前学的有些草率了,有些小知识点就不太熟悉,在这里就简单学习一下springboot中使用@Aspect注解实现AOP。

AOP(Aspect Oriented Programming,面向切面编程)是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

在Spring AOP中业务逻辑仅仅只关注业务本身,将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,从而在改变这些行为的时候不影响业务逻辑的代码。

相关注解:

注解 作用
@Aspect 把当前类标识为一个切面,通常是一个类的注解
@Pointcut Pointcut是织入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around 环绕增强,目标方法执行前后分别执行一些代码
@AfterReturning 返回增强,目标方法正常执行完毕时执行
@Before 前置增强,目标方法执行之前执行
@AfterThrowing 异常抛出增强,目标方法发生异常的时候执行
@After 后置增强,不管是抛出异常或者正常退出都会执行

我们用个例子走进这些方法

整合springboot

  1. 添加依赖

        <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-aop</artifactId>
            <version>2.6.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- SpringBoot 拦截器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

        <!--json相关的-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.13.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.2.2</version>
        </dependency>


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

  1. 增强类
package com.feng.aspect;
 
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
 
@Component
@Aspect
@Slf4j
public class LogAspect {
        private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
        @Pointcut("execution(* com.feng.controller.TestController.*(..))")
//        @Pointcut("execution(* com.feng.controller.TestController.doNormal(..))")
//        @Pointcut("execution(* com.feng.controller.TestController.doWithException(..))")
        public void pointCut(){

        }
 
        @Before(value = "pointCut()")
        public void before(JoinPoint joinPoint){
            log.info("@Before通知执行");
            //获取目标方法参数信息
            Object[] args = joinPoint.getArgs();
            Arrays.stream(args).forEach(arg->{  // 大大
                try {
                    log.info("目标方法的参数信息(正常打印): "+OBJECT_MAPPER.writeValueAsString(arg));
                } catch (JsonProcessingException e) {
                    log.info("目标方法的参数(异常抛出)"+arg.toString());
                }
            });
            //aop代理对象
            Object aThis = joinPoint.getThis();
            log.info("代理对象: "+aThis.toString());
            //被代理对象
            Object target = joinPoint.getTarget();
            log.info("被代理的对象: "+target.toString());
            //获取连接点的方法签名对象
            Signature signature = joinPoint.getSignature();
            log.info("连接点的方法签名对象(长): "+signature.toLongString());
            log.info("连接点的方法签名对象(短): "+signature.toShortString());
            log.info("连接点的方法签名对象: "+signature.toString());
            //获取方法名
            log.info("获取方法名: "+signature.getName());
            //获取声明类型名
            log.info("获取声明类名(getDeclaringTypeName()): "+signature.getDeclaringTypeName());
            //获取声明类型  方法所在类的class对象
            log.info("获取声明类型名: "+signature.getDeclaringType().toString());
            //和getDeclaringTypeName()一样
            log.info("获取声明类名(getDeclaringType().getName()): "+signature.getDeclaringType().getName());
            //连接点类型
            String kind = joinPoint.getKind();
            log.info("连接点的类型: "+kind);
            //返回连接点方法所在类文件中的位置  打印报异常
            SourceLocation sourceLocation = joinPoint.getSourceLocation();
            log.info("连接点方法所在类文件中的位置: "+sourceLocation.toString());
            //log.info(sourceLocation.getFileName());
            //log.info(sourceLocation.getLine()+"");
            //log.info(sourceLocation.getWithinType().toString());
            ///返回连接点静态部分
            JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();
            log.info("连接点静态位置: "+staticPart.toLongString());
            //attributes可以获取request信息 session信息等
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            log.info("url: "+request.getRequestURL().toString());
            log.info("ip地址: "+request.getRemoteAddr()); //127.0.0.1
            log.info("客户端请求方法是: "+request.getMethod()); //GET
            log.info("before通知执行结束");
        }
 
        /**
         * 后置返回
         *      如果第一个参数为JoinPoint,则第二个参数为返回值的信息
         *      如果第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
         * returning:限定了只有目标方法返回值与通知方法参数类型匹配时才能执行后置返回通知,否则不执行,
         *            参数为Object类型将匹配任何目标返回值
         */
        @AfterReturning(value = "pointCut()",returning = "result")
        public void doAfterReturningAdvice1(JoinPoint joinPoint,Object result){
            log.info("第一个后置返回通知的返回值:"+result);
        }
 
        @AfterReturning(value = "pointCut()",returning = "result",argNames = "result")
        public void doAfterReturningAdvice2(String result){
            log.info("第二个后置返回通知的返回值:"+result);
        }
        //第一个后置返回通知的返回值:姓名是大大
        //第二个后置返回通知的返回值:姓名是大大
        //第一个后置返回通知的返回值:{name=小小, id=1}
 
        /**
         * 后置异常通知
         *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
         *  throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
         *            对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
         * @param joinPoint
         * @param exception
         */
        @AfterThrowing(value = "pointCut()",throwing = "exception")
        public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
            log.info("发生异常的目标: "+joinPoint.getSignature().getName());
            if(exception instanceof NullPointerException){
                log.info("发生了空指针异常!!!!!");
            }
        }
 
        @After(value = "pointCut()")
        public void doAfterAdvice(JoinPoint joinPoint){
            log.info("后置通知执行了!");
        }
 
        /**
         * 环绕通知:
         *   注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用
         *
         *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
         *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
         */
        @Around(value = "pointCut()")
        public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
            log.info("@Around环绕通知:"+proceedingJoinPoint.getSignature().toString());
            Object obj = null;
            try {
                obj = proceedingJoinPoint.proceed(); //可以加参数
                log.info("这里是代理对象: "+obj.toString());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            log.info("@Around环绕通知执行结束");
            return obj;
        }
}
  1. 在controller中的测试方法
package com.feng.controller;
 
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@Slf4j
public class TestController {
 
    @RequestMapping("/doNormal")
    public String doNormal(String name, String age) {
        log.info("【执行方法】:doNormal");
        return "doNormal";
    }
 
    @RequestMapping("/doWithException")
    public String doWithException(String name, String age) {
        log.info("【执行方法】:doWithException");
        int a = 1 / 0;
        return "doWithException";
    }
 
}

运行一下测试:

这里先测试一下doNormal方法, 带"小小","20"两个参数进去

通过@Aspect实现aop_第1张图片

再运行doException方法, 带"ladidol","20"两个参数进去

通过@Aspect实现aop_第2张图片

运行输出(两个方法都运行输出):

2022-04-12 20:52:20.211  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : @Around环绕通知:String com.feng.controller.TestController.doNormal(String,String)
2022-04-12 20:52:20.211  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : @Before通知执行
2022-04-12 20:52:20.217  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 目标方法的参数信息(正常打印): "小小"
2022-04-12 20:52:20.217  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 目标方法的参数信息(正常打印): "20"
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 代理对象: com.feng.controller.TestController@484b628e
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 被代理的对象: com.feng.controller.TestController@484b628e
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 连接点的方法签名对象(): public java.lang.String com.feng.controller.TestController.doNormal(java.lang.String,java.lang.String)
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 连接点的方法签名对象(): TestController.doNormal(..)
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 连接点的方法签名对象: String com.feng.controller.TestController.doNormal(String,String)
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 获取方法名: doNormal
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 获取声明类名(getDeclaringTypeName()): com.feng.controller.TestController
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 获取声明类型名: class com.feng.controller.TestController
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 获取声明类名(getDeclaringType().getName()): com.feng.controller.TestController
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 连接点的类型: method-execution
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 连接点方法所在类文件中的位置: org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@773673db
2022-04-12 20:52:20.218  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 连接点静态位置: execution(public java.lang.String com.feng.controller.TestController.doNormal(java.lang.String,java.lang.String))
2022-04-12 20:52:20.219  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : url: http://localhost:8080/doNormal
2022-04-12 20:52:20.219  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : ip地址: 0:0:0:0:0:0:0:1
2022-04-12 20:52:20.219  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 客户端请求方法是: GET
2022-04-12 20:52:20.219  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : before通知执行结束
2022-04-12 20:52:20.227  INFO 26500 --- [nio-8080-exec-1] com.feng.controller.TestController       : 【执行方法】:doNormal
2022-04-12 20:52:20.227  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 第二个后置返回通知的返回值:doNormal
2022-04-12 20:52:20.227  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 第一个后置返回通知的返回值:doNormal
2022-04-12 20:52:20.227  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 后置通知执行了!
2022-04-12 20:52:20.227  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : 这里是代理对象: doNormal
2022-04-12 20:52:20.227  INFO 26500 --- [nio-8080-exec-1] com.feng.aspect.LogAspect                : @Around环绕通知执行结束
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : @Around环绕通知:String com.feng.controller.TestController.doWithException(String,String)
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : @Before通知执行
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 目标方法的参数信息(正常打印): "ladidol"
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 目标方法的参数信息(正常打印): "20"
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 代理对象: com.feng.controller.TestController@484b628e
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 被代理的对象: com.feng.controller.TestController@484b628e
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 连接点的方法签名对象(): public java.lang.String com.feng.controller.TestController.doWithException(java.lang.String,java.lang.String)
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 连接点的方法签名对象(): TestController.doWithException(..)
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 连接点的方法签名对象: String com.feng.controller.TestController.doWithException(String,String)
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 获取方法名: doWithException
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 获取声明类名(getDeclaringTypeName()): com.feng.controller.TestController
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 获取声明类型名: class com.feng.controller.TestController
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 获取声明类名(getDeclaringType().getName()): com.feng.controller.TestController
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 连接点的类型: method-execution
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 连接点方法所在类文件中的位置: org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@3cc966aa
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 连接点静态位置: execution(public java.lang.String com.feng.controller.TestController.doWithException(java.lang.String,java.lang.String))
2022-04-12 20:52:29.926  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : url: http://localhost:8080/doWithException
2022-04-12 20:52:29.927  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : ip地址: 0:0:0:0:0:0:0:1
2022-04-12 20:52:29.927  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 客户端请求方法是: GET
2022-04-12 20:52:29.927  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : before通知执行结束
2022-04-12 20:52:29.927  INFO 26500 --- [nio-8080-exec-6] com.feng.controller.TestController       : 【执行方法】:doWithException
2022-04-12 20:52:29.927  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 发生异常的目标: doWithException
2022-04-12 20:52:29.927  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : 后置通知执行了!
2022-04-12 20:52:29.929  INFO 26500 --- [nio-8080-exec-6] com.feng.aspect.LogAspect                : @Around环绕通知执行结束
java.lang.ArithmeticException: / by zero
	at com.feng.controller.TestController.doWithException(TestController.java:21)
	at com.feng.controller.TestController$$FastClassBySpringCGLIB$$8315b4d7.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:57)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:57)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:49)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:58)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)
	at com.feng.aspect.LogAspect.doAroundAdvice(LogAspect.java:134)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:624)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:72)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
	at com.feng.controller.TestController$$EnhancerBySpringCGLIB$$834e9dd5.doWithException(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

改进:

有同学就要问了, 如果有很多地方的方法都需要切面, 我们该怎么办呢?

这是后就可以用到自定义注解了!

自定义注解:

package com.feng.annotation;

import java.lang.annotation.*;

@Documented
@Target(ElementType.METHOD) // 说明该注解只能放在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface XiaoXiao {
}

增强类里面的更改:

@Around("pointCut(xiaoXiao)")
        public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, XiaoXiao xiaoXiao){
            
@Pointcut("@annotation(xiaoXiao)")
        public void pointCut(XiaoXiao xiaoXiao){

        }            
......
    

直接使用全类名也行

@Around("pointCut(com.feng.annotation.XiaoXiao)")

在需要切面的方法上面添加一下@XiaoXiao注解即可使用

再理解AOP:

什么是AOP

那么AOP为何那么重要呢?在我们的程序中,经常存在一些系统性的需求,比如权限校验、日志记录、统计等,这些代码会散落穿插在各个业务逻辑中,非常冗余且不利于维护。例如下面这个示意图:

通过@Aspect实现aop_第3张图片

这时可以将重复的代码给公共化:

通过@Aspect实现aop_第4张图片

这样aop的位置就大致确定了! 如果还需要这个公共代码像切面一样可以不需要动目标方法的代码的话的插入插出:
通过@Aspect实现aop_第5张图片

AOP体系与概念:

  • 在哪里切入,也就是权限校验等非业务操作在哪些业务代码中执行。
  • 在什么时候切入,是业务代码执行前还是执行后。
  • 切入后做什么事,比如做权限校验、日志记录等。

因此,AOP的体系可以梳理为下图:

通过@Aspect实现aop_第6张图片

一些概念详解:

  • Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
  • Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
  • Aspect:切面,即PointcutAdvice
  • Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
  • Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

传神的一个易理解

通过@Aspect实现aop_第7张图片

END

如果想要更深入了解一下切点表达式可以看一下这个戳这里

如果想更深入了解切面在项目中的运用可以看我昨日写的博客 图片验证码请求限制的实现戳这里

参考链接:

链接1
链接2

欢迎点赞关注哦!
也欢迎到访我的博客!
同样欢迎友链互加!
小小的博客传送门!

你可能感兴趣的:(后端,springboot学习,java,java,aspect,aop,springboot)