aop 的应用-
记录日志
1,自定义注解
1.1 @EnableRequestLog
用于启动日志记录功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy
@Import(LogConfigurationRegistrar.class)
public @interface EnableRequestLog {
Class<? extends Annotation>[] mark() default {}; // 添加一个属性,用于指定需要记录日志的注解
/**
* 记录日志逻辑
* @return {@link Class}<{@link ?} {@link extends} {@link TimeCost}>
*/
Class<? extends TimeCost> ref() default DefaultMethodLog.class;
}
1.2 @Loggable 标记需要记录日志的类或请求
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
String value() default "";
}
1.3 LogConfigurationRegistrar 导入配置
public class LogConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableRequestLog.class.getName());
Object o = annotationAttributes.get("mark");
Class<? extends Annotation>[] arg = (Class<? extends Annotation>[]) o;
BeanDefinition beanDefinition = null;
//获取切点
Pointcut pointcut = forAnnotations(arg);
//实际记录日志
Object ref = annotationAttributes.get("ref");
Class<? extends TimeCost> refClass = (Class<? extends TimeCost>) ref;
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(refClass);
builder.addConstructorArgValue(pointcut);
beanDefinition = builder.getBeanDefinition();
registry.registerBeanDefinition(TimeCost.class.getName(), beanDefinition);
}
public Pointcut forAnnotations(Class<? extends Annotation>... annotations) {
ComposablePointcut pointcut = null;
for (Class<? extends Annotation> annotation : annotations) {
if (pointcut == null) {
pointcut = new ComposablePointcut(classOrMethod(annotation));
} else {
pointcut.union(classOrMethod(annotation));
}
}
return pointcut;
}
private Pointcut classOrMethod(Class<? extends Annotation> annotation) {
return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),
new AnnotationMatchingPointcut(annotation, true));
}
}
2 接口相关
2.1 记录日志,与aop 顶级接口
public interface TimeCost extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
//打印参数
public void logParam(Object arg);
}
2.2 LogStorage 持久化日志
public interface LogStorage {
public void saveLog(Object arg);
}
2.2.1 默认实现
@Component
public class DefaultLogStorage implements LogStorage{
@Override
public void saveLog(Object arg) {
System.out.println("arg = " + arg);
}
}
2.3 aop 实际逻辑 抽象类 MethodLogAspectSupport ,真正的实现要依靠 其实现类
public abstract class MethodLogAspectSupport implements TimeCost, ApplicationContextAware {
private Pointcut pointcut;
private ApplicationContext applicationContext;
private LogStorage logStorage;
public MethodLogAspectSupport(Pointcut pointcut ) {
this.pointcut = pointcut;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return doInvoke(invocation);
}
public abstract Object doInvoke(MethodInvocation methodInvocation) throws Throwable;
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return false;
}
public String convertToCurl() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 获取请求 URL
String url = request.getRequestURL().toString();
// 构建 cURL 命令
StringBuilder curlCmd = new StringBuilder();
curlCmd.append("curl -X ").append(request.getMethod()).append(" '").append(url).append("'");
// 添加请求头
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
curlCmd.append(" -H '").append(headerName).append(": ").append(headerValue).append("'");
}
// 添加请求参数
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String paramValue = request.getParameter(paramName);
curlCmd.append(" --data '").append(paramName).append("=").append(paramValue).append("'");
}
return curlCmd.toString();
}
public void setPointcut(Pointcut pointcut) {
this.pointcut = pointcut;
}
@Override
public void logParam(Object arg) {
if (logStorage != null) {
logStorage.saveLog(arg);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
LogStorage bean = this.applicationContext.getBean( LogStorage.class);
if (bean != null) {
this.logStorage = bean;
}
if (this.logStorage == null) {
this.logStorage = new DefaultLogStorage();
}
}
}
2.3.1 自定义保存 逻辑 CustomMethodLog
@Slf4j
public class CustomMethodLog extends MethodLogAspectSupport {
public CustomMethodLog(Pointcut pointcut) {
super(pointcut);
}
@Override
public Object doInvoke(MethodInvocation methodInvocation) throws Throwable {
Loggable loggable = methodInvocation.getMethod().getAnnotation(Loggable.class);
if (loggable == null) {
Method method = methodInvocation.getMethod();
Class<?> declaringClass = method.getDeclaringClass();
loggable = declaringClass.getAnnotation(Loggable.class);
}
//模拟~~~~
log.info("自定义{},执行时间:{} curl:{}", loggable.value(), LocalDateTime.now(),convertToCurl());
Map<String, Object> objectMap = new HashMap<>();
RequestLog requestLog = new RequestLog();
requestLog.setMethod(methodInvocation.getMethod().getName());
objectMap.put("温度", "103");
objectMap.put("湿度", "50");
requestLog.setAttribute(objectMap);
this.logParam(requestLog);
return methodInvocation.proceed();
}
}
具体来说,这个功能包括以下几个关键部分:
@EnableRequestLog 注解:这是一个自定义的注解,用于启用日志记录功能。它可以指定需要记录日志的注解类型,以及日志记录的实现类。
@Loggable 注解:这是用来标记需要记录日志的方法或类的注解。
LogConfigurationRegistrar 类:这个类实现了 ImportBeanDefinitionRegistrar 接口,用于注册Bean定义。它会根据 @EnableRequestLog 注解的配置信息创建 TimeCost 的实现类,并将其注册到Spring容器中。
TimeCost 接口:这是一个顶级接口,定义了记录日志的方法和一些AOP相关的方法。
MethodLogAspectSupport 抽象类:这是 TimeCost 接口的抽象实现类,实际的日志记录逻辑需要在其子类中实现。
CustomMethodLog 类:这是 MethodLogAspectSupport 的一个具体子类,实现了自定义的日志记录逻辑。
总体来说,这个功能允许你使用 @Loggable 注解标记需要记录日志的方法或类,然后通过 @EnableRequestLog 注解启用日志记录功能,并指定日志记录的实现类。当方法被调用时,AOP会拦截这些方法,并在拦截器中执行日志记录逻辑