引言

反射和注解是Java的高级特性,是Spring、MyBatis等框架的基础。理解反射和注解的原理,对于掌握框架源码和设计模式至关重要。

一、Java反射机制

1.1 什么是反射?

定义:反射是Java在运行时动态获取类信息、调用方法、操作属性的能力。

1
2
3
4
5
6
7
8
9
// 普通方式:编译时确定
Person person = new Person();
person.setName("Alice");

// 反射方式:运行时确定
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(obj, "Alice");

1.2 获取Class对象的三种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
// 方式1:通过类名.class
Class<Person> clazz1 = Person.class;

// 方式2:通过对象.getClass()
Person person = new Person();
Class<?> clazz2 = person.getClass();

// 方式3:通过Class.forName()
Class<?> clazz3 = Class.forName("com.example.Person");

// 三种方式获取的是同一个Class对象
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true

1.3 反射创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person {
private String name;
private int age;

// 无参构造
public Person() {}

// 有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}

// 方式1:使用无参构造
Class<?> clazz = Person.class;
Person p1 = (Person) clazz.getDeclaredConstructor().newInstance();

// 方式2:使用有参构造
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Person p2 = (Person) constructor.newInstance("Alice", 25);

1.4 反射访问字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FieldDemo {
private String privateField = "private";
public String publicField = "public";

public static void main(String[] args) throws Exception {
FieldDemo obj = new FieldDemo();
Class<?> clazz = obj.getClass();

// 获取public字段
Field publicField = clazz.getField("publicField");
System.out.println(publicField.get(obj)); // "public"

// 获取private字段(需要设置可访问)
Field privateField = clazz.getDeclaredField("privateField");
privateField.setAccessible(true); // 关键:破坏封装
System.out.println(privateField.get(obj)); // "private"

// 修改字段值
privateField.set(obj, "modified");
System.out.println(obj.privateField); // "modified"
}
}

1.5 反射调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MethodDemo {
private void privateMethod(String msg) {
System.out.println("Private: " + msg);
}

public String publicMethod(String msg) {
return "Public: " + msg;
}

public static void main(String[] args) throws Exception {
MethodDemo obj = new MethodDemo();
Class<?> clazz = obj.getClass();

// 调用public方法
Method publicMethod = clazz.getMethod("publicMethod", String.class);
String result = (String) publicMethod.invoke(obj, "Hello");
System.out.println(result); // "Public: Hello"

// 调用private方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod", String.class);
privateMethod.setAccessible(true);
privateMethod.invoke(obj, "World"); // "Private: World"
}
}

1.6 反射获取类信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class ClassInfoDemo {
public static void main(String[] args) {
Class<?> clazz = Person.class;

// 获取类名
System.out.println("类名:" + clazz.getName());
System.out.println("简单类名:" + clazz.getSimpleName());

// 获取所有public字段(包括父类)
Field[] fields = clazz.getFields();

// 获取所有声明的字段(不包括父类)
Field[] declaredFields = clazz.getDeclaredFields();

// 获取所有public方法(包括父类)
Method[] methods = clazz.getMethods();

// 获取所有声明的方法(不包括父类)
Method[] declaredMethods = clazz.getDeclaredMethods();

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

// 获取父类
Class<?> superclass = clazz.getSuperclass();

// 获取接口
Class<?>[] interfaces = clazz.getInterfaces();

// 获取注解
Annotation[] annotations = clazz.getAnnotations();
}
}

二、反射应用场景

2.1 JDBC数据库连接

1
2
3
4
5
6
// 反射加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");

// 等价于
// new com.mysql.cj.jdbc.Driver();
// 但反射方式可以通过配置文件指定驱动类名

2.2 Spring IOC容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Spring通过反射创建Bean
public class SimpleIOC {
private Map<String, Object> beans = new HashMap<>();

public void registerBean(String name, String className) throws Exception {
// 1. 通过反射加载类
Class<?> clazz = Class.forName(className);

// 2. 通过反射创建实例
Object bean = clazz.getDeclaredConstructor().newInstance();

// 3. 存储Bean
beans.put(name, bean);
}

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

// 使用
SimpleIOC ioc = new SimpleIOC();
ioc.registerBean("userService", "com.example.UserService");
Object userService = ioc.getBean("userService");

2.3 通用工具类:对象复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class BeanUtil {
/**
* 将source对象的属性复制到target对象
*/
public static void copyProperties(Object source, Object target) throws Exception {
Class<?> sourceClass = source.getClass();
Class<?> targetClass = target.getClass();

// 获取source的所有字段
Field[] fields = sourceClass.getDeclaredFields();

for (Field field : fields) {
field.setAccessible(true);

// 获取字段值
Object value = field.get(source);

// 设置到target
try {
Field targetField = targetClass.getDeclaredField(field.getName());
targetField.setAccessible(true);
targetField.set(target, value);
} catch (NoSuchFieldException e) {
// target没有对应字段,跳过
}
}
}
}

// 使用示例
PersonDTO dto = new PersonDTO();
PersonVO vo = new PersonVO();
BeanUtil.copyProperties(dto, vo);

三、Java注解

3.1 内置注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. @Override - 重写方法
@Override
public String toString() {
return "Person";
}

// 2. @Deprecated - 标记过时
@Deprecated
public void oldMethod() {
// 已过时的方法
}

// 3. @SuppressWarnings - 抑制警告
@SuppressWarnings("unchecked")
public void method() {
List list = new ArrayList(); // 不会有警告
}

// 4. @FunctionalInterface - 函数式接口
@FunctionalInterface
public interface MyFunction {
void run();
}

3.2 元注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1. @Target - 指定注解可以用在哪里
@Target(ElementType.METHOD) // 只能用在方法上
@Target({ElementType.TYPE, ElementType.METHOD}) // 可以用在类和方法上

// ElementType取值:
// TYPE - 类、接口、枚举
// FIELD - 字段
// METHOD - 方法
// PARAMETER - 参数
// CONSTRUCTOR - 构造器
// LOCAL_VARIABLE - 局部变量

// 2. @Retention - 指定注解保留时期
@Retention(RetentionPolicy.SOURCE) // 源码期
@Retention(RetentionPolicy.CLASS) // 编译期(默认)
@Retention(RetentionPolicy.RUNTIME) // 运行时(反射可见)

// 3. @Documented - 注解包含在JavaDoc中
@Documented

// 4. @Inherited - 子类继承父类的注解
@Inherited

// 5. @Repeatable - 可重复注解(JDK 8+)
@Repeatable(Roles.class)

3.3 自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default ""; // 默认属性
String name() default "default";
int age() default 0;
String[] tags() default {};
}

// 使用注解
public class User {
@MyAnnotation(value = "test", name = "Alice", age = 25)
public void method1() {}

@MyAnnotation("simple") // 只有value时可以省略"value="
public void method2() {}
}

3.4 解析注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class AnnotationParser {
public static void main(String[] args) throws Exception {
Class<?> clazz = User.class;

// 获取类上的注解
if (clazz.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation anno = clazz.getAnnotation(MyAnnotation.class);
System.out.println(anno.value());
}

// 获取方法上的注解
Method method = clazz.getMethod("method1");
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation anno = method.getAnnotation(MyAnnotation.class);
System.out.println("value: " + anno.value());
System.out.println("name: " + anno.name());
System.out.println("age: " + anno.age());
}

// 获取所有注解
Annotation[] annotations = method.getAnnotations();
for (Annotation anno : annotations) {
System.out.println(anno);
}
}
}

