欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot之AOP具体实现详解

程序员文章站 2024-03-19 16:42:46
...

SpringBoot之AOP具体实现详解

  • //本文作者:wangkexin

Spring AOP 之 SpringBoot集成

上一篇文章《Spring AOP 面向切面概念入门》对AOP作了简要的介绍,包含一些专业术语的解释。

本文基于SpringBoot编写了一个简单的Spring AOPDemo。

准备工作(pom文件添加如下依赖)

maven依赖添加如下
<!--引入SpringBoot的Web模块-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
 
<!--引入AOP依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

本次使用的 maven 的 modelVersion 4.0.0, springBoot 版本是 2.1.7.RELEASE, jdk 1.8 的项目环境

注意:在完成了引入AOP依赖包后,不需要去做其他配置。AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy,不需要在程序主类中增加@EnableAspectJAutoProxy来启用。

application.yml的配置(可有可无,默认是开启)

列出来看一下是什么配置

spring:
  aop:
    proxy-target-class: true
    auto: true

web请求入口:实现具体的业务

这里实现的是日志信息的搜集


@RestController
public class TestController {

    @GetMapping("/test")
    @LogTestLog(value = "测试")   //使用这个注解就相当于切点了
    @ResponseBody
    public String test(@RequestParam("name") String name, @RequestParam("age") Integer age) throws Exception {
        return name;
    }
    
}

定义一个注解

这个注解是是相当于切入点


@Target({ElementType.PARAMETER, ElementType.METHOD}) //作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME) //运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface LogTestLog {
    String value() default "";
}

切面的具体实现

定义切面类:在类上添加@Aspect 和@Component 注解即可将一个类定义为切面类。
@Aspect 注解 使之成为切面类
@Component 注解 把切面类加入到IOC容器中


@Aspect
@Slf4j
@Component
public class LogAscpectTest {

    /**
     * 切面扫描的是 使用这个注解的方法
     * 可以定义多个切入点
     */
    @Pointcut("@annotation(com.java8.aopday06.LogTestLog)")
    public void webLog () {

    }

    /**
     * execution:用来匹配执行方法的连接点
     * A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
     * 第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数
     * B:@Pointcut("within(com.aijava.springcode.service.*)")
     * within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点
     * C:@Pointcut("this(com.aijava.springcode.service.UserService)")
     * this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService
     * D:@Pointcut("bean(userService)")
     * bean也是非常常用的,bean可以指定IOC容器中的bean的名称
     * @within(org.springframework.stereotype.Service),拦截带有 @Service 注解的类的所有方法
      * @annotation(org.springframework.web.bind.annotation.RequestMapping),拦截带有@RquestMapping的注解方法
     */
    @Pointcut("execution(* com.java8.aopday06..*.*(..))")//把指定包路径下所有方法都进行匹配
    private void pointCut() {
    }


    /**
     * 环绕通知--- 测试记录所有的日志信息
     * @param joinPoint
     * @return
     */
    @Around("webLog()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        String format = simpleDateFormat.format(new Date(System.currentTimeMillis()));
        log.info("调用方法之前答应()进入该方法的时间信息:{}", format);
        // 表示开始调用设置切点的方法
        Object proceed = joinPoint.proceed();
        long time = System.currentTimeMillis() - startTime;
        log.info("方法执行的时间: {}", time);
        try {
            //记录日记信息
            saveLog (joinPoint, time);
        }catch (Exception e) {
            log.error("出现异常: {}", e.toString());
            e.printStackTrace();
        }
        return proceed;
    }


	/**
     * 返回通知这是为了测试拿到被匹配所有切面的业务的返回值
     * @param joinPoint
     * @return
     */
    @AfterReturning(pointcut = "pointCut()", returning = "result")
    public void afterReturning (JoinPoint point, Object result) {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Class returnType = methodSignature.getReturnType();
        log.info("方法的返回值类型: {}, 方法的返回值:{}", returnType, result);
    }






    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取注解上的描述
        LogTestLog annotation = method.getAnnotation(LogTestLog.class);
        String value = annotation.value();
        // 获取的请求的类名字, 方法名,
        String name =  method.getName();
        //添加注解的方法名称: test
        log.info("添加注解的方法名称: {}", name);
        String className = joinPoint.getTarget().getClass().getName();
        // 添加注解的方法类的名称:com.java8.aopday06.TestController
        log.info("添加注解的方法类的名称:{}", className);

        Object[] args = joinPoint.getArgs();
        List<Object> objects = Arrays.asList(args);
        for (int j = 0; j < objects.size(); j++) {
            // 参数信息: 22 ---- 可以吧数据放入到容器中
            log.info("参数信息: {} ", objects.get(j));
        }

        Parameter[] parameters = method.getParameters();
        List<Parameter> parameterList = Arrays.asList(parameters);
        for (int i = 0; i < parameterList.size(); i++) {
            //parameters : class java.lang.String, name   ---- 参数的类型以及名字
            log.info("parameters : {}, {}", parameterList.get(i).getType(), parameterList.get(i).getName());
        }

        HttpServletRequest  httpServletRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String requestURI = httpServletRequest.getRequestURI();
        StringBuffer requestURL = httpServletRequest.getRequestURL();
        log.info("请求的 urI: {}, 请求的 url: {}", requestURI, requestURL);
        String remoteAddr = httpServletRequest.getRemoteAddr();
        String localAddr = httpServletRequest.getLocalAddr();
        log.info("本地ip : {}", localAddr);
        String remoteHost = httpServletRequest.getRemoteHost();
        log.info("远程 host: {}", remoteHost);

        int remotePort = httpServletRequest.getLocalPort();

        log.info("本地 post: {}", remotePort);

    }

}

启动调用服务返回结果

http://localhost:8081/test?name=45&age=2

SpringBoot之AOP具体实现详解
如果大家在遇到问题可以随时联系我

相关标签: 切面 aop AOP