- 工厂模式 + 反射:从"简单工厂"到"反射工厂族"
基础版回顾:工厂类根据类名字符串创建对象,新增产品零修改。
详细增强点:
· 带参数的构造函数:使用 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代码。这就是"对扩展开放,对修改封闭"。
- 策略模式 + 反射:消灭硬编码分支的终极方案
核心思想:用映射表(Map<String, Class<?>>)替代 if-else 或 switch。
进阶实现:自动扫描注解,消除手动注册
- 自定义一个注解 @StrategyType
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyType {
String value(); // 策略标识,如 "A", "B"
}
```
- 在策略实现类上标注
```java
@StrategyType("A")
public class ConcreteStrategyA implements Strategy { ... }
```
- 工厂自动扫描(需传入包名,或者遍历类路径)
```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 即可,工厂自动纳入管理,零改动核心逻辑。
- JDK动态代理的底层实现细节
代理执行流程:
-
Proxy.newProxyInstance() 动态生成一个字节码类($Proxy0)实现了指定接口。
-
该代理类内部持有 InvocationHandler 引用。
-
调用代理对象的 add(3,5) 时 → 代理类调用 handler.invoke(...)。
-
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 包。
- 单例模式 + 反射:攻防深入
攻击示例:
```java
// 普通单例(双重检查锁)
Singleton s1 = Singleton.getInstance();
Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor();
ctor.setAccessible(true);
Singleton s2 = ctor.newInstance(); // 成功创建第二个实例,破坏单例
```
防御手段:
- 标志位校验(最常用)
```java
private static boolean flag = false;
private Singleton() {
synchronized (Singleton.class) {
if (!flag) {
flag = true;
} else {
throw new RuntimeException("单例被反射破坏");
}
}
}
```
注意:标志位本身也可被反射修改,需加强保护(例如写入其他元信息或使用枚举)。
- 终极防御:枚举单例
```java
public enum EnumSingleton {
INSTANCE;
public void doSomething() { ... }
}
```
JVM 会保证枚举实例的唯一性,且反射在 newInstance 时会直接抛出异常禁止创建枚举对象。
- 依赖注入(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~3个数量级(依JVM优化而变) 高频调用可缓存Method对象;非框架代码优先用直接调用
安全检查 setAccessible(true) 可破坏私有封装 避免在生产代码中滥用;使用安全管理器时可禁止
泛型擦除 反射可绕过编译时泛型检查(如向List<String>插入Integer) 易引入ClassCastException,谨慎处理
维护性 大量反射使代码逻辑不直观,追踪困难 封装在框架底层;业务层保持清晰直接
- 综合面试例题:模拟 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); // 无日志
```
- 总结升华
· 本质:反射将"编译时决定"延迟到"运行时决定",从而让设计模式获得可插拔、可配置的能力。
· 经典组合:
· 工厂 + 配置文件 + 反射 = 零耦合的产品族管理
· 策略 + 反射扫描 = 无限扩展算法库
· 代理 + 反射 + 注解 = 横切逻辑的精准织入
· 适用场景:框架开发、通用工具库、中间件、代码生成器。
· 不适用场景:高频核心业务路径、对性能极度敏感、需要严格编译时类型安全的场景。