四、注解实战应用

4.1 自定义参数校验注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
String message() default "字段不能为空";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Length {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message() default "长度不符合要求";
}

// 使用注解
public class UserDTO {
@NotNull(message = "用户名不能为空")
@Length(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;

@NotNull(message = "密码不能为空")
@Length(min = 6, message = "密码至少6位")
private String password;

// getters and setters
}

// 校验器
public class Validator {
public static void validate(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();

for (Field field : fields) {
field.setAccessible(true);
Object value = field.get(obj);

// 校验@NotNull
if (field.isAnnotationPresent(NotNull.class)) {
if (value == null) {
NotNull anno = field.getAnnotation(NotNull.class);
throw new IllegalArgumentException(anno.message());
}
}

// 校验@Length
if (field.isAnnotationPresent(Length.class) && value instanceof String) {
Length anno = field.getAnnotation(Length.class);
String str = (String) value;
if (str.length() < anno.min() || str.length() > anno.max()) {
throw new IllegalArgumentException(anno.message());
}
}
}
}
}

// 使用
UserDTO user = new UserDTO();
user.setUsername("ab"); // 太短
Validator.validate(user); // 抛出异常:用户名长度必须在3-20之间

4.2 自定义权限注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String[] value(); // 需要的权限
}

// 使用注解
public class UserService {
@RequirePermission({"user:read", "user:list"})
public List<User> listUsers() {
// 查询用户列表
return new ArrayList<>();
}

@RequirePermission("user:delete")
public void deleteUser(Long id) {
// 删除用户
}
}

// 权限检查
public class PermissionChecker {
public static void checkPermission(Method method, Set<String> userPermissions) {
if (method.isAnnotationPresent(RequirePermission.class)) {
RequirePermission anno = method.getAnnotation(RequirePermission.class);
String[] requiredPermissions = anno.value();

// 检查用户是否拥有所需权限
for (String perm : requiredPermissions) {
if (!userPermissions.contains(perm)) {
throw new SecurityException("权限不足:需要" + perm);
}
}
}
}
}

