SpringBoot使用AOP
最近在学习使用springboot,我们都知道spring的核心是IOC和AOP,但是一直没有实际使用过AOP去实现某个功能,自己边学习边总结一些经验,有哪个地方写的不对的望大家一块指正和讨论。
- 首先引入aop的依赖,其他基础包不再贴出
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 我们使用注解的方式实现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层每个接口日志信息打印和接口请求时间的计算。
- 大概讲解一下每个注解的作用
@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);
}
}
- 看到以下结果恭喜你操作成功
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=[]}
- 以上为本人自己学习结果以及碰到的问题,有什么不对的,请大家指正,有什么问题我们也可以一块讨论!