SpringBoot使用AOP进行日志的打印,以及碰到的坑

SpringBoot使用AOP


最近在学习使用springboot,我们都知道spring的核心是IOC和AOP,但是一直没有实际使用过AOP去实现某个功能,自己边学习边总结一些经验,有哪个地方写的不对的望大家一块指正和讨论。

  1. 首先引入aop的依赖,其他基础包不再贴出
		<!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  1. 我们使用注解的方式实现aop

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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;


@Aspect
@Component
@Slf4j//如果你的log报错记得下载lombok插件,这里不细讲自行baidu
public class LogAspect {

    //线程副本类去记录各个线程的开始时间
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**1、execution 表达式主体
	  2、第1个* 表示返回值类型  *表示所有类型
	  3、包名  com.*.*.controller下
	  4、第4个* 类名,com.*.*.controller包下所有类
	  5、第5个* 方法名,com.*.*.controller包下所有类所有方法
	  6、(..) 表示方法参数,..表示任何参数
	  */
    @Pointcut("execution(public * com.wf.controller..*.*(..))")
    public void LogAspect() {
    }

    @Before("LogAspect()")
    public void doBefore(JoinPoint joinPoint) {
        startTime.set(System.currentTimeMillis());
        //获取servlet请求对象---因为这不是控制器,这里不能注入HttpServletRequest,但springMVC本身提供ServletRequestAttributes可以拿到
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 想那个url发的请求
        log.info("URL:" + request.getRequestURL().toString());
        log.info("METHOD:" + request.getMethod());
        // 请求的是哪个类,哪种方法
        log.info("请求方法为:" + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        // 方法本传了哪些参数
        log.info("传递参数:" + Arrays.toString(joinPoint.getArgs()));
    }

    @After("LogAspect()")
    public void doAfter(JoinPoint joinPoint) {
        log.info(joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName()+"方法执行时间:" + (System.currentTimeMillis() - startTime.get())+"ms");
    }

    @AfterReturning(returning = "ret", pointcut = "LogAspect()")
    public void doAfterReturning(JoinPoint joinPoint,Object ret) {
        log.info("返回值 : " + ret);
    }

    @AfterThrowing("LogAspect()")
    public void deAfterThrowing(JoinPoint joinPoint) {
    }

    @Around("LogAspect()")
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.proceed();
    }

}

直接使用上面的代码就可以用aop实现controller层每个接口日志信息打印和接口请求时间的计算。

  1. 大概讲解一下每个注解的作用
@Aspect -- 作用是把当前类标识为一个切面供容器读取
@Pointcut -- (切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
@Before -- 标识一个前置增强方法,相当于BeforeAdvice的功能
@AfterReturning -- 后置增强,相当于AfterReturningAdvice,方法退出时执行
@AfterThrowing -- 异常抛出增强,相当于ThrowsAdvice
@After -- final增强,不管是抛出异常或者正常退出都会执行
@Around -- 环绕增强,相当于MethodInterceptor

其中@Pointcut是比较关键的一个注解,下面是用法

//表示匹配所有方法  
1)execution(* *(..))  
//表示匹配com.wf.server.UserService中所有的公有方法  
2)execution(public * com. wf.service.UserService.*(..))  
//表示匹配com.savage.server包及其子包下的所有方法 
3)execution(* com.savage.server..*.*(..))

我贴出的代码中是这样写的
@Pointcut(“execution(public * com.wf.controller….(…))”)
因为我的controller下还有子包所以后面是controller…后面是连着的两个点,如果你的controller包下直接就是类,写一个点即可@Pointcut(“execution(public * com.wf.controller..(…))”) 。

4.如果你使用上面代码没有打印日志,可能问题是你定义的类没有被主程序扫描到,要检查一下主程序上面的扫描包。
我的LogAspect这个类写在com.wf.aop ,所以在主程序上要加上这个包,这样就会生效,代码如下,这样你就可以看到日志的输出打印。


import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@ComponentScan(basePackages = { "com.wf.controller","com.wf.service",
       "com.wf.configuration","com.wf.util","com.wf.aop"})
@MapperScan({"com.wf.mapper"})
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 看到以下结果恭喜你操作成功
2019-11-12 11:05:42.143  INFO 8545 --- [o-8111-exec-100] com.wf.aop.LogAspect                     : URL:http://*/*/*/*
2019-11-12 11:05:42.143  INFO 8545 --- [o-8111-exec-100] com.wf.aop.LogAspect                     : METHOD:POST
2019-11-12 11:05:42.143  INFO 8545 --- [o-8111-exec-100] com.wf.aop.LogAspect                     : 请求方法为:com.wf.controller.*.*.*
2019-11-12 11:05:42.143  INFO 8545 --- [o-8111-exec-100] com.wf.aop.LogAspect                     : 传递参数:
2019-11-12 11:05:42.144  INFO 8545 --- [o-8111-exec-100] com.wf.aop.LogAspect                     : com.wf.controller.*.*方法执行时间:1ms
2019-11-12 11:05:42.144  INFO 8545 --- [o-8111-exec-100] com.wf.aop.LogAspect                     : 返回值 : Result{code=10000, msg='操作成功', data=[]}

  1. 以上为本人自己学习结果以及碰到的问题,有什么不对的,请大家指正,有什么问题我们也可以一块讨论!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个用于构建独立的、生产级别的Spring应用程序的框架,它提供了丰富的功能和特性,方便开发人员快速搭建和开发项目。 在Spring Boot中使用AOP(面向切面编程)可以实现将一些公共的功能逻辑代码从业务逻辑代码中解耦出来,提高代码的复用性和可维护性。通过AOP可以在方法的前后注入一些通用的逻辑,例如日志记录、异常处理、权限校验等。 其中,使用AOP进行结果的统一封装是很常见的需求。通过AOP可以在方法执行后对返回的结果进行封装,统一处理返回结果的格式,并可以对结果进行一些统一的处理操作,例如添加统一的返回码、返回信息、返回时间等。 在Spring Boot中使用AOP进行结果的统一封装可以按照以下步骤进行: 1. 创建一个切面(Aspect),通过在切面类上加上@Aspect注解标识该类是一个切面类。 2. 在切面类中定义一个切点(Pointcut),通过定义一个方法并添加@Pointcut注解来指定切入点。 3. 在切面类中定义一个通知(Advice),通过@Before、@After、@Around等注解来指定通知类型,并在通知方法中编写需要执行的逻辑。 4. 在通知方法中获取方法的返回结果,并进行相应的封装和处理。 5. 在Spring Boot的配置类中添加@EnableAspectJAutoProxy注解来启用AOP使用以上步骤可以实现对方法返回结果的统一封装,使得返回结果具有统一的格式和处理逻辑。这样可以提高代码的重用性和可维护性,并且可以在一处对结果进行集中处理,减少了代码的重复性,提高了开发效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值