如何将切面函数中加载的对象传递至标注注解的方法?
如何在Spring AOP切面中传递对象到目标方法,避免重复数据库查询
当然可以实现,下面提供几种实用方案,按需选择:
方案1:通过@Around通知修改目标方法参数
@Before通知无法修改方法参数,改用@Around通知获取ProceedingJoinPoint,直接修改参数数组后执行目标方法,适合允许调整目标方法参数的场景。
切面代码修改:
@Aspect @Component @Slf4j public class MyAspect { @Autowired private MyService myService; @Around("@annotation(myAnnotationCheck)") @SneakyThrows public Object check(ProceedingJoinPoint joinPoint, MyAnnotationCheck myAnnotationCheck) { // 从数据库加载对象 EntityA myObj = myService.loadFromDB(); // 获取原方法参数列表 Object[] args = joinPoint.getArgs(); // 构造新参数数组,将加载好的对象加入末尾 Object[] newArgs = new Object[args.length + 1]; System.arraycopy(args, 0, newArgs, 0, args.length); newArgs[args.length] = myObj; // 执行修改参数后的目标方法并返回结果 return joinPoint.proceed(newArgs); } }
目标方法修改:
@MyAnnotationCheck(checkType = "abc") public void myMethod(MyRequest request, EntityA objA) { // 直接使用切面传递过来的objA,无需再次查询数据库 // ...业务逻辑 }
注意:如果需要调整参数位置,修改newArgs的赋值顺序即可,但要保证目标方法参数类型、顺序和切面传递的一致;若目标方法有重载,需确保注解能精准匹配到目标方法。
方案2:使用ThreadLocal存储对象(无侵入修改目标方法)
通过ThreadLocal在切面中存储加载的对象,目标方法直接从ThreadLocal中获取,无需修改方法参数,侵入性极低。
第一步:创建ThreadLocal工具类
public class EntityHolder { private static final ThreadLocal<EntityA> ENTITY_A_THREAD_LOCAL = new ThreadLocal<>(); public static void setEntityA(EntityA entity) { ENTITY_A_THREAD_LOCAL.set(entity); } public static EntityA getEntityA() { return ENTITY_A_THREAD_LOCAL.get(); } public static void clear() { ENTITY_A_THREAD_LOCAL.remove(); } }
第二步:修改切面代码
@Aspect @Component @Slf4j public class MyAspect { @Autowired private MyService myService; @Around("@annotation(myAnnotationCheck)") @SneakyThrows public Object check(ProceedingJoinPoint joinPoint, MyAnnotationCheck myAnnotationCheck) { try { EntityA myObj = myService.loadFromDB(); EntityHolder.setEntityA(myObj); // 执行目标方法 return joinPoint.proceed(); } finally { // 必须清理ThreadLocal,避免线程池复用导致内存泄漏或数据混乱 EntityHolder.clear(); } } }
第三步:修改目标方法
@MyAnnotationCheck(checkType = "abc") public void myMethod(MyRequest request) { EntityA objA = EntityHolder.getEntityA(); // 直接使用objA,无需查询数据库 // ...业务逻辑 }
关键注意点:必须在finally块中清理ThreadLocal,否则线程池中的线程会残留旧数据,引发难以排查的问题。
方案3:Spring MVC场景专属——自定义参数解析器
如果目标方法是Controller层接口,可以实现HandlerMethodArgumentResolver,结合注解自动注入对象,更符合Spring MVC的参数注入习惯。
第一步:新增参数注入注解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface LoadedEntityA { }
第二步:实现参数解析器
public class EntityAArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { // 判断参数是否标记了@LoadedEntityA注解,且类型为EntityA return parameter.hasParameterAnnotation(LoadedEntityA.class) && parameter.getParameterType().equals(EntityA.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { // 从ThreadLocal中获取切面加载好的对象 return EntityHolder.getEntityA(); } }
第三步:注册参数解析器
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new EntityAArgumentResolver()); } }
第四步:修改目标方法
@MyAnnotationCheck(checkType = "abc") public void myMethod(MyRequest request, @LoadedEntityA EntityA objA) { // 直接使用注入的objA // ...业务逻辑 }
这种方案更优雅,但仅适用于Spring MVC环境。
内容的提问来源于stack exchange,提问作者Laodao
相关产品推荐
相关产品推荐

