Spring反射机制

Spring反射机制

反射机制是加载类时,在运行时动态地获取类的信息,并且可以操作类或对象的属性、方法、构造函数等成员的能力。在 Java 里,反射机制的实现主要依赖于 java.lang.reflect 包下的多个类,以及 java.lang 包中的 Class 类。

反射的机制的应用场景

类:

  • 获取class对象

  • 获取构造器

  • 获取成员变量

  • 获取成员方法

基本的作用:

得到一个类的全部作用,然后实现操作,主要是框架的开发领域

获取class对象的方法:

java 复制代码
// 方式一:通过类名的 .class 属性
Class<?> stringClass = String.class;

// 方式二:通过对象的 getClass() 方法
String sampleStr = "example";
Class<?> anotherStringClass = sampleStr.getClass();

// 方式三:通过全限定类名的静态方法 forName()
try {
    Class<?> yetAnotherStringClass = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

获取类构造器的常见方法:

java 复制代码
1,获取所有公共构造器
使用 Class 类的 getConstructors() 方法能获取类的所有公共构造器。该方法返回一个 Constructor 数组,包含类中所有声明为 public 的构造函数。

import java.lang.reflect.Constructor;

public class GetPublicConstructors {
    public static void main(String[] args) {
        try {
            Class<?> clazz = java.util.ArrayList.class;
            Constructor<?>[] constructors = clazz.getConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


2. 获取指定参数类型的公共构造器
使用 Class 类的 getConstructor(Class<?>... parameterTypes) 方法可以获取指定参数类型的公共构造器。如果找不到匹配的构造器,会抛出 NoSuchMethodException 异常。
import java.lang.reflect.Constructor;
import java.util.ArrayList;

public class GetSpecificPublicConstructor {
    public static void main(String[] args) {
        try {
            Class<?> clazz = ArrayList.class;
            // 获取接收一个 int 类型参数的公共构造器
            Constructor<?> constructor = clazz.getConstructor(int.class);
            System.out.println(constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

3,获取所有构造器(包含私有、受保护和默认构造器)
使用 Class 类的 getDeclaredConstructors() 方法可获取类的所有构造器,无论其访问修饰符是什么。该方法返回一个 Constructor 数组,包含类中所有构造函数。
import java.lang.reflect.Constructor;

class SampleClass {
    private SampleClass() {}
    protected SampleClass(int value) {}
    public SampleClass(String str) {}
}

public class GetAllConstructors {
    public static void main(String[] args) {
        Class<?> clazz = SampleClass.class;
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

4,获取指定参数类型的构造器(包含私有、受保护和默认构造器)
使用 Class 类的 getDeclaredConstructor(Class<?>... parameterTypes) 方法可以获取指定参数类型的构造器,不论其访问修饰符。若找不到匹配的构造器,会抛出 NoSuchMethodException 异常
    
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class PrivateConstructorClass {
    private PrivateConstructorClass(int num) {}
}

public class GetSpecificDeclaredConstructor {
    public static void main(String[] args) {
        try {
            Class<?> clazz = PrivateConstructorClass.class;
            // 获取接收一个 int 类型参数的构造器
            Constructor<?> constructor = clazz.getDeclaredConstructor(int.class);
            // 设置可访问私有构造器,实现暴力访问,禁止检查访问权限
            /*
            对于私有构造器,调用前需要使用 setAccessible(true) 来突破访问限制。同时,在使用这些方法时,要注意处理可能出现的异常。
            */
            constructor.setAccessible(true);
            // 使用构造器创建对象
            Object obj = constructor.newInstance(10);
            System.out.println(obj);
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
    
获取所有的成员变量
java 复制代码
获取所有公共成员变量
使用 Class 类的 getFields() 方法可以获取类及其所有父类的公共成员变量。该方法返回一个 Field 数组,包含所有公共成员变量。
import java.lang.reflect.Field;

class ParentClass {
    public int publicParentField = 1;
}

class ChildClass extends ParentClass {
    public String publicChildField = "child";
}

public class GetPublicFields {
    public static void main(String[] args) {
        Class<?> clazz = ChildClass.class;
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}
获取指定名称的公共成员变量
java 复制代码
获取指定名称的公共成员变量
import java.lang.reflect.Field;

class MyClass {
    public int publicField = 10;
}

public class GetSpecificPublicField {
    public static void main(String[] args) {
        try {
            Class<?> clazz = MyClass.class;
            Field field = clazz.getField("publicField");
            System.out.println(field);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
获取所有成员变量(包含私有、受保护和默认成员变量)
java 复制代码
使用 Class 类的 getDeclaredFields() 方法可以获取类自身声明的所有成员变量,无论访问修饰符是什么。该方法返回一个 Field 数组,包含所有声明的成员变量,但不包含父类的成员变量。
import java.lang.reflect.Field;

class MyClass {
    private int privateField = 20;
    protected String protectedField = "protected";
    String defaultField = "default";
    public double publicField = 3.14;
}

public class GetAllDeclaredFields {
    public static void main(String[] args) {
        Class<?> clazz = MyClass.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}
获取指定名称的成员变量(包含私有、受保护和默认成员变量)

使用 Class 类的 getDeclaredField(String name) 方法可以获取指定名称的成员变量,无论其访问修饰符是什么。如果找不到对应的成员变量,会抛出 NoSuchFieldException 异常。

java 复制代码
import java.lang.reflect.Field;

class MyClass {
    private int privateField = 20;
}

public class GetSpecificDeclaredField {
    public static void main(String[] args) {
        try {
            Class<?> clazz = MyClass.class;
            Field field = clazz.getDeclaredField("privateField");
            // 设置可访问私有成员变量
            field.setAccessible(true);
            MyClass obj = new MyClass();
            int value = (int) field.get(obj);
            System.out.println(value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

若只需要获取公共成员变量,可使用 getFields() 和 getField(String name) 方法。

若要获取类自身声明的所有成员变量,可使用 getDeclaredFields() 和 getDeclaredField(String name) 方法。

对于私有成员变量,在访问前需要使用 setAccessible(true) 来突破访问限制。同时,使用这些方法时要注意处理可能出现的异常。

获取类的成员方法

获取 Class 对象:通过 ExampleClass.class 获取 ExampleClass 的 Class 对象。

创建实例:使用 getDeclaredConstructor().newInstance() 创建 ExampleClass 的实例。

获取所有公共方法:使用 getMethods() 方法获取类及其父类的所有公共方法。

获取指定公共方法并调用:使用 getMethod() 方法获取指定名称和参数类型的公共方法,然后使用 invoke() 方法调用该方法。

获取所有声明的方法:使用 getDeclaredMethods() 方法获取类自身声明的所有方法,不考虑访问权限。

获取指定非公共方法并调用:使用 getDeclaredMethod() 方法获取指定名称和参数类型的方法,对于私有方法,需要使用 setAccessible(true) 来突破访问限制,然后使用 invoke() 方法调用该方法。

java 复制代码
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

// 定义一个示例类
class ExampleClass {
    // 公共方法
    public void publicMethod() {
        System.out.println("This is a public method.");
    }

    // 带参数的公共方法
    public int publicMethodWithParams(int a, int b) {
        return a + b;
    }

    // 私有方法
    private void privateMethod() {
        System.out.println("This is a private method.");
    }

    // 受保护方法
    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }

    // 默认访问权限方法
    void defaultMethod() {
        System.out.println("This is a default method.");
    }
}

public class ReflectionMethodExample {
    public static void main(String[] args) {
        try {
            // 获取 ExampleClass 的 Class 对象
            Class<?> clazz = ExampleClass.class;
            // 创建 ExampleClass 的实例
            Object instance = clazz.getDeclaredConstructor().newInstance();

            // 1. 获取所有公共方法(包括继承自父类的公共方法)
            System.out.println("所有公共方法:");
            Method[] publicMethods = clazz.getMethods();
            for (Method method : publicMethods) {
                System.out.println(method);
            }

            // 2. 获取指定的公共方法并调用
            System.out.println("\n调用指定的公共方法:");
            Method publicMethod = clazz.getMethod("publicMethod");
            publicMethod.invoke(instance);

            Method publicMethodWithParams = clazz.getMethod("publicMethodWithParams", int.class, int.class);
            int result = (int) publicMethodWithParams.invoke(instance, 3, 5);
            System.out.println("公共方法返回结果: " + result);

            // 3. 获取所有声明的方法(只包含本类声明的方法,不考虑访问权限)
            System.out.println("\n所有声明的方法:");
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println(method);
            }

            // 4. 获取指定的私有方法并调用
            System.out.println("\n调用指定的私有方法:");
            Method privateMethod = clazz.getDeclaredMethod("privateMethod");
            // 设置可访问私有方法
            privateMethod.setAccessible(true);
            privateMethod.invoke(instance);

            // 5. 获取指定的受保护方法并调用
            System.out.println("\n调用指定的受保护方法:");
            Method protectedMethod = clazz.getDeclaredMethod("protectedMethod");
            protectedMethod.invoke(instance);

            // 6. 获取指定的默认访问权限方法并调用
            System.out.println("\n调用指定的默认访问权限方法:");
            Method defaultMethod = clazz.getDeclaredMethod("defaultMethod");
            defaultMethod.invoke(instance);

        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

使用反射实现设计一些简单的框架的设计

注解(Annotation)和反射的关系

注解(Annotation)是一种代码级别的元数据,可在类、方法、变量、参数和包等元素上添加,用于为程序提供额外信息。这些信息能在编译时、运行时被读取和处理,发挥多种重要作用,让其它程序根据注解是西安怎么操作程序。

java 复制代码
使用 @interface 关键字定义注解
使用 @interface 关键字来创建自定义注解,其语法格式如下:
public @interface 注解名 {
    // 注解元素定义
}

使用元注解配置自定义注解
元注解是用于修饰注解的注解,Java 提供了几种常用的元注解,用于控制自定义注解的作用范围、保留策略等。
@Target:指定注解可以应用的目标元素类型,如类、方法、字段等。
@Retention:指定注解的保留策略,即注解在什么阶段可用,有 SOURCE(仅在源码中保留)、CLASS(在编译后的字节码文件中保留,但运行时不可用)、RUNTIME(在运行时可用,开发时最常见)三种策略。
@Documented:表示该注解会被包含在 JavaDoc 文档中。
@Inherited:表示该注解可以被继承,即如果一个类使用了该注解,那么它的子类也会继承该注解。
    
    
定义注解元素
注解元素类似于方法,可定义默认值。注解元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型以及这些类型的数组。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 定义注解可以应用于类、方法和字段
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 定义注解在运行时可用
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 定义注解元素,可设置默认值
    String value() default "";//特殊属性,只有一个value的时候可以不写
    int count() default 1;
}

// 使用自定义注解
@MyAnnotation(value = "This is a class annotation", count = 3)
class MyClass {
    @MyAnnotation("This is a field annotation")
    private String myField;

    @MyAnnotation(value = "This is a method annotation", count = 5)
    public void myMethod() {
        System.out.println("Method is called.");
    }
}

注解的本质是一个接口,所有的注解都继承了Annotation接口。
@注解(...)其实就是一个实现类对象,实现了该注解以及annotation的接口。

注解的解析

在 Java 里,注解本身只是元数据,若要让其发挥作用,就需要对注解进行解析。注解解析主要依靠反射机制,在不同阶段(编译时、运行时)获取注解信息并执行相应逻辑。下面从元注解、注解解析的方式和示例等方面详细介绍注解解析。

注解解析方式

  1. 编译时解析
    编译时解析通常借助注解处理器(Annotation Processor)实现。注解处理器在编译阶段扫描源代码中的注解,依据注解信息生成额外的代码或进行编译时检查。
  2. 运行时解析
    运行时解析通过反射机制获取注解信息,在程序运行时根据注解信息执行相应逻辑。这种方式更为常见,下面重点介绍运行时解析。
    运行时注解解析示例。
java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

// 定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyFieldAnnotation {
    String value() default "";
    int order() default 0;
}

// 使用注解
class MyClass {
    @MyFieldAnnotation(value = "姓名", order = 1)
    private String name;

    @MyFieldAnnotation(value = "年龄", order = 2)
    private int age;
}

// 解析注解
public class AnnotationParser {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        Class<?> clazz = myClass.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(MyFieldAnnotation.class)) {
                MyFieldAnnotation annotation = field.getAnnotation(MyFieldAnnotation.class);
                System.out.println("字段名: " + field.getName());
                System.out.println("注解值: " + annotation.value());
                System.out.println("排序值: " + annotation.order());
                System.out.println("----------------------");
            }
        }
    }
}
java 复制代码
isAnnotationPresent(Class<? extends Annotation> annotationClass):判断元素(类、方法、字段等)是否存在指定类型的注解。
getAnnotation(Class<T> annotationClass):获取元素上指定类型的注解,如果不存在则返回 null。
getAnnotations():获取元素上的所有注解。
getDeclaredAnnotations():获取元素上直接声明的所有注解,不包含继承的注解。
相关推荐
此木|西贝20 分钟前
【设计模式】享元模式
java·设计模式·享元模式
李少兄1 小时前
解决Spring Boot多模块自动配置失效问题
java·spring boot·后端
bxlj_jcj2 小时前
JVM性能优化之年轻代参数设置
java·性能优化
八股文领域大手子2 小时前
深入理解缓存淘汰策略:LRU 与 LFU 算法详解及 Java 实现
java·数据库·算法·缓存·mybatis·哈希算法
不当菜虚困2 小时前
JAVA设计模式——(八)单例模式
java·单例模式·设计模式
m0_740154672 小时前
Maven概述
java·maven
吗喽对你问好2 小时前
Java位运算符大全
java·开发语言·位运算
Java致死2 小时前
工厂设计模式
java·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式
程序员JerrySUN3 小时前
驱动开发硬核特训 · Day 21(上篇) 抽象理解 Linux 子系统:内核工程师的视角
java·linux·驱动开发