Java反射 结合spring源码

97 阅读3分钟

Java 是一种极其流行的面向对象编程语言,拥有丰富的特性和功能。其中之一就是反射(Reflection),它为开发人员提供了一种动态获取信息以及在运行时操作类、接口、对象等的能力。

什么是 Java 反射?

Java 反射指的是在程序运行时检查或修改其结构、状态以及行为的能力。通过反射,我们可以动态地创建对象、调用方法、访问成员变量,并且可以更加灵活地操作类的各种元素。

如何使用 Java 反射?

获取 Class 对象

// 方式1
Class<?> clazz = OrderContext.class;
// 方式2
Class<?> clazz = Class.forName(cn.xicode.cloud.lab.spring.OrderContext");

创建对象

// 方式1
OrderContext obj = (OrderContext) clazz.newInstance();
// 方式2
Constructor<OrderContext> constructor = clazz.getConstructor(parameterTypes);
OrderContext obj = constructor.newInstance(args);

访问父类

// 获取父类class对象
Class<? super OrderContext> superclass = clazz.getSuperclass();
// 获取父类泛型信息
Type genericSuperclass = clazz.getGenericSuperclass();
// 父类泛型名称
String typeName = genericSuperclass.getTypeName();
// 获取class实现接口信息
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
    System.out.println("interfaces:" + anInterface.getName());
}

调用方法

Method method = clazz.getMethod("methodName", parameterTypes);
method.invoke(obj, args);

访问字段

// 根据字段名获取Field对象
Field field = clazz.getDeclaredField("fieldName");
// 字段赋值
field.setAccessible(true);
field.set(obj, value);
// 获取字段名
String fieldName = field.getName();
// 获取字段类型
Class<?> fieldType = field.getType();
// 获取字段泛型类型
Type genericType = field.getGenericType();
// 字段段是否引入注解 如:Autowired注解
field.isAnnotationPresent(Autowired.class);

spring 反射的使用

在创建 bean 的属性赋值过程中,就需要通过反射获取对象字段的信息并进行依赖注入。

对添加@Autowired 注解的属性找到对应 Class

注意:@Autowired 注解除了可以作用在字段,也可以作用在 set 方法和构造器上

  1. 是否有某个注解
// 字段是否加了@Autowired注解
boolean annotationPresent=  field.isAnnotationPresent(Autowired.class);
// 方法获取注解信息
Annotation[] allMAnnos = method.getAnnotations();
Annotation[] deMAnnos = method.getDeclaredAnnotations();
Annotation annotation = method.getAnnotation(Autowired.class);
  1. 获取字段泛型类型对象
Type genericType = field.getGenericType();

处理泛型相关的字段

如果是泛型类型 或 泛型变量 或 泛型数组,需要找出泛型对应字段的 Class 对象

public static void main(String[] args) {
        // 获取class类型
        Class<OrderContext> clazz = OrderContext.class;
        // 获取父类泛型信息
        Type genericSuperclass = clazz.getGenericSuperclass();
        String typeName = genericSuperclass.getTypeName();
        System.out.println("父类泛型信息:" + typeName);

        // 获取父类泛型变量名
        Class<? super OrderContext> superclass = clazz.getSuperclass();
        TypeVariable<? extends Class<? super OrderContext>>[] typeParameters = superclass.getTypeParameters();
        for (TypeVariable<? extends Class<?>> typeParameter : typeParameters) {
            System.out.println("父类泛型变量名:" + typeParameter.getName());
        }
}


// 输出

父类泛型信息:cn.xicode.cloud.lab.spring.AbstractContext<cn.xicode.cloud.lab.spring.OrderService>
父类泛型变量名:R

看上代码输出,R 泛型变量对应实际类型是 cn.xicode.cloud.lab.spring.OrderService,进而可以通过 Class 和 bean 名称从 BeanFactory 中找到或创建该对象,并通过反射对字段进行属性赋值

Java 反射的优缺点

优点

  • 灵活性:可以在运行时获取类的信息并进行操作,使得代码更加灵活。
  • 动态性:可以动态地加载类、调用方法,以及处理对象。

缺点

  • 性能损耗:反射操作通常比直接代码执行更慢,因为它需要在运行时进行额外的检查和解析。
  • 安全性问题:由于反射可以绕过访问限制,可能会导致安全隐患。

最佳实践

缓存 Class 对象

获取 Class 对象是一个昂贵的操作,应该尽量避免重复获取,而是将其缓存起来重复使用。

异常处理

在使用反射时,一定要注意合理处理可能出现的异常,如 ClassNotFoundException、NoSuchMethodException 等。