专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java 反射与注解详解

temp10 2025-04-24 08:25:45 java教程 7 ℃ 0 评论

一、反射(Reflection)

反射允许程序在运行时动态获取类的信息(如类名、方法、字段、构造器等),并操作类或对象(如创建实例、调用方法、访问字段)。它是实现框架(如Spring、MyBatis)和动态代理的核心技术。


1. 反射核心 API

1.1 获取 Class 对象

Java 反射与注解详解

// 方式1:通过类名.class
Class<?> clazz1 = String.class;

// 方式2:通过对象.getClass()
String str = "hello";
Class<?> clazz2 = str.getClass();

// 方式3:通过Class.forName()
Class<?> clazz3 = Class.forName("java.lang.String");

1.2 获取类信息

// 获取类名
String className = clazz.getName();      // java.lang.String
String simpleName = clazz.getSimpleName(); // String

// 获取字段(包括私有字段)
Field[] fields = clazz.getDeclaredFields();

// 获取方法(包括私有方法)
Method[] methods = clazz.getDeclaredMethods();

// 获取构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

1.3 操作对象

// 创建实例(通过无参构造器)
Object instance = clazz.newInstance(); // 已过时,建议用构造器
Constructor<?> constructor = clazz.getDeclaredConstructor();
Object instance = constructor.newInstance();

// 调用方法
Method method = clazz.getDeclaredMethod("methodName", int.class, String.class);
method.setAccessible(true); // 访问私有方法
Object result = method.invoke(instance, 123, "arg");

// 访问/修改字段
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 访问私有字段
Object value = field.get(instance);
field.set(instance, "new value");

2. 应用场景

  • 动态代理:通过 Proxy 和 InvocationHandler 实现接口的代理。
  • 框架设计:如Spring的依赖注入(@Autowired)、MyBatis的Mapper动态实现。
  • 序列化/反序列化:JSON库(如Jackson)通过反射读取对象字段。

二、注解(Annotation)

注解是代码中的元数据,用于标记类、方法或字段,提供额外信息供编译时或运行时处理。Java内置注解(如 @Override)和自定义注解均可通过反射读取。


1. 注解的定义

1.1 元注解(定义注解的注解)

  • @Target:指定注解可应用的目标(类、方法、字段等)。
@Target(ElementType.METHOD) // 只能修饰方法
  • @Retention:指定注解保留策略。
@Retention(RetentionPolicy.RUNTIME) // 运行时保留(反射可读取)
  • @Documented:注解是否出现在Javadoc中。
  • @Inherited:注解是否可被子类继承。

1.2 自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default value"; // 属性
    int priority() default 1;
}

2. 注解的使用

public class MyClass {
    @MyAnnotation(value = "test", priority = 2)
    public void myMethod() { ... }
}

3. 注解的解析(通过反射)

Method method = MyClass.class.getMethod("myMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    String value = annotation.value(); // "test"
    int priority = annotation.priority(); // 2
}

三、反射与注解结合应用

1. 模拟 Spring 的依赖注入

public class MyContainer {
    private Map<String, Object> beans = new HashMap<>();

    public void init() throws Exception {
        // 扫描包下的类
        Class<?> clazz = Class.forName("com.example.MyService");
        if (clazz.isAnnotationPresent(Component.class)) {
            Object instance = clazz.newInstance();
            beans.put(clazz.getSimpleName(), instance);
        }
    }

    public Object getBean(String name) {
        return beans.get(name);
    }
}

2. 自定义 ORM 框架(字段映射)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
    String name();
}

public class User {
    @Column(name = "user_id")
    private int id;
    
    @Column(name = "user_name")
    private String name;
}

// 反射解析注解生成SQL
public String buildInsertSQL(Object obj) {
    StringBuilder sql = new StringBuilder("INSERT INTO ");
    Class<?> clazz = obj.getClass();
    sql.append(clazz.getSimpleName()).append(" (");
    
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(Column.class)) {
            Column column = field.getAnnotation(Column.class);
            sql.append(column.name()).append(", ");
        }
    }
    sql.delete(sql.length()-2, sql.length()).append(") VALUES (...)");
    return sql.toString();
}

3. 自定义单元测试框架

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
    String description() default "";
}

public class TestRunner {
    public static void main(String[] args) throws Exception {
        Class<?> testClass = Class.forName("com.example.MyTestClass");
        for (Method method : testClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyTest.class)) {
                method.invoke(testClass.newInstance());
            }
        }
    }
}

四、注意事项

  1. 反射的性能
  2. 反射操作比直接调用慢,频繁调用需缓存 Method、Field 对象。
  3. 避免在性能敏感场景过度使用反射。
  4. 安全性
  5. 反射可以绕过访问权限(setAccessible(true)),需谨慎使用。
  6. 安全管理器(SecurityManager)可限制反射操作。
  7. 注解的保留策略
  8. 若注解的 @Retention 设置为 SOURCE 或 CLASS,则运行时无法通过反射读取。

总结

  • 反射是动态操作类的工具,适用于框架、动态代理等场景,但需注意性能和安全。
  • 注解为代码添加元数据,结合反射可实现灵活的逻辑控制(如依赖注入、ORM映射)。

深入学习方向

  • 动态代理(Proxy 和 InvocationHandler)
  • 注解处理器(APT):在编译时处理注解(如Lombok)。
  • ASM 字节码操作:直接修改字节码实现高级功能。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表