AOP 编程
1、基础
1.1、配置applicationContext.xml文件
<!--AOP配置-->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
<bean id="aspectBean" class="com.fht.aop.ApiMonitor" />
1.2、添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<!--注意与spring版本一直致-->
<version>5.2.2.RELEASE</version>
</dependency>
1.3、模板一
@Aspect
public class ApiMonitor {
private static Logger logger = LogManager.getLogger(ApiMonitor.class);
@Pointcut("execution(* com.fht.service.*.*(..))")
private void pointCutMethod() {}
//声明前置通知
@Before("pointCutMethod()")
public void doBefore(JoinPoint point) {
logger.info("@Before:模拟权限检查...");
logger.info("@Before:目标方法为:" +
point.getSignature().getDeclaringTypeName() +
"." + point.getSignature().getName());
logger.info("@Before:参数为:" + Arrays.toString(point.getArgs()));
logger.info("@Before:被织入的目标对象为:" + point.getTarget());
}
//声明后置通知
@AfterReturning(pointcut = "pointCutMethod()", returning = "returnValue")
public void doAfterReturning(JoinPoint point, Object returnValue) {
logger.info("@AfterReturning:模拟日志记录功能...");
logger.info("@AfterReturning:目标方法为:" +
point.getSignature().getDeclaringTypeName() +
"." + point.getSignature().getName());
logger.info("@AfterReturning:参数为:" +
Arrays.toString(point.getArgs()));
logger.info("@AfterReturning:返回值为:" + returnValue);
logger.info("@AfterReturning:被织入的目标对象为:" + point.getTarget());
}
//声明例外通知
@AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
logger.info("例外通知");
logger.info(e.getMessage());
}
//声明最终通知
@After("pointCutMethod()")
public void doAfter(JoinPoint point) {
logger.info("@After:模拟释放资源...");
logger.info("@After:目标方法为:" +
point.getSignature().getDeclaringTypeName() +
"." + point.getSignature().getName());
logger.info("@After:参数为:" + Arrays.toString(point.getArgs()));
logger.info("@After:被织入的目标对象为:" + point.getTarget());
}
//声明环绕通知
@Around("pointCutMethod()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
logger.info("@Around:执行目标方法之前...");
//访问目标方法的参数:
Object[] args = point.getArgs();
if (args != null && args.length > 0 && args[0].getClass() == String.class) {
args[0] = "改变后的参数1";
}
//用改变后的参数执行目标方法
Object returnValue = point.proceed(args);
logger.info("@Around:执行目标方法之后...");
logger.info("@Around:被织入的目标对象为:" + point.getTarget());
return "原返回值:" + returnValue + ",这是返回结果的后缀";
}
}
1.4、模板二
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.google.gson.Gson;
/**
*
* @ClassName: LogAspect
* @Description: 日志记录AOP实现
* @author shaojian.yu
* @date 2014年11月3日 下午1:51:59
*
*/
@Aspect
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private String requestPath = null ; // 请求地址
private String userName = null ; // 用户名
private Map<?,?> inputParamMap = null ; // 传入参数
private Map<String, Object> outputParamMap = null; // 存放输出结果
private long startTimeMillis = 0; // 开始时间
private long endTimeMillis = 0; // 结束时间
/**
*
* @Title:doBeforeInServiceLayer
* @Description: 方法调用前触发
* 记录开始时间
* @author shaojian.yu
* @date 2014年11月2日 下午4:45:53
* @param joinPoint
*/
@Before("execution(* com.yusj.controller..*.*(..))")
public void doBeforeInServiceLayer(JoinPoint joinPoint) {
startTimeMillis = System.currentTimeMillis(); // 记录方法开始执行的时间
}
/**
*
* @Title:doAfterInServiceLayer
* @Description: 方法调用后触发
* 记录结束时间
* @author shaojian.yu
* @date 2014年11月2日 下午4:46:21
* @param joinPoint
*/
@After("execution(* com.yusj.controller..*.*(..))")
public void doAfterInServiceLayer(JoinPoint joinPoint) {
endTimeMillis = System.currentTimeMillis(); // 记录方法执行完成的时间
this.printOptLog();
}
/**
*
* @Title:doAround
* @Description: 环绕触发
* @author shaojian.yu
* @date 2014年11月3日 下午1:58:45
* @param pjp
* @return
* @throws Throwable
*/
@Around("execution(* com.yusj.controller..*.*(..))")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
/**
* 1.获取request信息
* 2.根据request获取session
* 3.从session中取出登录用户信息
*/
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes)ra;
HttpServletRequest request = sra.getRequest();
// 从session中获取用户信息
String loginInfo = (String) session.getAttribute("username");
if(loginInfo != null && !"".equals(loginInfo)){
userName = operLoginModel.getLogin_Name();
}else{
userName = "用户未登录" ;
}
// 获取输入参数
inputParamMap = request.getParameterMap();
// 获取请求地址
requestPath = request.getRequestURI();
// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
outputParamMap = new HashMap<String, Object>();
Object result = pjp.proceed();// result的值就是被拦截方法的返回值
outputParamMap.put("result", result);
return result;
}
/**
*
* @Title:printOptLog
* @Description: 输出日志
* @author shaojian.yu
* @date 2014年11月2日 下午4:47:09
*/
private void printOptLog() {
Gson gson = new Gson(); // 需要用到google的gson解析包
String optTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis);
logger.info("\n user:"+userName
+" url:"+requestPath+"; op_time:" + optTime + " pro_time:" + (endTimeMillis - startTimeMillis) + "ms ;"
+" param:"+gson.toJson(inputParamMap)+";"+"\n result:"+gson.toJson(outputParamMap));
}
}
1.5、模板三
https://github.com/MusicXi/demo-aop-log
1.6、ELK:日志分析
- elk基于jolokia监控springboot应用jvm方案:https://www.jianshu.com/p/6edec67d1b10
- ELK 实现分布式 Nginx 日志分析系统:https://www.lanqiao.cn/courses/562
- ELK 安装和搭建:https://www.cnblogs.com/luomuqing/p/12878562.html
2、自定义日志+性能检测版
2.1、执行顺序测试
@Aspect
public class ServiceMonitor {
private static final Logger logger = LogManager.getLogger(ServiceMonitor.class);
private static final String BASE_PRE_STR = "======> ";
private String methodNameCache = "";
private String okMsg = "";
private String errMsg = "";
@Pointcut("execution(* com.fht.service.*.*(..))")
private void pointCutMethod() {}
// 声明前置通知
@Before("pointCutMethod()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("@Before");
}
// 声明最终通知
@After("pointCutMethod()")
public void doAfter() {
System.out.println("@After");
}
// 声明后置通知:全部执行完成之后
@AfterReturning("pointCutMethod()")
public void doAfterReturning() {
System.out.println("AfterReturning");
}
// 声明报错处理
@AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println("AfterThrowing");
}
// 声明环绕通知
@Around("pointCutMethod()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知开");
Object result = joinPoint.proceed();
System.out.println("环绕通知关");
return result;
}
}
结果
环绕通知开
@Before
2020-08-11 08:51:09 DEBUG [BaseJdbcLogger.java : 143] - ==> Preparing: select count(1) from `vote_page` where `v_title`=?
2020-08-11 08:51:09 DEBUG [BaseJdbcLogger.java : 143] - ==> Parameters: 大叔大婶大(String)
2020-08-11 08:51:09 TRACE [BaseJdbcLogger.java : 149] - <== Columns: count(1)
2020-08-11 08:51:09 TRACE [BaseJdbcLogger.java : 149] - <== Row: 0
2020-08-11 08:51:09 DEBUG [BaseJdbcLogger.java : 143] - <== Total: 1
环绕通知关
@After
AfterReturning
2.1、自定义注解
用来保存方法的功能信息,如果没有则输出方法名
/**
* 自定义注解:主要用来保存方法信息
*
* @author hubozhi
* @email 1310225330@qq.com
* @date 2020/8/10 15:32
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Desc {
String value() default "";
String okMsg() default "";
String errMsg() default "";
}
2.2、AOP工具类
获取方法的注解
public class AOPUtil {
public static <T extends Annotation> T getAnn(JoinPoint joinPoint, Class<T> annotationClass) {
// 获取方法名
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
Class<?>[] argTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
argTypes[i] = args[i].getClass();
}
Method method = null;
try {
method = joinPoint.getTarget().getClass().getMethod(methodName, argTypes);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
if (method != null) {
return method.getAnnotation(annotationClass);
}else{
return null;
}
}
}
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小航
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果