4.3 自定义ORM框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value(); // 表名
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value(); // 列名
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}

// 实体类
@Table("t_user")
public class User {
@Id
@Column("id")
private Long id;

@Column("username")
private String username;

@Column("age")
private Integer age;

// getters and setters
}

// 简单ORM实现
public class SimpleORM {
public String generateInsertSQL(Object obj) throws Exception {
Class<?> clazz = obj.getClass();

// 获取表名
Table table = clazz.getAnnotation(Table.class);
String tableName = table.value();

// 获取字段和值
List<String> columns = new ArrayList<>();
List<Object> values = new ArrayList<>();

Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
columns.add(column.value());

field.setAccessible(true);
values.add(field.get(obj));
}
}

// 生成SQL
String sql = String.format("INSERT INTO %s (%s) VALUES (%s)",
tableName,
String.join(", ", columns),
values.stream().map(v -> "?").collect(Collectors.joining(", "))
);

return sql;
}
}

// 使用
User user = new User();
user.setId(1L);
user.setUsername("Alice");
user.setAge(25);

SimpleORM orm = new SimpleORM();
String sql = orm.generateInsertSQL(user);
// INSERT INTO t_user (id, username, age) VALUES (?, ?, ?)

五、动态代理

5.1 JDK动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 接口
public interface UserService {
void addUser(String name);
String getUser(Long id);
}

// 实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户:" + name);
}

@Override
public String getUser(Long id) {
return "User-" + id;
}
}

// 代理处理器
public class LogInvocationHandler implements InvocationHandler {
private Object target; // 被代理对象

public LogInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("调用方法:" + method.getName());
System.out.println("参数:" + Arrays.toString(args));

// 调用原方法
Object result = method.invoke(target, args);

// 后置增强
System.out.println("返回值:" + result);

return result;
}
}

// 使用动态代理
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogInvocationHandler(target)
);

proxy.addUser("Alice");
// 输出:
// 调用方法:addUser
// 参数:[Alice]
// 添加用户:Alice
// 返回值:null

5.2 CGLIB动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 不需要接口,直接代理类
public class UserService {
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}

// CGLIB代理
public class CglibProxy implements MethodInterceptor {
private Object target;

public Object getProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("调用方法:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("调用完成");
return result;
}
}

// 使用
UserService target = new UserService();
CglibProxy cglibProxy = new CglibProxy();
UserService proxy = (UserService) cglibProxy.getProxy(target);
proxy.addUser("Bob");

5.3 JDK代理 vs CGLIB代理

特性 JDK动态代理 CGLIB代理
要求 必须有接口 不需要接口
原理 实现接口 继承目标类
性能 较快 较慢(创建时)
final方法 可以代理 无法代理
Spring默认 有接口时使用 无接口时使用

六、反射的性能优化

6.1 缓存Class对象

1
2
3
4
5
6
7
8
9
10
// 不好的做法
for (int i = 0; i < 10000; i++) {
Class<?> clazz = Class.forName("com.example.User");
}

// 优化:缓存Class对象
Class<?> clazz = Class.forName("com.example.User");
for (int i = 0; i < 10000; i++) {
// 使用缓存的clazz
}

6.2 缓存Method对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MethodCache {
private static final Map<String, Method> cache = new ConcurrentHashMap<>();

public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
throws NoSuchMethodException {
String key = clazz.getName() + "#" + methodName;
return cache.computeIfAbsent(key, k -> {
try {
return clazz.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
}

6.3 使用MethodHandle(JDK 7+)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MethodHandle性能接近直接调用
public class MethodHandleDemo {
public static void main(String[] args) throws Throwable {
Person person = new Person();

// 获取MethodHandle
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(void.class, String.class);
MethodHandle mh = lookup.findVirtual(Person.class, "setName", mt);

// 调用
mh.invoke(person, "Alice");
}
}

七、面试高频问题

7.1 反射的优缺点?

优点:

  • 灵活性高,运行时动态操作
  • 框架开发的基础(Spring、MyBatis)
  • 可以访问私有成员

缺点:

  • 性能开销大
  • 破坏封装性
  • 编译时无法检查类型安全

7.2 如何提高反射性能?

  1. 缓存Class、Method、Field对象
  2. 使用setAccessible(true)减少安全检查
  3. 使用MethodHandle替代反射
  4. 批量操作减少反射调用次数

7.3 注解的原理?

注解本质是一个继承了Annotation接口的接口,JVM在加载类时会解析注解信息并存储在Class对象中,通过反射可以获取注解信息。

总结

反射和注解是Java的核心特性:

  1. 反射:动态获取类信息、创建对象、调用方法
  2. 注解:元数据标记,通过反射解析
  3. 应用:框架开发、AOP、ORM、参数校验
  4. 优化:缓存、MethodHandle、减少反射调用

参考资料