反射与设计模式1

  1. 工厂模式 + 反射:从"简单工厂"到"反射工厂族"

基础版回顾:工厂类根据类名字符串创建对象,新增产品零修改。

详细增强点:

· 带参数的构造函数:使用 Constructor 对象创建实例。

· 配置驱动:配合 .properties 或 XML 文件,实现"零代码"增删产品。

完整代码示例:可传参的反射工厂

```java

public class ReflectionFactory {

// 根据类名和参数类型/值动态创建对象

public static Object getInstance(String className, Class<?>\[\] paramTypes, Object\[\] params) {

try {

Class<?> clazz = Class.forName(className);

// 获取匹配的构造函数

Constructor<?> ctor = clazz.getDeclaredConstructor(paramTypes);

ctor.setAccessible(true); // 即便是私有构造函数也可调用

return ctor.newInstance(params);

} catch (Exception e) {

throw new RuntimeException("反射创建对象失败:" + className, e);

}

}

}

// 使用:创建Product(String name, double price)实例

Product p = (Product) ReflectionFactory.getInstance(

"com.demo.Product",

new Class\[\]{String.class, double.class},

new Object\[\]{"苹果", 5.5}

);

```

工厂+配置文件的典型模式(框架雏形)

```properties

application.properties

payment.alipay=com.demo.AlipayService

payment.wechat=com.demo.WechatService

```

```java

// 读取配置,动态创建支付服务对象

String className = properties.getProperty("payment." + userChoice);

IPaymentService service = (IPaymentService) ReflectionFactory.getInstance(className, null, null);

service.pay(amount);

```

此时,新增支付方式(如 PaypalService)仅需:① 编写类实现接口;② 在配置文件中加一行;③ 完全不用修改Java代码。这就是"对扩展开放,对修改封闭"。


  1. 策略模式 + 反射:消灭硬编码分支的终极方案

核心思想:用映射表(Map<String, Class<?>>)替代 if-else 或 switch。

进阶实现:自动扫描注解,消除手动注册

  1. 自定义一个注解 @StrategyType

```java

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface StrategyType {

String value(); // 策略标识,如 "A", "B"

}

```

  1. 在策略实现类上标注

```java

@StrategyType("A")

public class ConcreteStrategyA implements Strategy { ... }

```

  1. 工厂自动扫描(需传入包名,或者遍历类路径)

```java

public class StrategyFactory {

private static Map<String, Class<?>> strategyMap = new HashMap<>();

static {

// 伪代码:扫描指定包下所有带 @StrategyType 注解的类

Set<Class<?>> classes = scanPackage("com.demo.strategies");

for (Class<?> clazz : classes) {

StrategyType annotation = clazz.getAnnotation(StrategyType.class);

if (annotation != null) {

strategyMap.put(annotation.value(), clazz);

}

}

}

public static Strategy getStrategy(String type) {

Class<?> clazz = strategyMap.get(type);

if (clazz == null) throw new IllegalArgumentException("未知策略类型");

return (Strategy) clazz.getDeclaredConstructor().newInstance();

}

}

```

优势:新增一个策略类,写上 @StrategyType 即可,工厂自动纳入管理,零改动核心逻辑。


  1. JDK动态代理的底层实现细节

代理执行流程:

  1. Proxy.newProxyInstance() 动态生成一个字节码类($Proxy0)实现了指定接口。

  2. 该代理类内部持有 InvocationHandler 引用。

  3. 调用代理对象的 add(3,5) 时 → 代理类调用 handler.invoke(...)。

  4. invoke 内部通过 method.invoke(target, args) 反射调用真实对象的方法。

关键源码模拟(生成的代理类逻辑):

```java

public final class $Proxy0 extends Proxy implements Calculator {

private static Method m3; // add 方法

static {

m3 = Class.forName("Calculator").getMethod("add", int.class, int.class);

}

public int add(int a, int b) {

return (int) super.h.invoke(this, m3, new Object\[\]{a, b});

}

}

```

性能优化:在 InvocationHandler 内部可缓存 Method 对象(JVM 已做),但更重要的优化是避免在 invoke 内做重操作,如大量字符串拼接、远程调用等。

CGLib 动态代理(针对无接口的类):

· 基于字节码框架创建目标类的子类。

· 覆盖(override)父类方法,织入增强逻辑。

· 需要引入 cglib 包。


  1. 单例模式 + 反射:攻防深入

攻击示例:

```java

// 普通单例(双重检查锁)

Singleton s1 = Singleton.getInstance();

Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor();

ctor.setAccessible(true);

Singleton s2 = ctor.newInstance(); // 成功创建第二个实例,破坏单例

```

防御手段:

  1. 标志位校验(最常用)

