动态切面

简介: `AdviceConfiguration` 类用于动态注册一个基于 AspectJ 的切面顾问,该顾问通过 `@ConditionalOnExpression` 注解控制是否生效。配置中包含一个从 Apollo 获取的 JSON 值,用于构建方法拦截器的切入点表达式,涉及特定包和类。拦截器 `ControllerAdvice` 实现了 `MethodInterceptor`,用于记录请求日志,包括 URL、参数和执行时间,同时根据配置决定是否对返回结果进行加密。
package integration.configuration;

import cn.hutool.core.collection.CollUtil;
import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
import integration.advice.ControllerAdvice;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 动态注入需要切面的类
 * 没什么卵用
 */
@Configuration
public class AdviceConfiguration {

    private String expression;

    [
        {
            "package": "com.xx.xx.xxxxxx.xxxxxxx.controller",
            "class": [
                "XXXXXXXController",
                "XXXXXXXXController"
            ]
        }
    ]
    
    @ApolloJsonValue("${advice.expression:}")
    private void setExpression(List<Map<String, Object>> value) {
        if (CollUtil.isEmpty(value)) return;
        StringBuilder sb = new StringBuilder();
        for (Map<String, Object> item : value) {
            List<String> clazz = (List<String>) item.get("class");
            for (String s : clazz) {
                sb.append("execution(* ");
                sb.append(item.get("package")).append(".");
                sb.append(s);
                sb.append(".*(..)) || ");
            }
        }
        expression = sb.substring(0, sb.length() - 4);
    }

    private final Map<String, Boolean> aa = new HashMap<>();

    @Bean
    @ConditionalOnExpression("${advice.switch:false}")
    public AspectJExpressionPointcutAdvisor config() {
        AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
        advisor.setExpression(expression);
        advisor.setAdvice(new ControllerAdvice());
        return advisor;
    }

}

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import integration.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 这俩配合着用
*/
public class ControllerAdvice implements MethodInterceptor {
    
    @Value("${isPrd:false}")
    private boolean isEncryption;

    private final ObjectMapper objectMapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        String requestUrl = getRequestUrl();
        Map<String, String> headers = LoginUtil.getHeaders();
        Object[] args = invocation.getArguments();
        log.info("Controller.LogAspect url:{} headers:{}, param:{} ", requestUrl, headers, printObj(args));
        Object result = invocation.proceed();
        log.info("Controller.LogAspect url:{} cost:{}ms result:{} ", requestUrl, System.currentTimeMillis() - start, printObj(result));
        return result;
    }

    private String getRequestUrl(){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes == null ? "" : attributes.getRequest().getRequestURI();
    }

    private Object printObj(Object o){
        try {
            return isEncryption ? Base64.getEncoder().encodeToString(objectMapper.writeValueAsBytes(o)) : o;
        } catch (JsonProcessingException e) {
            return e.getMessage();
        }
    }
}
相关文章
|
6月前
|
XML 数据格式
Karl_Albright:Rougamo、Fody 实现静态Aop
4. Fody 有很多其他的“插件”,大家可以多试试 AutoProperties.Fody: 这个外接程序为您提供了对自动属性的扩展控制,比如直接访问backing字段或拦截getter和setter。 PropertyChanged.Fody: 将属性通知添加到实现INotifyPropertyChanged的所有类。 InlineIL.Fody: 在编译时注入任意IL代码。 MethodDecorator.Fody:通过IL重写编译时间装饰器模式。 NullGuard.Fody: 将空参数检查添加到程序集。
71 4
|
8月前
|
Java 测试技术 开发者
【亮剑】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强
【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。
226 1
|
缓存 Java Spring
Spring AOP中CGLIB代理对象增强通知执行原理
Spring AOP中CGLIB代理对象增强通知执行原理
93 0
|
XML 设计模式 安全
Spring AOP:原理、 通知、连接点、切点、切面、表达式
Spring AOP:原理、 通知、连接点、切点、切面、表达式
937 0
Spring AOP:原理、 通知、连接点、切点、切面、表达式
|
Java Maven Spring
如何通过自定义注解来实现 Spring AOP,以便更加灵活地控制方法的拦截和增强?
如何通过自定义注解来实现 Spring AOP,以便更加灵活地控制方法的拦截和增强?
118 0
|
Java Spring
Spring AOP统一功能处理(切面、切点、连接点、通知)(下)
Spring AOP统一功能处理(切面、切点、连接点、通知)(下)
154 0
|
前端开发 Java Maven
Spring AOP统一功能处理(切面、切点、连接点、通知)(上)
Spring AOP统一功能处理(切面、切点、连接点、通知)(上)
316 0
|
SQL Java 数据库连接
注解方式实现AOP的接口控制
注解方式实现AOP的接口控制
注解方式实现AOP的接口控制
|
XML 监控 Java
Spring之AOP的切点、通知、切点表达式以及知识要点
Spring之AOP的切点、通知、切点表达式以及知识要点
191 0
Spring之AOP的切点、通知、切点表达式以及知识要点
|
Java 开发工具 Windows
六、集成全局AOP切面,进行访问时间与日志打印
集成全局AOP切面,进行访问时间与日志打印
196 0
六、集成全局AOP切面,进行访问时间与日志打印