反射与动态代理详解
反射是 Java 框架的基石,理解反射和动态代理对掌握 Spring 等框架至关重要。
一、反射机制
1.1 什么是反射
反射 = 运行时获取类信息 + 动态操作对象
运行时获取:
- 类的属性、方法、构造函数
- 注解、泛型信息
运行时操作:
- 创建对象
- 调用方法
- 访问属性
1.2 获取 Class 对象
// 方式 1:Class.forName()
Class<?> clazz = Class.forName("com.example.User");
// 方式 2:类名.class
Class<User> clazz = User.class;
// 方式 3:对象.getClass()
User user = new User();
Class<?> clazz = user.getClass();
// 方式 4:类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> clazz = loader.loadClass("com.example.User");
二、反射核心 API
2.1 创建对象
Class<?> clazz = Class.forName("com.example.User");
// 方式 1:无参构造
User user1 = (User) clazz.newInstance(); // 已废弃
// 方式 2:指定构造器
Constructor<User> constructor = clazz.getDeclaredConstructor();
User user2 = constructor.newInstance();
// 方式 3:有参构造
Constructor<User> constructor = clazz.getDeclaredConstructor(String.class, int.class);
User user3 = constructor.newInstance("Alice", 20);
2.2 访问属性
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance();
// 获取公共属性
Field nameField = clazz.getField("name");
// 获取任意属性(包括私有)
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 突破访问限制
// 设置/获取值
ageField.set(user, 20);
int age = (int) ageField.get(user);
// 获取所有属性
Field[] fields = clazz.getDeclaredFields();
2.3 调用方法
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance();
// 获取公共方法
Method getNameMethod = clazz.getMethod("getName");
// 获取任意方法(包括私有)
Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
setNameMethod.setAccessible(true);
// 调用方法
setNameMethod.invoke(user, "Alice");
String name = (String) getNameMethod.invoke(user);
// 调用静态方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null); // 静态方法传入 null
三、反射应用场景
3.1 工厂模式
public class BeanFactory {
public static <T> T createBean(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 使用
UserService service = BeanFactory.createBean(UserService.class);
3.2 依赖注入
public @interface Autowired {}
public class SpringContainer {
private Map<String, Object> beans = new HashMap<>();
public void register(Class<?> clazz) {
try {
Object bean = clazz.getDeclaredConstructor().newInstance();
// 注入依赖
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
Object dependency = beans.get(field.getType().getName());
field.set(bean, dependency);
}
}
beans.put(clazz.getName(), bean);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3.3 动态配置
public class ConfigLoader {
public static <T> T loadConfig(Class<T> clazz, Properties props) {
try {
T config = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
String key = field.getName();
if (props.containsKey(key)) {
field.setAccessible(true);
String value = props.getProperty(key);
// 类型转换
if (field.getType() == int.class) {
field.set(config, Integer.parseInt(value));
} else if (field.getType() == boolean.class) {
field.set(config, Boolean.parseBoolean(value));
} else {
field.set(config, value);
}
}
}
return config;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
四、动态代理
4.1 什么是动态代理
动态代理 = 运行时创建代理类
优势:
- 无需手动编写代理类
- 统一处理横切逻辑
- AOP 实现基础
4.2 JDK 动态代理
// 接口
interface UserService {
void addUser(String name);
}
// 目标对象
class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("Adding user: " + name);
}
}
// 创建代理
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxyObj, method, args) -> {
// 前置处理
System.out.println("Before: " + method.getName());
// 调用目标方法
Object result = method.invoke(target, args);
// 后置处理
System.out.println("After: " + method.getName());
return result;
}
);
// 使用代理
proxy.addUser("Alice");
4.3 代理工厂
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, InvocationHandler handler) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
// 事务代理
public static <T> T createTransactionProxy(T target, DataSource dataSource) {
return createProxy(target, (proxy, method, args) -> {
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
Object result = method.invoke(target, args);
conn.commit();
return result;
} catch (Exception e) {
conn.rollback();
throw e;
} finally {
conn.close();
}
});
}
// 日志代理
public static <T> T createLoggingProxy(T target) {
return createProxy(target, (proxy, method, args) -> {
System.out.println("Calling: " + method.getName());
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long cost = System.currentTimeMillis() - start;
System.out.println("Cost: " + cost + "ms");
return result;
});
}
}
五、CGLIB 动态代理
5.1 为什么需要 CGLIB
JDK 动态代理限制:
- 只能代理接口
- 不能代理类
CGLIB 优势:
- 可以代理类
- 通过继承实现
5.2 CGLIB 使用
// 添加依赖
// <dependency>
// <groupId>cglib</groupId>
// <artifactId>cglib</artifactId>
// <version>3.3.0</version>
// </dependency>
// 目标类(无接口)
class UserService {
public void addUser(String name) {
System.out.println("Adding user: " + name);
}
}
// 创建代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("Before: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After: " + method.getName());
return result;
});
UserService proxy = (UserService) enhancer.create();
proxy.addUser("Alice");
5.3 JDK vs CGLIB
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 实现方式 | 接口 | 继承 |
| 代理对象 | 接口实现类 | 任意类 |
| 性能 | 高 | 稍低 |
| 依赖 | JDK 自带 | 需要第三方库 |
| Spring 选择 | 有接口用 JDK | 无接口用 CGLIB |
六、Spring AOP 原理
6.1 AOP 核心概念
Aspect(切面) = 横切关注点的模块化
Pointcut(切入点) = 匹配连接点的谓词
Advice(通知) = 切面在特定执行的动作
Joinpoint(连接点) = 程序执行的点(方法调用)
Weaving(织入) = 将切面应用到目标对象
6.2 Spring AOP 实现
// 切面定义
@Aspect
@Component
public class LoggingAspect {
// 切入点
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void before(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature());
}
// 后置通知
@After("serviceMethods()")
public void after(JoinPoint joinPoint) {
System.out.println("After: " + joinPoint.getSignature());
}
// 环绕通知
@Around("serviceMethods()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long cost = System.currentTimeMillis() - start;
System.out.println("Cost: " + cost + "ms");
return result;
}
}
6.3 Spring 代理选择
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// 默认:有接口用 JDK,无接口用 CGLIB
@Bean
public UserService userService() {
return new UserServiceImpl();
}
// 强制使用 CGLIB
@Bean
public ProxyFactoryBean proxyFactoryBean() {
ProxyFactoryBean factory = new ProxyFactoryBean();
factory.setProxyTargetClass(true); // 强制 CGLIB
return factory;
}
}
七、反射性能优化
7.1 缓存 Class 对象
// ❌ 每次都获取
Class<?> clazz = Class.forName("com.example.User");
// ✅ 缓存
private static final Class<?> USER_CLASS = User.class;
7.2 缓存 Method/Field
// ❌ 每次都查找
Method method = clazz.getMethod("getName");
// ✅ 缓存
private static final Method GET_NAME_METHOD;
static {
try {
GET_NAME_METHOD = User.class.getMethod("getName");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
7.3 关闭访问检查
// 访问私有方法/属性
Method method = clazz.getDeclaredMethod("privateMethod");
method.setAccessible(true); // 关闭检查,提升性能
// 使用后恢复
method.setAccessible(false);
八、总结
反射与代理核心要点:
| 主题 | 核心 API | 应用场景 |
|---|---|---|
| 反射 | Class/Method/Field | 工厂、DI、配置 |
| JDK 代理 | Proxy/InvocationHandler | 接口代理、AOP |
| CGLIB | Enhancer/MethodInterceptor | 类代理、Spring AOP |
反射是框架的基石,动态代理是 AOP 的基础,理解它们对掌握 Spring 至关重要。