Java获取CGLIB代理类上的注解为null的问题

Java获取CGLIB代理类上的注解为null的问题

一、场景

搭建框架的时候踩到的一个坑。spring boot项目,项目中用到了AOP记录Log,又用到了拦截器判断权限和记录系统log到表中,于是在拦截器中需要
HandlerMethod handlerMethod = (HandlerMethod) handler;获取类注解和方法注解,但是获取的类注解是null,源码如下:

 // 获取类注解
Class<? extends Object> clazz = handlerMethod.getBean().getClass();
CategoryLog categoryLog = AnnotationUtils.findAnnotation(clazz, CategoryLog.class);
if (Objects.isNull(categoryLog)) {
     return;
}
String menu = categoryLog.menu();
// 获取方法注解
Method method = handlerMethod.getMethod();
DescLog descLog = method.getAnnotation(DescLog.class);
if (Objects.isNull(descLog)) {
     return;
}

二、原因分析

是由于上述代码我们所需要获取的类首先被AOP拦截生成了代理对象。我是debug发现clazz对象是个代理对象可知的,如下图。
在这里插入图片描述
上图可知,是cglib代理后的对象(默认是jdk的,我在application.yml里手动指定的cglib),那么我们获取代理对象的注解信息肯定是获取不到的,需要获取原始对象才能拿到注解等信息。

三、解决方案

通过org.springframework.core.annotation.AnnotationUtils注解提供的findAnnotation方法即可完美解决问题,代码在上面已经给到了。用clazz.getAnnotation(CategoryLog.class);这种方式是获取不到的,获取的是代理对象的,为null。

四、广告

QQ群:458430385
微信公众号
在这里插入图片描述

<think>好的,我现在需要解决用户关于@ServerEndpoint注解导致代理类错误的问题。用户提到了在WebSocket中使用@ServerEndpoint时,无法注入Service层的问题,并引用了相关的资料。首先,我需要理解这个问题的根本原因,然后根据提供的引用内容找到解决方案。 根据用户提供的引用[1],问题是由于AOP切面导致的,因为Spring的代理机制可能无法正确处理@ServerEndpoint注解的类。引用[4]中的代码显示,用户尝试在WebSocket类中使用@Autowired注入MyService,但可能因为该类是由Java WebSocket API直接实例化的,而不是由Spring管理的,导致依赖注入失败。此外,引用[3]提到Spring AOP的类内部方法调用问题,这可能与代理类的工作方式有关,比如CGLIB或JDK动态代理无法正确创建被@ServerEndpoint注解的类的代理,导致注入的Service为null。 接下来,解决方案方面,引用[1]和引用[4]提到了手动获取Bean的方法,比如通过ApplicationContextAware来获取Spring上下文,从而手动注入Service实例。另外,可能需要调整Spring的配置,如引用[2]中的SPR-16677问题,可能需要添加内省配置来支持代理类。此外,引用[5]讨论了HTTP和WebSocket的协议差异,但可能与此问题关联不大,但需要确认WebSocket的长连接是否影响Spring的上下文管理。 需要验证这些解决方案的有效性,并确保语法正确,比如使用正确的LaTeX格式。同时,生成相关问题时要围绕WebSocket、依赖注入、AOP代理等方面展开。</think>### @ServerEndpoint 注解引发代理类错误的原因及解决方案 #### 原因分析 1. **类实例化机制冲突** - `@ServerEndpoint` 是 Java WebSocket API 的注解,由容器(如 Tomcat)直接实例化,而 **Spring 管理的 Bean 需要通过代理类CGLIB/JDK 动态代理)创建**。由于容器和 Spring 的实例化流程分离,导致 **`@Autowired` 注入失效**,最终注入的 Service 对象为 `null`[^1][^4]。 2. **AOP 代理限制** - 若被代理的类(如 `MyWebSocket`)未被 Spring 管理,或未通过 Spring 容器初始化,AOP 切面无法正确生成代理对象。这会导致 **类内部方法调用无法触发切面逻辑**,甚至破坏依赖注入流程[^3]。 3. **内省配置缺失** - Spring 对部分类的代理支持需要显式配置。例如,针对 `@ServerEndpoint` 类,若未配置内省规则(如 `@EnableAspectJAutoProxy` 或 XML 配置),可能导致 **代理类生成失败**,进而影响依赖注入[^2]。 #### 解决方案 1. **手动注入 Service 对象** 通过 `ApplicationContextAware` 获取 Spring 上下文,手动注入依赖: ```java @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext ctx) { context = ctx; } public static <T> T getBean(Class<T> clazz) { return context.getBean(clazz); } } @ServerEndpoint("/websocket") public class MyWebSocket { private MyService myService = SpringContextUtil.getBean(MyService.class); // ... } ``` 2. **调整 Spring 代理配置** 在配置类中添加 `@EnableAspectJAutoProxy(proxyTargetClass = true)`,强制使用 CGLIB 代理,避免 JDK 动态代理因接口缺失导致的错误: ```java @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class AppConfig {} ``` 3. **静态成员变量持有 Bean 实例** 将 Service 对象声明为静态变量,并通过 `@PostConstruct` 初始化: ```java @ServerEndpoint("/websocket") public class MyWebSocket { private static MyService myService; @Autowired public void setMyService(MyService service) { myService = service; } // ... } ``` 4. **使用 `@Configurable` 注解** 结合 AspectJ 编译时织入,使容器实例化的类支持 Spring 依赖注入: ```java @Configurable(preConstruction = true) @ServerEndpoint("/websocket") public class MyWebSocket { @Autowired private MyService myService; // ... } ``` 需在 Maven/Gradle 中配置 AspectJ 插件。 #### 关键问题总结 | 问题类型 | 表现 | 解决思路 | |----------------|--------------------------|-----------------------------| | 实例化流程冲突 | `@Autowired` 失效 | 手动获取 Bean 或静态注入 | | 代理生成失败 | AOP 切面不生效 | 调整代理类型或启用 AspectJ | | 配置缺失 | 类未被 Spring 管理 | 添加内省配置或注解支持 |
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【原】编程界的小学生

没有打赏我依然会坚持。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值