```java

private static boolean flag = false;

private Singleton() {

synchronized (Singleton.class) {

if (!flag) {

flag = true;

} else {

throw new RuntimeException("单例被反射破坏");

}

}

}

```

注意:标志位本身也可被反射修改,需加强保护(例如写入其他元信息或使用枚举)。

  1. 终极防御:枚举单例

```java

public enum EnumSingleton {

INSTANCE;

public void doSomething() { ... }

}

```

JVM 会保证枚举实例的唯一性,且反射在 newInstance 时会直接抛出异常禁止创建枚举对象。


  1. 依赖注入(IoC)中的反射实战

模拟一个极简版 Spring @Autowired 注入:

```java

// 目标类

public class UserService {

@Autowired

private UserDao userDao;

// getter/setter 略

}

// 注入处理器

public class InjectProcessor {

public static void inject(Object target) {

Field\[\] fields = target.getClass().getDeclaredFields();

for (Field field : fields) {

if (field.isAnnotationPresent(Autowired.class)) {

Object dependency = getBean(field.getType()); // 从容器获取实例

field.setAccessible(true);

field.set(target, dependency);

}

}

}

}

```

原理:运行时扫描字段,找到 @Autowired 后,反射获取依赖对象实例,然后 field.set() 强行赋值。


  1. 性能与安全:进阶权衡

方面 说明 建议

性能 反射调用比直接调用慢约1~3个数量级(依JVM优化而变) 高频调用可缓存Method对象;非框架代码优先用直接调用

安全检查 setAccessible(true) 可破坏私有封装 避免在生产代码中滥用;使用安全管理器时可禁止

泛型擦除 反射可绕过编译时泛型检查(如向List<String>插入Integer) 易引入ClassCastException,谨慎处理

维护性 大量反射使代码逻辑不直观,追踪困难 封装在框架底层;业务层保持清晰直接


  1. 综合面试例题:模拟 Spring AOP 实现

题目:使用 JDK 动态代理 + 自定义注解实现方法级日志记录。

```java

// 自定义注解

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Loggable {}

// 被代理类

public class CalculatorImpl implements Calculator {

@Loggable

public int add(int a, int b) { return a + b; }

public int sub(int a, int b) { return a - b; }

}

// InvocationHandler 实现

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 {

// 只有带 @Loggable 的方法才打印日志

if (method.isAnnotationPresent(Loggable.class)) {

System.out.println("Start method: " + method.getName());

long start = System.nanoTime();

Object result = method.invoke(target, args);

long end = System.nanoTime();

System.out.println("Cost: " + (end - start) + " ns");

return result;

}

return method.invoke(target, args);

}

}

// 测试

Calculator real = new CalculatorImpl();

Calculator proxy = (Calculator) Proxy.newProxyInstance(

real.getClass().getClassLoader(),

real.getClass().getInterfaces(),

new LogHandler(real));

proxy.add(1,2); // 打印日志

proxy.sub(1,2); // 无日志

```


  1. 总结升华

· 本质:反射将"编译时决定"延迟到"运行时决定",从而让设计模式获得可插拔、可配置的能力。

· 经典组合:

· 工厂 + 配置文件 + 反射 = 零耦合的产品族管理

· 策略 + 反射扫描 = 无限扩展算法库

· 代理 + 反射 + 注解 = 横切逻辑的精准织入

· 适用场景:框架开发、通用工具库、中间件、代码生成器。

· 不适用场景:高频核心业务路径、对性能极度敏感、需要严格编译时类型安全的场景。

相关推荐
神仙别闹2 小时前
基于Python + SQL server 实现(GUI)原神圣遗物管理与角色数值模拟系统
java·数据库·python
珊瑚里的鱼2 小时前
手撕单例模式中的饿汉模式和懒汉模式,懒汉模式还要再多加一个C++11版本的
开发语言·c++·单例模式
是有头发的程序猿2 小时前
电商自动化实战:淘宝/天猫item_get商品详情API全量采集教程(Python源码)
java·python·自动化
_不会dp不改名_2 小时前
python-opencv环境搭建
开发语言·python·opencv
古韵2 小时前
告别手写分页逻辑:usePagination 从 50 行到 3 行
java·前端
HappyAcmen2 小时前
9.复盘API全套流程
开发语言·python
V搜xhliang02462 小时前
临床科研新范式:从选题到投稿,AI智能体如何接管全流程?
运维·数据结构·人工智能·算法·microsoft·数据挖掘·自动化
北城以北88882 小时前
Quartz定时任务
java·spring boot·intellij-idea
charlie1145141912 小时前
通用GUI编程技术——图形渲染实战(四十五)——D3D12资源与堆管理:从上传到驻留
开发语言·3d·图形渲染·win32