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 里,注解本身只是元数据,若要让其发挥作用,就需要对注解进行解析。注解解析主要依靠反射机制,在不同阶段(编译时、运行时)获取注解信息并执行相应逻辑。下面从元注解、注解解析的方式和示例等方面详细介绍注解解析。
注解解析方式
- 编译时解析
编译时解析通常借助注解处理器(Annotation Processor)实现。注解处理器在编译阶段扫描源代码中的注解,依据注解信息生成额外的代码或进行编译时检查。 - 运行时解析
运行时解析通过反射机制获取注解信息,在程序运行时根据注解信息执行相应逻辑。这种方式更为常见,下面重点介绍运行时解析。
运行时注解解析示例。
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():获取元素上直接声明的所有注解,不包含继承的注解。