SpringBoot之AOP具体实现详解

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

在这里插入图片描述
如果大家在遇到问题可以随时联系我

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值