ApplicationContext获取对象时注解丢失问题及 Arthas 验证方法

引言


在使用 Spring 框架时遇到的一个常见问题:通过 ApplicationContext 获取到的对象无法获取到注解。
本文的目的:探讨这个问题的原因,并展示如何使用 Arthas 工具来验证和解决问题。

问题描述

描述具体的问题场景:通过 ApplicationContext 获取到的 Bean 对象无法获取到其上的注解。

提供一个简单的示例代码来复现问题:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    
    

    public static void main(String[] args) {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = context.getBean(MyService.class);

        // 尝试获取注解
        MyAnnotation annotation = myService.getClass().getAnnotation(MyAnnotation.class);
        if (annotation != null) {
    
    
            System.out.println("Annotation found: " + annotation.value());
        } else {
    
    
            System.out.println("Annotation not found.");
        }
        
    }

    public interface MyService {
    
    
        void doSomething();
    }
	
	@MyAnnotation(value = "example")
    public static class MyServiceImpl implements MyService {
    
    
        @Override
        public void doSomething() {
    
    
            System.out.println("Doing something...");
        }
    }
	
    public static class AppConfig {
    
    
        // 配置 Bean
    }
    
}

解决方案

Spring AOP 默认使用 CGLIB 或 JDK 动态代理来创建代理对象。如果你通过 ApplicationContext 获取到的是一个代理对象,而不是实际的目标对象,那么你可能无法直接从代理对象上获取到注解。

获取目标类:使用 AopUtils 工具类来获取目标类。
获取目标对象:使用 AopProxyUtils 工具类来获取目标对象。

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AnnotationExample {
    
    

    public static void main(String[] args) {
    
    
        // 创建 ApplicationContext
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取 Bean
        MyService myService = context.getBean(MyService.class);

        // 获取目标类
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(myService);
        System.out.println("Target Class: " + targetClass.getName());

        // 获取目标对象
        Object targetObject = AopProxyUtils.getSingletonTarget(myService, MyService.class);
        if (targetObject != null) {
    
    
            MyService targetMyService = (MyService) targetObject;
            MyAnnotation annotation = targetMyService.getClass().getAnnotation(MyAnnotation.class);
            if (annotation != null) {
    
    
                System.out.println("Annotation found: " + annotation.value());
            } else {
    
    
                System.out.println("Annotation not found.");
            }
        } else {
    
    
            System.out.println("Target object is null.");
        }
    }

    public interface MyService {
    
    
        void doSomething();
    }
	
	@MyAnnotation(value = "example")
    public static class MyServiceImpl implements MyService {
    
    
        @Override
        public void doSomething() {
    
    
            System.out.println("Doing something...");
        }
    }

    public static class AppConfig {
    
    
        // 配置 Bean
    }
    
}

使用 Arthas 验证

这里我以我当时开发时所遇到的问题同时去使用arthas来验证:

上述代码我是从ApplicationContext通过接口类型的方式去获取实现类的,但是有个问题,我其中一个实现类里面用到了事物回滚,所以我这个类会被Spring生成一个CGLIB的代理对象:

使用了回滚的事物注解:

可以断点来验证类型:

我们使用arthas查看cglib对象的结构:

  • 搜索类:使用 sc 命令查找相关的类。
  • 反编译源码:使用 jad 命令反编译代理类和目标类,查看其源码。
# 示例启动 Arthas 并选择目标进程
java -jar arthas-boot.jar

# 搜索实现类的全路径包名加上*可以将代理类也检索出来
sc com.example.MyServiceImpl*

# 示例反编译代理类
jad com.example.MyService$$EnhancerBySpringCGLIB$$c8826fe2

# 示例反编译目标类
jad com.example.MyServiceImpl

本人电脑展示一下:
查找目标类与代理类:

查看代理类:

查看原始类:

总结

  • 注解被保留:注解的 @Retention 应设置为 RUNTIME,否则也会导致获取不到注解的问题。
  • 检查代理对象:使用 AopProxyUtils 和 AopUtils 获取目标类和目标对象。
  • debug调试信息:添加调试信息来查看具体的对象类型和类信息。

通过这些方法就成功地从 Spring 的 ApplicationContext 获取的对象中读取到注解。

猜你喜欢

转载自blog.csdn.net/qq_31762741/article/details/142919569