一、基于配置切面类-@Aspect-bean
1、引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.对imp及其子包下所有方法进行拦截
package com.thwcompany.demo.config;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
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// 表示一个切面bean
@Component
@Slf4j
@Order(1)//越小,优先级越高
public class AOPConfig {
ThreadLocal<Long> startTime=new ThreadLocal<>();
/**
execution()是最常用的切点函数,其语法如下所示: 整个表达式可以分为五个部分:
1、execution(): 表达式主体。匹配所有目标类的public方法
2、第一个*号:表示返回类型,*号表示所有的类型。
3、包名:表示需要拦截的包名,".."表示com.thwcompany.demo.service.impl包、子包。
4、第二个*号:包中所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,"(..)"表示方法的参数,表示任何参数。
*/
@Pointcut("(execution(public * com.thwcompany.demo.service.impl..*.*(..)))")
@Order(300)
public void logAOP1(){
}
@Pointcut("(execution(public * com.thwcompany.demo.service.impl..*.*(..)))")
@Order(1)
public void logAOP2(){
}
//声明环绕通知
@Around(value = "logAOP1()")--可用&&,||等,比如:@Around(value = "logAOP1() && logAOP2()"
public Object doAround(ProceedingJoinPoint joinPoint){
String method = joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName();
Object responseAOP=null;
try {
log.debug("AOP1@AroundStart===method:{},param:{}",method, Arrays.toString(joinPoint.getArgs()));
responseAOP=joinPoint.proceed();
log.info("AOP1@AroundFinish===method:{},result:{}",method,responseAOP);
} catch (Throwable throwable) {
log.error("AOP1@AroundError===method:{},cause:{}",method, throwable);
}
return responseAOP;
}
//声明环绕通知,除了@Around外,每个方法里都可以加或者不加参数JoinPoint,如果有用JoinPoint的地方就加,不加也可以,JoinPoint里包含了类名、被切面的方法名,参数等属性,可供读取使用。只有只有@Around参数必须为ProceedingJoinPoint(其他的都是JoinPoint),pjp.proceed()相当于执行被切面的方法。
@Around(value = "logAOP2()")
public Object doAround1(ProceedingJoinPoint joinPoint) throws Throwable {
Object responseAOP=null;
log.debug("AOP2======Start......");
responseAOP=joinPoint.proceed();
log.info("AOP2@AroundFinish===,result:{}",responseAOP);
return responseAOP;
}
//声明前置通知
@Before(value = "logAOP1()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
// 接收到请求,记录请求内容
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (request != null) {
String uri = request.getRequestURI().toString();
String method = request.getMethod();
String class_method = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
log.info("AOP1doBefore===uri===" + uri);
log.info("AOP1doBefore===method===" + method);
log.info("AOP1doBefore===class_method===" + class_method);
log.info("AOP1doBefore===RemoteAddr===" + request.getRemoteAddr());
}
}
//声明最终通知
后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
@After("logAOP1() ")
public void doAfter(JoinPoint joinPoint) {
log.info("AOP1@After===声明最终通知"+joinPoint);
}
//声明后置通知
@AfterReturning(pointcut = "logAOP1()", returning = "returnValue")
public void doAfterReturning(JoinPoint point,Object returnValue) {
log.info("AOP1doAfterReturning==="+returnValue);
}
//声明例外通知
@AfterThrowing(pointcut = "logAOP1()", throwing = "e")
public void doAfterThrowing(Exception e) {
log.info("AOP1doAfterThrowing===" + e.getMessage());
}
}
3.运行结果如下所示:
2020-06-18 17:48:22.013 DEBUG 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1@AroundStart===method:StudentServiceImp.getById,param:[8]
2020-06-18 17:48:22.013 DEBUG 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP2======Start......
2020-06-18 17:48:22.013 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1doBefore===uri===/getById
2020-06-18 17:48:22.013 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1doBefore===method===GET
2020-06-18 17:48:22.013 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1doBefore===class_method===com.thwcompany.demo.service.impl.StudentServiceImp.getById
2020-06-18 17:48:22.014 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1doBefore===RemoteAddr===0:0:0:0:0:0:0:1
2020-06-18 17:48:22.025 INFO 19048 --- [nio-8082-exec-1] c.t.demo.service.impl.StudentServiceImp : service业务层代码start: 2020-06-18 17:48:22
2020-06-18 17:48:22.048 DEBUG 19048 --- [nio-8082-exec-1] c.t.demo.dao.StudentMapper.getById : ==> Preparing: select ID id,NAME name,ADDRESS address,CLASSID classid from student where ID=?
2020-06-18 17:48:22.068 DEBUG 19048 --- [nio-8082-exec-1] c.t.demo.dao.StudentMapper.getById : ==> Parameters: 8(Integer)
2020-06-18 17:48:22.081 DEBUG 19048 --- [nio-8082-exec-1] c.t.demo.dao.StudentMapper.getById : <== Total: 1
2020-06-18 17:48:22.084 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP2@AroundFinish===,result:Student(id=8, name=司马昭, address=河内温县, classid=1)
2020-06-18 17:48:22.084 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1@AroundFinish===method:StudentServiceImp.getById,result:Student(id=8, name=司马昭, address=河内温县, classid=1)
2020-06-18 17:48:22.084 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1@After===声明最终通知execution(Student com.thwcompany.demo.service.impl.StudentServiceImp.getById(int))
2020-06-18 17:48:22.084 INFO 19048 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : AOP1doAfterReturning===Student(id=8, name=司马昭, address=河内温县, classid=1)
二、基于注解annotation
一个方法还好只需定义一次,如果多个方法需要使用同一个advice,就要声明多个切点,显然这种方法会造成大量重复代码影响代码可读性。为了解决此类问题我们引入@annotation()
我们先定义一个注解:
1.自定义注解@AOPTest
package com.thwcompany.demo.config;
import java.lang.annotation.*;
/**
* @author ZARD
*/
@Target({ElementType.TYPE,ElementType.METHOD})//声明注解作用在方法上面
@Retention(RetentionPolicy.RUNTIME)//注解保留至运行时
@Documented
public @interface AOPTest {
String desc() default "";
}
2.controller中随机一个方法上加@AOPTest:
RandomStringUtils工具类需要如下依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
@GetMapping(path = "/sendmsg")
@ResponseBody
@AOPTest(desc = "testone")
public String sendmsg(String mobile,HttpServletRequest request,HttpServletResponse response){
String checkcode = RandomStringUtils.randomNumeric(6);//随机生成6位有效数字验证码
// redisTemplate.opsForValue().set("checkcode", checkcode, 2, TimeUnit.HOURS);
//
// Map<String,Object> map=new HashMap();
// map.put("mobile",mobile);
// map.put("checkcode",checkcode);
//
// rabbitTemplate.convertAndSend("mapmsg",map);
log.info("controller-senndmsg-start");
request.setAttribute("code", checkcode);
return checkcode;
}
}
3.切面类中添加:
@Pointcut("@annotation(com.thwcompany.demo.config.AOPTest)")//该advice作用在有@AopIest注解标注的方法
public void test(){}
@Before(value = "test()")
public void before(){
log.debug("自定义注解@before");
}
@Around(value = "@annotation(aopTest)")//自定义注解是这种格式,匹配所有方法上加@AOPTest注解进行拦截增强
public Object aroundtest(ProceedingJoinPoint joinPoint,AOPTest aopTest){
//获取注解里desc="v1"的值
log.debug("自定义注解@around--desc里面值"+aopTest.desc());
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
4.运行结果如下所示:
2020-06-18 18:48:03.987 DEBUG 19680 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : 自定义注解@around--desc里面值testone
2020-06-18 18:48:03.987 DEBUG 19680 --- [nio-8082-exec-1] com.thwcompany.demo.config.AOPConfig : 自定义注解@before
2020-06-18 18:48:03.991 INFO 19680 --- [nio-8082-exec-1] c.thwcompany.demo.web.StudentController : controller-senndmsg-start
结尾
我是码农小伟,一个在南京工作的java小学狗,未来还有很长的路要走。
雄关漫道真如铁,而今迈步从头越。
如果您觉得这篇文章对您有帮助的话,麻烦点了赞加个关注!
谢谢!