06-SpringBoot工程中AOP应用实践

org.springframework.boot

spring-boot-starter-aop

说明:基于此依赖spring可以整合AspectJ框架快速完成AOP的基本实现。AspectJ 是一个面向切面的框架,他定义了 AOP 的一些语法,有一个专门的字节码生成器来生成遵守 java 规范的 class 文件。

业务切面对象设计


通过设计切面对象,为目标业务方法做功能增强,关键步骤如下:

第一步:创建注解类型,应用于切入点表达式的定义,关键代码如下:

package com.cy.pj.common.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface RequiredLog {

String operation();

}

第二步:创建切面对象,用于做日志业务增强,关键代码如下:

package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;

import com.cy.pj.sys.pojo.SysLog;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect

@Component

public class SysLogAspect {

private static final Logger log= LoggerFactory.getLogger(SysLogAspect.class);

/**

  • @Pointcut注解用于定义切入点

  • @annotation(注解)为切入点表达式,后续由此注解描述的方法为切入

  • 点方法

*/

@Pointcut(“@annotation(com.cy.pj.common.annotation.RequiredLog)”)

public void doLog(){}//此方法只负责承载切入点的定义

/**

  • @Around注解描述的方法,可以在切入点执行之前和之后进行业务拓展,

  • @param jp 连接点对象,此对象封装了要执行的目标方法信息.

  • 可以通过连接点对象调用目标方法.

  • @return 目标方法的执行结果

  • @throws Throwable

*/

@Around(“doLog()”)

public Object doAround(ProceedingJoinPoint jp)throws Throwable{

long t1=System.currentTimeMillis();

try {

//执行目标方法(切点方法中的某个方法)

Object result = jp.proceed();

long t2=System.currentTimeMillis();

log.info(“opertime:{}”,t2-t1); return result;//目标业务方法的执行结果

}catch(Throwable e){

e.printStackTrace();

long t2=System.currentTimeMillis();

log.info(“exception:{}”,e.getMessage());

throw e;

}

}

第三步:通过注解RequiredLog注解描述日志查询或删除业务相关方法,此时这个方法为日志切入点方法,例如:

@RequiredLog(operation=“公告查询”)

@Override

public List findLogs(SysLog sysLog) {

List list=syslogDao.selectLogs(sysLog);

return list;

}

第四步:测试通知业务方法,并检测日志输出以及了解其运行原理,如图所示:

在这里插入图片描述

获取并记录详细日志


第一步:定义日志pojo对象,用于封装日志信息,例如:

package com.pj.sys.pojo;

import java.util.Date;

public class SysLog {

private Integer id;

private String ip;

private String username;

private String operation;

private String method;

private String params;

private Long time;

private Integer status;

private String error;

private Date createdTime;

//自己添加set/get/toString等方法

}

第二步:修改日切面对象,获取并记录详细日志,关键代码如下:

package com.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;

import com.cy.pj.sys.pojo.SysLog;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect

@Component

public class SysLogAspect {

private static final Logger log=LoggerFactory.getLogger(SysLogAspect.class);

/**

  • @Pointcut注解用于定义切入点

  • @annotation(注解)为切入点表达式,后续由此注解描述的方法为切入

  • 点方法

*/

@Pointcut(“@annotation(com.cy.pj.common.annotation.RequiredLog)”)

public void doLog(){}//此方法只负责承载切入点的定义

/**

  • @Around注解描述的方法,可以在切入点执行之前和之后进行业务拓展,

  • @param jp 连接点对象,此对象封装了要执行的目标方法信息.

  • 可以通过连接点对象调用目标方法.

  • @return 目标方法的执行结果

  • @throws Throwable

*/

@Around(“doLog()”)

public Object doAround(ProceedingJoinPoint jp)throws Throwable{

long t1=System.currentTimeMillis();

log.info(“Start:{}”,t1);

try {

//执行目标方法(切点方法中的某个方法)

Object result = jp.proceed();

long t2=System.currentTimeMillis();

log.info(“After:{}”,t2);

doLogInfo(jp,t2-t1,null);

return result;//目标业务方法的执行结果

}catch(Throwable e){

e.printStackTrace();

long t2=System.currentTimeMillis();

doLogInfo(jp,t2-t1,e);

throw e;

}

}

//记录用户行为日志

private void doLogInfo(ProceedingJoinPoint jp,long time,Throwable e) throws Exception {

//1.获取用户行为日志

//1.1获取登录用户名(没做登录时,可以先给个固定值)

String username=“cgb”;

//1.2获取ip地址

String ip= “202.106.0.20”;

//1.3获取操作名(operation)-@RequiredLog注解中value属性的值

//1.3.1获取目标对象类型

Class<?> targetCls=jp.getTarget().getClass();

//1.3.2获取目标方法

MethodSignature ms=(MethodSignature) jp.getSignature();//方法签名

Method targetMethod=targetCls.getMethod(ms.getName(),ms.getParameterTypes());

//1.3.3 获取方法上RequiredLog注解

RequiredLog annotation =targetMethod.getAnnotation(RequiredLog.class);

//1.3.4 获取注解中定义操作名

String operation=annotation.operation();

//1.4获取方法声明(类全名+方法名)

String classMethodName=targetCls.getName()+“.”+targetMethod.getName();

//1.5获取方法实际参数信息

Object[]args=jp.getArgs();

String params=new ObjectMapper().writeValueAsString(args);

//2.封装用户行为日志

SysLog sysLog=new SysLog();

sysLog.setUsername(username);

sysLog.setIp(ip);

sysLog.setOperation(operation);

sysLog.setMethod(classMethodName);

sysLog.setParams(params);

sysLog.setTime(time);

if(e!=null) {

sysLog.setStatus(0);

sysLog.setError(e.getMessage());

}

//3.打印日志

String userLog=new ObjectMapper().writeValueAsString(sysLog);

log.info(“user.oper {}”,userLog);

}

}

第三步:进行日志业务查询,并检测是否有详细日志输出。

Spring AOP 技术进阶

=========================================================================

通知类型


Spring框架AOP模块定义通知类型,有如下几种:

  • @Around (优先级最高的通知,可以在目标方法执行之前,之后灵活进行业务拓展.)

  • @Before (目标方法执行之前调用)

  • @AfterReturning (目标方法正常结束时执行)

  • @AfterThrowing (目标方法异常结束时执行)

  • @After (目标方法结束时执行,正常结束和异常结束它都会执行)

案例分析如下:(了解,可选择进行实现)

第一步:定义注解,代码如下:

package com.cy.pj.common.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface RequiredTime{}

第二步:定义时间切面对象对象演示通知执行,关键代码如下:

package com.cy.pj.sys.service.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Component;

@Aspect

@Component

public class SysTimeAspect {

@Pointcut(“@annotation(com.cy.pj.common.annotation.RequiredTime)”)

public void doTime(){}

@Before(“doTime()”)

public void doBefore(){

System.out.println(“@Before”);

}

@After(“doTime()”)

public void doAfter(){

System.out.println(“@After”);

}

@AfterReturning(“doTime()”)

public void doAfterReturning(){

System.out.println(“@AfterReturning”);

}

@AfterThrowing(“doTime()”)

public void doAfterThrowing(){

System.out.println(“@AfterThrowing”);

}

//最重要,优先级也是最高

@Around(“doTime()”)

public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{

try {

System.out.println(“@Around.before”);

Object result = joinPoint.proceed();

System.out.println(“@Around.AfterReturning”);

return result;

}catch(Exception e){

System.out.println(“@Around.AfterThrowing”);

e.printStackTrace();

throw e;

}finally {

System.out.println(“@Around.after”);

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值