Java 反射与注解详解
一、反射(Reflection)
反射允许程序在运行时动态获取类的信息(如类名、方法、字段、构造器等),并操作类或对象(如创建实例、调用方法、访问字段)。它是实现框架(如Spring、MyBatis)和动态代理的核心技术。
1. 反射核心 API
1.1 获取 Class 对象
// 方式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());
}
}
}
}
四、注意事项
- 反射的性能
- 反射操作比直接调用慢,频繁调用需缓存 Method、Field 对象。
- 避免在性能敏感场景过度使用反射。
- 安全性
- 反射可以绕过访问权限(setAccessible(true)),需谨慎使用。
- 安全管理器(SecurityManager)可限制反射操作。
- 注解的保留策略
- 若注解的 @Retention 设置为 SOURCE 或 CLASS,则运行时无法通过反射读取。
总结
- 反射是动态操作类的工具,适用于框架、动态代理等场景,但需注意性能和安全。
- 注解为代码添加元数据,结合反射可实现灵活的逻辑控制(如依赖注入、ORM映射)。
深入学习方向:
- 动态代理(Proxy 和 InvocationHandler)
- 注解处理器(APT):在编译时处理注解(如Lombok)。
- ASM 字节码操作:直接修改字节码实现高级功能。