Java (Spring Boot) 反射完整学习笔记
目录
- 什么是反射(Reflection)
- 为什么需要反射
- [Java 反射核心 API](#Java 反射核心 API)
- 反射的高级应用
- [Spring Boot 中的反射应用](#Spring Boot 中的反射应用)
- 实战案例
- 性能与安全
- 常见误区与最佳实践
- 扩展阅读
1. 什么是反射(Reflection)
1.1 定义与核心概念
反射(Reflection) 是 Java 提供的一种机制,允许程序在运行时:
- 检查类的结构(类名、父类、接口、字段、方法、构造器)
- 创建对象实例
- 访问 和修改字段的值
- 调用方法
核心思想:将类的元数据(metadata)作为数据进行操作。
1.2 Java 反射的特点
| 特性 | 说明 |
|---|---|
| 运行时类型识别 | 在运行时获取类的完整信息 |
| 动态创建对象 | 无需 new 关键字即可创建实例 |
| 访问私有成员 | 通过 setAccessible(true) 突破访问控制 |
| 框架基础 | Spring、Hibernate、MyBatis 等框架的核心技术 |
1.3 反射 vs 正常调用
正常调用(编译时确定):
java
User user = new User();
user.setName("Alice");
String name = user.getName();
反射调用(运行时确定):
java
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.getDeclaredConstructor().newInstance();
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(user, "Alice");
Method getName = clazz.getMethod("getName");
String name = (String) getName.invoke(user);
对比:
| 维度 | 正常调用 | 反射调用 |
|---|---|---|
| 性能 | 快 | 慢(10-100 倍) |
| 类型安全 | 编译期检查 | 运行时检查 |
| 代码可读性 | 高 | 低 |
| 灵活性 | 低 | 高 |
2. 为什么需要反射
2.1 动态性的价值
反射使 Java 具备了运行时的动态能力:
java
// 场景:根据配置文件决定创建哪个类的实例
String className = config.getProperty("database.driver");
Class<?> clazz = Class.forName(className);
Driver driver = (Driver) clazz.getDeclaredConstructor().newInstance();
优势:
- 无需修改代码即可切换实现
- 支持插件化架构
- 框架可以在不知道具体类的情况下操作对象
2.2 典型应用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 框架开发 | IoC 容器、ORM 框架 | Spring、Hibernate |
| 动态代理 | AOP 切面编程 | Spring AOP、MyBatis |
| 注解处理 | 运行时解析注解 | @Autowired、@RequestMapping |
| 序列化/反序列化 | 对象 ↔ JSON/XML | Jackson、Gson |
| 工具类 | 对象拷贝、属性比较 | BeanUtils、MapStruct |
| 单元测试 | 访问私有方法 | JUnit、Mockito |
3. Java 反射核心 API
3.1 Class 类的获取
Class 类是反射的入口,代表一个类的元数据。
3.1.1 获取 Class 对象的三种方式
java
// 方式 1: 类名.class
Class<String> clazz1 = String.class;
// 方式 2: 对象.getClass()
String str = "Hello";
Class<? extends String> clazz2 = str.getClass();
// 方式 3: Class.forName("全限定类名")
Class<?> clazz3 = Class.forName("java.lang.String");
对比:
| 方式 | 使用场景 | 是否触发类初始化 |
|---|---|---|
类名.class |
编译时已知类型 | 否 |
对象.getClass() |
已有实例对象 | 是(对象已存在) |
Class.forName() |
运行时动态加载 | 是(默认) |
3.1.2 Class 类的常用方法
java
Class<?> clazz = User.class;
// 获取类的基本信息
String name = clazz.getName(); // 全限定类名:com.example.User
String simpleName = clazz.getSimpleName(); // 简单类名:User
Package pkg = clazz.getPackage(); // 包信息
// 获取修饰符
int modifiers = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);
// 获取父类和接口
Class<?> superClass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
// 判断类型
boolean isInterface = clazz.isInterface();
boolean isEnum = clazz.isEnum();
boolean isAnnotation = clazz.isAnnotation();
boolean isArray = clazz.isArray();
3.2 Constructor(构造器反射)
3.2.1 获取构造器
java
Class<?> clazz = User.class;
// 获取所有 public 构造器
Constructor<?>[] constructors = clazz.getConstructors();
// 获取所有构造器(包括 private)
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
// 获取指定参数的 public 构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 获取指定参数的构造器(包括 private)
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
3.2.2 创建对象实例
java
// 方式 1: 使用无参构造器
Class<?> clazz = User.class;
Object user1 = clazz.getDeclaredConstructor().newInstance();
// 方式 2: 使用有参构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user2 = constructor.newInstance("Alice", 25);
// 访问私有构造器
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
privateConstructor.setAccessible(true); // 关键:突破访问控制
Object user3 = privateConstructor.newInstance("Bob");
完整示例:
java
public class User {
private String name;
private int age;
// 私有构造器
private User(String name) {
this.name = name;
}
// 公共构造器
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
// 测试
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = User.class;
// 调用公共构造器
Constructor<?> publicConstructor = clazz.getConstructor(String.class, int.class);
Object user1 = publicConstructor.newInstance("Alice", 30);
System.out.println(user1); // User{name='Alice', age=30}
// 调用私有构造器
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
privateConstructor.setAccessible(true);
Object user2 = privateConstructor.newInstance("Bob");
System.out.println(user2); // User{name='Bob', age=0}
}
}
3.3 Field(字段反射)
3.3.1 获取字段
java
Class<?> clazz = User.class;
// 获取所有 public 字段(包括继承的)
Field[] fields = clazz.getFields();
// 获取所有字段(包括 private,不包括继承的)
Field[] allFields = clazz.getDeclaredFields();
// 获取指定名称的 public 字段
Field nameField = clazz.getField("name");
// 获取指定名称的字段(包括 private)
Field ageField = clazz.getDeclaredField("age");
3.3.2 读取和修改字段值
java
public class User {
public String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
// 测试
public class FieldTest {
public static void main(String[] args) throws Exception {
User user = new User("Alice", 25);
// 操作 public 字段
Field nameField = User.class.getField("name");
String name = (String) nameField.get(user);
System.out.println("原始 name: " + name); // Alice
nameField.set(user, "Bob");
System.out.println("修改后 name: " + user.name); // Bob
// 操作 private 字段
Field ageField = User.class.getDeclaredField("age");
ageField.setAccessible(true); // 关键:突破访问控制
int age = (int) ageField.get(user);
System.out.println("原始 age: " + age); // 25
ageField.set(user, 30);
System.out.println("修改后 age: " + ageField.get(user)); // 30
}
}
3.3.3 字段的其他信息
java
Field field = User.class.getDeclaredField("age");
// 字段名称
String fieldName = field.getName();
// 字段类型
Class<?> fieldType = field.getType();
System.out.println(fieldType); // int
// 泛型类型(如 List<String>)
Type genericType = field.getGenericType();
// 修饰符
int modifiers = field.getModifiers();
boolean isPrivate = Modifier.isPrivate(modifiers);
boolean isStatic = Modifier.isStatic(modifiers);
boolean isFinal = Modifier.isFinal(modifiers);
3.4 Method(方法反射)
3.4.1 获取方法
java
Class<?> clazz = User.class;
// 获取所有 public 方法(包括继承的)
Method[] methods = clazz.getMethods();
// 获取所有方法(包括 private,不包括继承的)
Method[] allMethods = clazz.getDeclaredMethods();
// 获取指定名称和参数的 public 方法
Method setName = clazz.getMethod("setName", String.class);
// 获取指定名称和参数的方法(包括 private)
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
3.4.2 调用方法
java
public class User {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
private String privateMethod() {
return "私有方法被调用";
}
public static String staticMethod(String msg) {
return "静态方法: " + msg;
}
}
// 测试
public class MethodTest {
public static void main(String[] args) throws Exception {
User user = new User();
// 调用 public 方法
Method setName = User.class.getMethod("setName", String.class);
setName.invoke(user, "Alice");
Method getName = User.class.getMethod("getName");
String name = (String) getName.invoke(user);
System.out.println("name: " + name); // Alice
// 调用 private 方法
Method privateMethod = User.class.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
String result = (String) privateMethod.invoke(user);
System.out.println(result); // 私有方法被调用
// 调用 static 方法
Method staticMethod = User.class.getMethod("staticMethod", String.class);
String staticResult = (String) staticMethod.invoke(null, "Hello");
System.out.println(staticResult); // 静态方法: Hello
}
}
3.4.3 方法的其他信息
java
Method method = User.class.getMethod("setName", String.class);
// 方法名称
String methodName = method.getName();
// 返回类型
Class<?> returnType = method.getReturnType();
// 参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
// 异常类型
Class<?>[] exceptionTypes = method.getExceptionTypes();
// 修饰符
int modifiers = method.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isStatic = Modifier.isStatic(modifiers);
3.5 Array(数组反射)
java
import java.lang.reflect.Array;
public class ArrayTest {
public static void main(String[] args) {
// 创建数组:int[10]
Object arr = Array.newInstance(int.class, 10);
// 设置数组元素
Array.set(arr, 0, 100);
Array.set(arr, 1, 200);
// 获取数组元素
int value = (int) Array.get(arr, 0);
System.out.println(value); // 100
// 获取数组长度
int length = Array.getLength(arr);
System.out.println(length); // 10
// 创建多维数组:int[5][10]
Object multiArr = Array.newInstance(int.class, 5, 10);
}
}
4. 反射的高级应用
4.1 动态代理
动态代理是 Java 反射的重要应用,分为 JDK 动态代理 和 CGLib 动态代理。
4.1.1 JDK 动态代理
特点:
- 基于接口实现
- 使用
java.lang.reflect.Proxy类 - 被代理类必须实现接口
示例:
java
// 接口
public interface UserService {
void addUser(String name);
void deleteUser(String name);
}
// 实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户: " + name);
}
@Override
public void deleteUser(String name) {
System.out.println("删除用户: " + name);
}
}
// 代理处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {
private Object target; // 被代理对象
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[日志] 方法开始: " + method.getName());
Object result = method.invoke(target, args); // 调用真实对象的方法
System.out.println("[日志] 方法结束: " + method.getName());
return result;
}
}
// 测试
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口列表
new LogHandler(target) // InvocationHandler
);
proxy.addUser("Alice");
// 输出:
// [日志] 方法开始: addUser
// 添加用户: Alice
// [日志] 方法结束: addUser
}
}
4.1.2 CGLib 动态代理
特点:
- 基于子类实现
- 使用字节码技术(ASM)
- 被代理类不需要实现接口
- 不能代理
final类和方法
Maven 依赖:
xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
示例:
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类(无需实现接口)
public class UserDao {
public void save(String name) {
System.out.println("保存用户: " + name);
}
}
// 拦截器
public class CglibInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("[CGLib] 方法开始: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用父类方法
System.out.println("[CGLib] 方法结束: " + method.getName());
return result;
}
}
// 测试
public class CglibTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDao.class); // 设置父类
enhancer.setCallback(new CglibInterceptor()); // 设置拦截器
UserDao proxy = (UserDao) enhancer.create();
proxy.save("Bob");
// 输出:
// [CGLib] 方法开始: save
// 保存用户: Bob
// [CGLib] 方法结束: save
}
}
4.1.3 JDK Proxy vs CGLib
| 对比维度 | JDK Proxy | CGLib |
|---|---|---|
| 实现方式 | 基于接口(反射) | 基于子类(字节码) |
| 要求 | 必须有接口 | 无需接口 |
| 性能 | 调用稍慢 | 调用稍快 |
| 限制 | 无 | 不能代理 final 类/方法 |
| Spring 默认 | 有接口时使用 | 无接口时使用 |
4.2 注解处理
4.2.1 定义注解
java
import java.lang.annotation.*;
@Target(ElementType.METHOD) // 作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时可获取
public @interface Log {
String value() default ""; // 注解参数
String level() default "INFO";
}
4.2.2 使用注解
java
public class UserController {
@Log(value = "用户登录", level = "INFO")
public void login(String username) {
System.out.println(username + " 登录成功");
}
@Log(value = "用户登出", level = "WARN")
public void logout(String username) {
System.out.println(username + " 已登出");
}
}
4.2.3 解析注解
java
import java.lang.reflect.Method;
public class AnnotationParser {
public static void main(String[] args) throws Exception {
Class<?> clazz = UserController.class;
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Log.class)) {
Log log = method.getAnnotation(Log.class);
System.out.println("方法: " + method.getName());
System.out.println("描述: " + log.value());
System.out.println("级别: " + log.level());
System.out.println("---");
}
}
}
}
// 输出:
// 方法: login
// 描述: 用户登录
// 级别: INFO
// ---
// 方法: logout
// 描述: 用户登出
// 级别: WARN
4.3 泛型信息获取
Java 的泛型在编译后会被擦除,但可以通过反射获取部分泛型信息。
java
import java.lang.reflect.*;
import java.util.*;
public class GenericTest {
// 字段的泛型信息
private List<String> stringList;
private Map<String, Integer> map;
// 方法的泛型信息
public List<String> getList(Map<String, Integer> param) {
return null;
}
public static void main(String[] args) throws Exception {
// 获取字段的泛型类型
Field field = GenericTest.class.getDeclaredField("stringList");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
System.out.println("原始类型: " + pt.getRawType()); // interface java.util.List
Type[] actualTypes = pt.getActualTypeArguments();
System.out.println("泛型参数: " + actualTypes[0]); // class java.lang.String
}
// 获取方法的泛型类型
Method method = GenericTest.class.getMethod("getList", Map.class);
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) returnType;
System.out.println("返回类型泛型参数: " + pt.getActualTypeArguments()[0]);
}
}
}
5. Spring Boot 中的反射应用
5.1 IoC 容器与依赖注入
5.1.1 Spring Bean 的创建过程
Spring 使用反射创建和管理 Bean:
java
// 伪代码:Spring 内部的 Bean 创建流程
public class SimpleBeanFactory {
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public Object getBean(String beanName, Class<?> beanClass) throws Exception {
// 检查单例缓存
if (singletonObjects.containsKey(beanName)) {
return singletonObjects.get(beanName);
}
// 使用反射创建实例
Object instance = beanClass.getDeclaredConstructor().newInstance();
// 依赖注入(字段注入)
for (Field field : beanClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
Object dependency = getBean(field.getName(), field.getType());
field.set(instance, dependency);
}
}
// 缓存单例 Bean
singletonObjects.put(beanName, instance);
return instance;
}
}
5.1.2 @Autowired 的实现原理
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // Spring 通过反射注入
public void saveUser(User user) {
userRepository.save(user);
}
}
原理:
- Spring 扫描所有类,找到带
@Service、@Component等注解的类 - 使用反射创建实例:
clazz.getDeclaredConstructor().newInstance() - 查找带
@Autowired的字段/方法 - 使用反射注入依赖:
field.set(instance, dependency)
5.2 AOP 切面编程
5.2.1 AOP 的底层实现
Spring AOP 基于动态代理实现:
- 有接口:使用 JDK 动态代理
- 无接口:使用 CGLib 动态代理
示例:
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Before("serviceLayer()")
public void beforeAdvice() {
System.out.println("[AOP] 方法执行前");
}
@Around("serviceLayer()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[AOP] 环绕通知 - 开始");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("[AOP] 环绕通知 - 结束");
return result;
}
}
原理:
- Spring 为带
@Service的类生成代理对象 - 代理对象的
InvocationHandler或MethodInterceptor拦截方法调用 - 在方法执行前后插入切面逻辑
5.3 自动配置原理
5.3.1 @SpringBootApplication 注解
java
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
等价于:
java
@SpringBootConfiguration // 标记为配置类
@EnableAutoConfiguration // 启用自动配置
@ComponentScan // 组件扫描
public class MyApplication {
// ...
}
5.3.2 自动配置的反射机制
核心流程:
@EnableAutoConfiguration触发自动配置- 读取
META-INF/spring.factories文件 - 加载所有自动配置类
- 使用反射创建 Bean
spring.factories 示例:
properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyAutoConfiguration
自动配置类:
java
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(MyService.class) // 条件:MyService 类存在
public class MyAutoConfiguration {
@Bean
public MyService myService() {
return new MyService();
}
}
反射实现:
java
// 伪代码:Spring Boot 自动配置加载
public class AutoConfigurationLoader {
public void loadAutoConfigurations() throws Exception {
// 读取 spring.factories
List<String> classNames = loadFactoryNames();
for (String className : classNames) {
// 使用反射加载类
Class<?> configClass = Class.forName(className);
// 检查条件注解
if (configClass.isAnnotationPresent(ConditionalOnClass.class)) {
ConditionalOnClass condition = configClass.getAnnotation(ConditionalOnClass.class);
if (!isClassPresent(condition.value())) {
continue; // 条件不满足,跳过
}
}
// 创建配置类实例并注册 Bean
Object configInstance = configClass.getDeclaredConstructor().newInstance();
registerBeans(configInstance);
}
}
}
5.4 注解驱动开发
5.4.1 @RequestMapping 的实现
java
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return new User(id, "Alice");
}
@PostMapping
public void createUser(@RequestBody User user) {
System.out.println("创建用户: " + user);
}
}
原理:
- Spring 扫描所有类,找到
@RestController注解的类 - 遍历类的所有方法,查找
@RequestMapping、@GetMapping等注解 - 解析注解的路径、HTTP 方法等信息
- 使用反射调用对应的方法
简化实现:
java
import java.lang.reflect.Method;
import java.util.*;
public class SimpleDispatcher {
private Map<String, HandlerMethod> handlerMethods = new HashMap<>();
public void registerController(Class<?> controllerClass) throws Exception {
RequestMapping classMapping = controllerClass.getAnnotation(RequestMapping.class);
String basePath = classMapping != null ? classMapping.value()[0] : "";
for (Method method : controllerClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(GetMapping.class)) {
GetMapping mapping = method.getAnnotation(GetMapping.class);
String fullPath = basePath + mapping.value()[0];
handlerMethods.put(fullPath, new HandlerMethod(controllerClass, method));
}
}
}
public Object handleRequest(String path, Object... args) throws Exception {
HandlerMethod handler = handlerMethods.get(path);
if (handler == null) {
throw new IllegalArgumentException("No handler for path: " + path);
}
Object controller = handler.controllerClass.getDeclaredConstructor().newInstance();
return handler.method.invoke(controller, args);
}
static class HandlerMethod {
Class<?> controllerClass;
Method method;
HandlerMethod(Class<?> controllerClass, Method method) {
this.controllerClass = controllerClass;
this.method = method;
}
}
}
5.5 Bean 生命周期中的反射
Spring Bean 的完整生命周期:
1. 实例化(Instantiation) ← 反射创建对象
↓
2. 属性赋值(Populate Properties) ← 反射注入依赖
↓
3. 初始化前(BeanPostProcessor.before)
↓
4. 初始化(InitializingBean / @PostConstruct)
↓
5. 初始化后(BeanPostProcessor.after) ← AOP 代理创建
↓
6. 使用
↓
7. 销毁(DisposableBean / @PreDestroy)
代码示例:
java
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.Component;
import javax.annotation.*;
@Component
public class LifecycleBean {
@Autowired
private UserService userService; // 2. 反射注入
public LifecycleBean() {
System.out.println("1. 构造器执行"); // 1. 反射调用
}
@PostConstruct
public void init() {
System.out.println("4. 初始化方法执行");
}
@PreDestroy
public void destroy() {
System.out.println("7. 销毁方法执行");
}
}
6. 实战案例
6.1 自定义注解 + 反射处理
需求:实现一个日志注解,自动记录方法的执行时间。
6.1.1 定义注解
java
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeLog {
String value() default "";
}
6.1.2 AOP 切面处理
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class TimeLogAspect {
@Around("@annotation(com.example.annotation.TimeLog)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 使用反射获取方法信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
TimeLog timeLog = method.getAnnotation(TimeLog.class);
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标方法
long endTime = System.currentTimeMillis();
System.out.println(String.format("[%s] %s 执行时间: %dms",
timeLog.value(), method.getName(), endTime - startTime));
return result;
}
}
6.1.3 使用注解
java
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@TimeLog("订单服务")
public void processOrder(Long orderId) throws InterruptedException {
Thread.sleep(500); // 模拟耗时操作
System.out.println("处理订单: " + orderId);
}
}
6.2 动态 Bean 注册
需求:根据配置文件动态注册不同的数据源 Bean。
java
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
public class DynamicBeanRegistrar {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 获取 Bean 定义注册器
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
// 动态创建 Bean 定义
String dataSourceClass = "com.zaxxer.hikari.HikariDataSource"; // 从配置读取
Class<?> clazz = Class.forName(dataSourceClass);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
builder.addPropertyValue("jdbcUrl", "jdbc:mysql://localhost:3306/test");
builder.addPropertyValue("username", "root");
builder.addPropertyValue("password", "123456");
// 注册 Bean
registry.registerBeanDefinition("dynamicDataSource", builder.getBeanDefinition());
context.refresh();
// 获取动态注册的 Bean
DataSource dataSource = context.getBean("dynamicDataSource", DataSource.class);
System.out.println("动态 Bean: " + dataSource);
}
}
6.3 简易 ORM 框架
需求:通过反射将对象映射到 SQL。
java
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.*;
// 表名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
String value();
}
// 列名注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column {
String value();
}
// 实体类
@Table("t_user")
class User {
@Column("id")
private Long id;
@Column("username")
private String username;
@Column("email")
private String email;
public User(Long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
// Getters...
}
// 简易 ORM
class SimpleORM {
public static String generateInsertSQL(Object entity) throws Exception {
Class<?> clazz = entity.getClass();
// 获取表名
Table table = clazz.getAnnotation(Table.class);
String tableName = table.value();
// 获取字段和值
List<String> columns = new ArrayList<>();
List<String> values = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
columns.add(column.value());
field.setAccessible(true);
Object value = field.get(entity);
values.add(value instanceof String ? "'" + value + "'" : String.valueOf(value));
}
}
return String.format("INSERT INTO %s (%s) VALUES (%s)",
tableName,
String.join(", ", columns),
String.join(", ", values));
}
public static void main(String[] args) throws Exception {
User user = new User(1L, "alice", "alice@example.com");
String sql = generateInsertSQL(user);
System.out.println(sql);
// INSERT INTO t_user (id, username, email) VALUES (1, 'alice', 'alice@example.com')
}
}
6.4 自定义 AOP 切面(无 Spring)
需求:不使用 Spring,纯手工实现 AOP。
java
import java.lang.reflect.*;
// 切面接口
interface MethodInterceptor {
void before(Method method, Object[] args);
void after(Method method, Object result);
}
// 代理工厂
class ProxyFactory {
public static <T> T createProxy(T target, MethodInterceptor interceptor) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
interceptor.before(method, args);
Object result = method.invoke(target, args);
interceptor.after(method, result);
return result;
}
);
}
}
// 测试
interface Calculator {
int add(int a, int b);
}
class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
}
public class AopTest {
public static void main(String[] args) {
Calculator calculator = new CalculatorImpl();
Calculator proxy = ProxyFactory.createProxy(calculator, new MethodInterceptor() {
@Override
public void before(Method method, Object[] args) {
System.out.println("[AOP] 执行方法: " + method.getName());
}
@Override
public void after(Method method, Object result) {
System.out.println("[AOP] 返回结果: " + result);
}
});
int result = proxy.add(10, 20);
System.out.println("最终结果: " + result);
// 输出:
// [AOP] 执行方法: add
// [AOP] 返回结果: 30
// 最终结果: 30
}
}
7. 性能与安全
7.1 反射的性能开销
7.1.1 性能测试
java
public class ReflectionPerformanceTest {
static class User {
private String name;
public void setName(String name) { this.name = name; }
public String getName() { return name; }
}
public static void main(String[] args) throws Exception {
User user = new User();
Method setName = User.class.getMethod("setName", String.class);
int iterations = 10_000_000;
// 1. 直接调用
long start1 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
user.setName("Alice");
}
long time1 = System.currentTimeMillis() - start1;
// 2. 反射调用(未优化)
long start2 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
setName.invoke(user, "Alice");
}
long time2 = System.currentTimeMillis() - start2;
// 3. 反射调用(关闭访问检查)
setName.setAccessible(true);
long start3 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
setName.invoke(user, "Alice");
}
long time3 = System.currentTimeMillis() - start3;
System.out.println("直接调用: " + time1 + "ms");
System.out.println("反射调用(未优化): " + time2 + "ms (慢 " + (time2 / time1) + "x)");
System.out.println("反射调用(setAccessible): " + time3 + "ms (慢 " + (time3 / time1) + "x)");
}
}
// 典型输出:
// 直接调用: 5ms
// 反射调用(未优化): 150ms (慢 30x)
// 反射调用(setAccessible): 80ms (慢 16x)
7.1.2 性能优化方案
| 优化方案 | 说明 | 效果 |
|---|---|---|
| 缓存反射对象 | 缓存 Method、Field 对象 |
避免重复查找 |
| setAccessible(true) | 关闭访问检查 | 提升 50% 性能 |
| 使用 MethodHandle | Java 7+ 的高性能 API | 接近直接调用 |
| 编译优化 | JIT 编译器优化 | 长期运行后性能改善 |
缓存示例:
java
public class ReflectionCache {
private static final Map<String, Method> methodCache = new ConcurrentHashMap<>();
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
throws NoSuchMethodException {
String key = clazz.getName() + "#" + methodName;
return methodCache.computeIfAbsent(key, k -> {
try {
Method method = clazz.getMethod(methodName, paramTypes);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
}
7.2 setAccessible 与安全管理器
7.2.1 访问私有成员
java
public class SecurityTest {
private String secret = "敏感数据";
public static void main(String[] args) throws Exception {
SecurityTest obj = new SecurityTest();
Field field = SecurityTest.class.getDeclaredField("secret");
// field.get(obj); // IllegalAccessException
field.setAccessible(true); // 绕过访问控制
String value = (String) field.get(obj);
System.out.println("私有字段: " + value); // 敏感数据
}
}
7.2.2 安全管理器限制
Java 9+ 开始限制反射访问内部 API:
java
// Java 9+ 报警告
Field field = String.class.getDeclaredField("value");
field.setAccessible(true); // 警告: Illegal reflective access
解决方案:
- 使用
--add-opensJVM 参数 - 使用模块系统的
opens声明
7.3 反射的安全风险
7.3.1 反序列化攻击
java
// 危险代码示例
public class DeserializeAttack {
public static void main(String[] args) throws Exception {
String maliciousClassName = "java.lang.Runtime"; // 恶意类名
Class<?> clazz = Class.forName(maliciousClassName);
Method exec = clazz.getMethod("exec", String.class);
Object runtime = clazz.getMethod("getRuntime").invoke(null);
exec.invoke(runtime, "rm -rf /"); // 危险操作!
}
}
防御措施:
- 白名单机制:只允许加载特定的类
- 禁用危险类 :如
Runtime、ProcessBuilder - 使用安全框架:如 Apache Commons Collections 的安全版本
8. 常见误区与最佳实践
8.1 何时使用/不使用反射
✅ 适合使用反射的场景
| 场景 | 原因 |
|---|---|
| 框架开发 | 需要操作未知类型的对象 |
| 插件系统 | 动态加载第三方代码 |
| ORM 框架 | 对象与数据库映射 |
| 序列化 | 通用的对象转换 |
| 单元测试 | 访问私有方法进行测试 |
❌ 不应使用反射的场景
| 场景 | 原因 | 替代方案 |
|---|---|---|
| 性能关键路径 | 反射慢 10-100 倍 | 直接调用 |
| 类型固定 | 增加复杂度 | 硬编码 |
| 简单逻辑 | 过度设计 | if-else |
| 安全敏感 | 可能被攻击 | 白名单 + 验证 |
8.2 Spring 中的最佳实践
8.2.1 依赖注入方式选择
| 方式 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| 构造器注入 | 不可变、易测试 | 构造器参数多时冗长 | ⭐⭐⭐⭐⭐ |
| Setter 注入 | 灵活 | 可能为 null | ⭐⭐⭐ |
| 字段注入 | 简洁 | 难以测试、耦合框架 | ⭐⭐ |
推荐:构造器注入
java
@Service
public class UserService {
private final UserRepository userRepository;
// 推荐:构造器注入
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
8.2.2 避免循环依赖
java
// 错误示例
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
class ServiceB {
@Autowired
private ServiceA serviceA; // 循环依赖
}
解决方案:
- 重新设计类结构
- 使用
@Lazy注解 - 使用 Setter 注入
8.3 反射代码的可维护性
错误示例
java
// ❌ 硬编码字符串,容易拼写错误
Method method = clazz.getMethod("getName");
正确示例
java
// ✅ 使用常量
public interface MethodNames {
String GET_NAME = "getName";
}
Method method = clazz.getMethod(MethodNames.GET_NAME);
9. 扩展阅读
9.1 相关主题
- Java Agent:字节码增强技术(Instrumentation API)
- ASM / Javassist:字节码操作框架
- Spring 源码分析 :
BeanFactory、ApplicationContext实现 - 设计模式:工厂模式、代理模式、装饰器模式
9.2 进阶学习资源
官方文档
书籍推荐
- 《深入理解 Java 虚拟机》(周志明)
- 《Spring 源码深度解析》(郝佳)
- 《Effective Java》(Joshua Bloch)
源码阅读
java.lang.reflect包- Spring
org.springframework.beans.factory包 - MyBatis
org.apache.ibatis.reflection包
9.3 实战项目
- 自定义 IoC 容器:实现依赖注入功能
- 简易 AOP 框架:基于动态代理
- ORM 框架:对象与数据库映射
- JSON 序列化库:通过反射实现
总结
核心要点
- 反射是什么:运行时操作类、对象、方法、字段的能力
- 核心 API :
Class、Constructor、Field、Method - 高级应用:动态代理(JDK/CGLib)、注解处理、泛型信息
- Spring 应用:IoC、AOP、自动配置、注解驱动
- 注意事项:性能开销、安全风险、可维护性
学习检查清单
- 理解反射的基本概念和应用场景
- 掌握
Class、Constructor、Field、Method的使用 - 理解 JDK Proxy 和 CGLib 的区别
- 了解 Spring IoC 和 AOP 的反射实现
- 能够实现自定义注解 + 反射处理
- 知道反射的性能和安全问题
- 明白何时使用/不使用反射
实践建议
- 动手实践:实现一个简单的 IoC 容器
- 阅读源码 :研究 Spring
AnnotationConfigApplicationContext - 性能测试:对比反射与直接调用的性能差异
- 安全审计 :检查项目中是否存在
setAccessible滥用
附录:快速参考表
反射 API 速查
java
// 获取 Class 对象
Class<?> clazz = String.class;
Class<?> clazz = obj.getClass();
Class<?> clazz = Class.forName("java.lang.String");
// 创建对象
Object obj = clazz.getDeclaredConstructor().newInstance();
Object obj = constructor.newInstance(args);
// 获取字段
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
Object value = field.get(obj);
field.set(obj, newValue);
// 获取方法
Method method = clazz.getMethod("setName", String.class);
method.setAccessible(true);
Object result = method.invoke(obj, args);
// 动态代理
Object proxy = Proxy.newProxyInstance(
classLoader, interfaces, invocationHandler
);
Spring 注解速查
java
// Bean 定义
@Component / @Service / @Repository / @Controller
// 依赖注入
@Autowired / @Resource / @Inject
// 配置
@Configuration / @Bean / @ComponentScan
// AOP
@Aspect / @Pointcut / @Before / @After / @Around
// Web
@RestController / @RequestMapping / @GetMapping / @PostMapping
// 自动配置
@SpringBootApplication / @EnableAutoConfiguration
@ConditionalOnClass / @ConditionalOnBean