反射(Reflection)与设计模式的结合,是 Java 高级开发中的核心技能,它通过在运行时动态操作类、方法和属性,为设计模式提供了更灵活、强大的实现方式。
总体而言,反射主要通过以下几种方式应用于设计模式:动态实例化解除代码耦合,动态调用实现行为与业务分离,动态代理支持如 AOP 等高级功能,以及运行时信息获取支持框架开发。它在提升扩展性(符合"开闭原则")的同时,也需要权衡相应的性能开销和维护成本。
下面我们来具体看看它在几种经典设计模式中的应用。
🏭 与工厂模式的结合:实现"超级工厂"
传统的静态工厂模式每增加一个产品,都需要修改工厂类。引入反射后,可以通过类名(字符串)进行实例化,实现零修改的"通用工厂"。
```java
// 通用反射工厂
class Factory {
private Factory() {}
@SuppressWarnings("unchecked")
public static <T> T getInstance(String className) {
try {
// 根据类名字符串创建实例,无需修改工厂代码
return (T) Class.forName(className).newInstance();
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}
// 客户端可以动态创建任意类型的对象,极大地提高了灵活性
IMessage msg = Factory.getInstance("refelect.CloudMessage");
```
此外,还可以配合配置文件实现数据库切换等"零修改"扩展。
🎭 与代理模式的结合:实现动态代理
动态代理是 AOP 的基石,它无需为每个类单独创建代理类,能够在运行时统一添加日志、事务等横切逻辑。
JDK 动态代理需要被代理类实现接口。核心是 InvocationHandler,在 invoke 方法中编写增强逻辑。
```java
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("方法返回: " + result); // 后置增强
return result;
}
}
// 客户端使用
Calculator proxy = (Calculator) Proxy.newProxyInstance(realObj.getClass().getClassLoader(),
realObj.getClass().getInterfaces(), new LogHandler(realObj));
proxy.add(3, 5); // 输出日志后,实际执行并返回8reference:8
```
若类未实现接口,可使用 CGLib 等基于字节码技术的动态代理。
🧠 与策略模式的结合:消灭"if-else"
利用反射工厂维护策略映射关系,可以彻底消除条件判断逻辑,让添加新策略时无需修改业务代码。
```java
// 1. 定义策略接口
public interface Strategy { void algorithm(); }
// 2. 编写具体策略实现:StrategyA, StrategyB...
// 3. 创建反射工厂,维护策略映射(可通过扫描注解实现自动化)
public class StrategyFactory {
private static Map<String, Class<?>> strategyMap = new HashMap<>();
static {
strategyMap.put("A", StrategyA.class); // 可通过反射扫描注解
}
public static Strategy getStrategy(String type) throws Exception {
Class<?> clazz = strategyMap.get(type);
return (Strategy) clazz.newInstance();
}
}
// 客户端使用时,避免了复杂判断,直接调用工厂即可reference:12
Strategy strategy = StrategyFactory.getStrategy(userInput); // 无switch判断
strategy.algorithm();
```
🧱 与单例模式的"攻防战"
反射可以破坏单例模式,因此需要通过防御性编码。
· 攻击:利用 setAccessible(true) 绕过私有构造器。
· 防御:在私有构造器中添加标志位校验。
```java
private Singleton() {
if (instance != null) { // 防止反射调用
throw new RuntimeException("单例模式被破坏");
}
}
```
但最安全的防御方式是使用枚举实现单例。
💉 与依赖注入(IoC)的结合
依赖注入(Dependency Injection)的本质是一种设计思想,而反射是实现它的关键底层技术,与 Spring 等具体框架无关。框架读取配置(XML/注解),通过反射解析类的构造器、方法或属性,并动态注入依赖对象。这几乎是所有 IoC 容器的标准实现方式。
📚 经典例题与延伸思考
例题1:反射工厂处理不同的数据类型
题目:请用反射工厂设计一个系统,能动态处理 Apple、Pear、Oranges 等水果的创建和价格计算。
例题2:JDK动态代理拦截与日志记录
题目:为 Calculator 接口的 add(int a, int b) 方法添加动态代理,打印入参和结果。(上文动态代理小节即为例题解答)
延伸:反射的应用与性能
· 应用场景:许多主流 Java 框架都重度依赖反射,如 Spring(IoC/AOP 容器)、MyBatis(结果映射)、JUnit(测试方法的发现与执行)等。
· 性能考量:反射涉及动态类型检查和方法查找,性能低于直接调用。若需在关键路径上高频使用,可考虑缓存 Class 或 Method 对象进行优化。
💎 总结
反射赋予了经典设计模式全新的活力。它通过"运行时动态性"解决了编译时的静态耦合问题,是实现大多数 Java 框架优雅设计的基石,也是构建高扩展性应用的必备技能。
最后想提醒一下,反射是一把"双刃剑"。在享受它带来的灵活性的同时,也要注意其带来的性能开销、安全风险(可访问私有成员)以及代码可读性下降的问题。建议在构建框架、通用工具库或处理动态需求时使用,而在普通业务逻辑中则应谨慎。