第一步:添加依赖 (这里我去除了spring-boot-starter-logging,因为项目中可能引入了其他日志,为了避免冲突,就直接去除了)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
第二步:定义切面类
@Aspect
@Component
@Log4j2
public class BizLogAspect /*implements ApplicationContextAware*/{//如果涉及到需要获取一些类的话,要继承plicationContextAware
/*private ApplicationContext applicationContext; bizLog是一个自定义注解
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = ((MethodSignature) signature);
EnableBizLog bizLog =methodSignature.getMethod().getAnnotation(EnableBizLog.class);
MyLogService bizLogObjectService = (MyLogService) applicationContext.getBean(bizLog.serviceclass());*/
//定义切点
@Pointcut("@annotation(com.myproject.test.annotation.EnableBizLog)")
public void apiPoinCut() {
}
//前置通知 还可以添加参数
@Before("apiPoinCut() &&" + args(name,pass))
public void before(JoinPoint point,String name,String pass){
log.info("用户名:" + name +",密码:" + pass);
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP_METHOD : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
// 环绕通知
@Around("apiPoinCut()")
public Object execute(ProceedingJoinPoint joinPoint) {
log.info("方法执行前");
joinPoint.proceed()
log.info("方法执行后");
}
// 后置通知
@After(apiPoinCut())
public void doAfterAdvice(JoinPoint joinPoint)
{
logger.info("后置最终通知执行了!!!!"); }
}
}
//自定义注解
/**
* 记录编辑详细信息的标注
* @author lw
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface EnableBizLog {
/**
* @return 查询数据库所调用的class文件
*/
Class serviceclass();
/**
* @return 业务日志处理的class文件
*/
Class bizLogService() default LogServiceImpl.class;
/**
* @return 前台字段名称
*/
String feildName() default "id";
/**
* @return 具体业务操作名称
*/
String handleName() default "";
/**
* @return 是否处理数字
*/
Class dealNumFlag() default IService.class;
/**
* @return 前台字段名称 会员id
*/
String commonField() default "id";
/**
* @return 操作对象
*/
Class operateObject() default Member.class;
/**
* @return crm内部调用还是外部接口调用
*/
String operateType() default "api";
}
附录
切入点表达式
现在我们介绍一下最重要的切入点表达式:
如上文所说,定义切入点时需要一个包含名字和任意参数的签名,还有一个切入点表达式,就是* findById*(..)这一部分。
切入点表达式的格式:execution([可见性] 返回类型 [声明类型].方法名(参数) [异常])
其中【】中的为可选,其他的还支持通配符的使用:
*:匹配所有字符
..:一般用于匹配多个包,多个参数
+:表示类及其子类
运算符有:&&、||、!
切入点表达式关键词:
1)execution:用于匹配子表达式。
//匹配com.cjm.model包及其子包中所有类中的所有方法,返回类型任意,方法参数任意
@Pointcut("execution(* com.cjm.model..*.*(..))")
public void before(){}
2)within:用于匹配连接点所在的Java类或者包。
//匹配Person类中的所有方法
@Pointcut("within(com.cjm.model.Person)")
public void before(){}
//匹配com.cjm包及其子包中所有类中的所有方法
@Pointcut("within(com.cjm..*)")
public void before(){}
3) this:用于向通知方法中传入代理对象的引用。
@Before("before() && this(proxy)")
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
4)target:用于向通知方法中传入目标对象的引用。
@Before("before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
5)args:用于将参数传入到通知方法中。
@Before("before() && args(age,username)")
public void beforeAdvide(JoinPoint point, int age, String username){
//处理逻辑
}
6)@within :用于匹配在类一级使用了参数确定的注解的类,其所有方法都将被匹配。@Pointcut("@within(com.cjm.annotation.AdviceAnnotation)") - 所有被@AdviceAnnotation标注的类都将匹配
public void before(){}
7)@target :和@within的功能类似,但必须要指定注解接口的保留策略为RUNTIME。
@Pointcut("@target(com.cjm.annotation.AdviceAnnotation)")
public void before(){}
8)@args :传入连接点的对象对应的Java类必须被@args指定的Annotation注解标注。
@Before("@args(com.cjm.annotation.AdviceAnnotation)")
public void beforeAdvide(JoinPoint point){
//处理逻辑
}
9)@annotation :匹配连接点被它参数指定的Annotation注解的方法。也就是说,所有被指定注解标注的方法都将匹配。
@Pointcut("@annotation(com.cjm.annotation.AdviceAnnotation)")
public void before(){}10)bean:通过受管Bean的名字来限定连接点所在的Bean。该关键词是Spring2.5新增的。
@Pointcut("bean(person)")
public void before(){}参考资料