设计模式与依赖注入:工厂模式实现DI、单例模式管理Bean、代理模式实现AOP
说实话,很多朋友学设计模式时,总觉得它们是孤立的。工厂就是创建对象,单例就是全局唯一,代理就是加个中间层。但在我十几年的项目经验里,这些模式往往是一起出现的。尤其是当你开始写一个轻量级的框架时——嗯,你会发现它们天然就是一家人。
今天我们就来聊聊,怎么用工厂模式实现依赖注入(DI),用单例模式管理Bean,用代理模式实现AOP。这三者组合起来,其实就是Spring IoC容器的核心雏形。我自己在早期做项目时,就手写过这样一个微型容器,效果出奇地好。
1. 工厂模式实现依赖注入
依赖注入,说白了就是「别自己new,让别人给你」。谁来做这个「别人」?工厂模式最合适。
我习惯把工厂分为两种:一种是简单工厂,根据类型直接创建;另一种是IoC工厂,它会扫描配置,递归地创建并注入依赖。
来看一个最简的IoC工厂实现:
// 一个极简的依赖注入容器
public class SimpleIoCContainer {
private Map<String, Object> beans = new HashMap<>();
private Map<String, BeanDefinition> definitions = new HashMap<>();
public void registerBean(String name, BeanDefinition def) {
definitions.put(name, def);
}
public Object getBean(String name) {
if (beans.containsKey(name)) {
return beans.get(name);
}
// 工厂模式:创建并注入依赖
BeanDefinition def = definitions.get(name);
Object bean = createInstance(def.getClassName());
// 注入依赖属性
injectDependencies(bean, def.getProperties());
beans.put(name, bean);
return bean;
}
private void injectDependencies(Object bean, Map<String, String> props) {
for (Map.Entry<String, String> entry : props.entrySet()) {
String fieldName = entry.getKey();
String refBeanName = entry.getValue();
Object refBean = getBean(refBeanName); // 递归注入
setField(bean, fieldName, refBean);
}
}
}
你看,这里的getBean方法就是一个典型的工厂方法。它不直接new,而是根据配置创建对象,并且自动把依赖的Bean注入进去。我在一个电商项目中就用过类似的思路,把几十个Service的依赖关系全部交给这个工厂管理,再也不用担心构造顺序了。
2. 单例模式管理Bean
容器里的Bean,大部分应该是单例的。为什么?因为无状态的Service、DAO,创建一次就够了,反复创建浪费内存,也浪费GC。
单例模式在这里的应用,其实比教科书上的「懒汉式」「饿汉式」要灵活得多。我们不是在类内部控制单例,而是在容器层面控制——同一个Bean名称,只创建一个实例。
// 容器级别的单例管理
public class SingletonBeanRegistry {
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}
public void registerSingleton(String beanName, Object bean) {
Object old = singletonObjects.putIfAbsent(beanName, bean);
if (old != null) {
throw new IllegalStateException("Bean already exists: " + beanName);
}
}
public boolean containsSingleton(String beanName) {
return singletonObjects.containsKey(beanName);
}
}
这里有个细节:我用的是putIfAbsent而不是put。为什么?因为并发环境下,两个线程可能同时注册同一个Bean。我曾经在生产环境遇到过这个问题——一个定时任务框架因为并发注册导致Bean被覆盖,结果某些任务执行了两次。嗯,从那以后我对并发注册格外小心。
3. 代理模式实现AOP
AOP(面向切面编程)的核心,就是代理模式。你想在方法执行前后加日志、加事务、加权限检查,但又不想改原有代码——那就用代理把它包一层。
Java里实现代理有两种方式:JDK动态代理和CGLIB。JDK代理要求目标类实现接口,CGLIB可以直接代理类。我一般优先用JDK代理,因为它是原生支持的,性能也更好。
// 一个简单的AOP代理工厂
public class JdkDynamicAopProxy implements InvocationHandler {
private Object target;
private List<MethodInterceptor> interceptors;
public Object createProxy(Object target, List<MethodInterceptor> interceptors) {
this.target = target;
this.interceptors = interceptors;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
for (MethodInterceptor interceptor : interceptors) {
interceptor.before(method, args);
}
// 调用目标方法
Object result = method.invoke(target, args);
// 后置增强
for (MethodInterceptor interceptor : interceptors) {
interceptor.after(method, args, result);
}
return result;
}
}
你想想看,这个代理工厂和前面的IoC容器结合起来,会发生什么?容器创建Bean时,如果检测到它有AOP配置,就不直接返回原始Bean,而是返回代理对象。调用方拿到代理对象,完全感知不到底层有增强逻辑——这就是AOP的魅力。
1. 容器启动,读取配置
2. 工厂模式创建Bean实例
3. 单例注册表管理Bean生命周期
4. 如果Bean需要AOP,代理工厂生成代理对象
5. 最终返回给调用方的是代理后的Bean
4. 三者整合:一个微型IoC容器
下面我把这三者整合起来,形成一个完整的微型容器。这个容器虽然简单,但核心思想跟Spring IoC是一致的。
// 微型IoC容器
public class MiniApplicationContext {
private SingletonBeanRegistry singletonRegistry = new SingletonBeanRegistry();
private SimpleIoCContainer iocContainer = new SimpleIoCContainer();
private JdkDynamicAopProxy aopProxy = new JdkDynamicAopProxy();
public void refresh() {
// 1. 扫描配置,注册Bean定义
loadBeanDefinitions();
// 2. 创建单例Bean(非懒加载的)
for (String beanName : getSingletonBeanNames()) {
getBean(beanName);
}
}
public Object getBean(String name) {
// 先从单例缓存取
Object bean = singletonRegistry.getSingleton(name);
if (bean != null) {
return bean;
}
// 工厂创建(含依赖注入)
bean = iocContainer.getBean(name);
// 如果需要AOP,生成代理
if (hasAopConfig(name)) {
List<MethodInterceptor> interceptors = getInterceptors(name);
bean = aopProxy.createProxy(bean, interceptors);
}
// 注册为单例
singletonRegistry.registerSingleton(name, bean);
return bean;
}
}
这段代码虽然只有几十行,但它把工厂模式、单例模式、代理模式完美地融合在了一起。我在做一个内部工具平台时,就用这个思路写了一个不到500行的容器,支撑了十几个微服务的Bean管理。说实话,效果超出预期。
5. 知识体系总览
为了让你更直观地理解这三者的关系,我画了一张图:
从这张图可以看得很清楚:工厂模式是「生产者」,单例模式是「仓库」,代理模式是「包装车间」。三者各司其职,又紧密协作。你写框架时,这个结构可以直接复用。
6. 总结与思考
聊了这么多,我想表达的核心观点其实就一句话:设计模式不是背出来的,是用出来的。工厂、单例、代理这三个模式,单独看都很简单,但把它们组合起来,就能构建出像Spring这样强大的框架。
我在带团队时,经常让新人做一个小练习:用这三大模式手写一个简易的IoC容器,支持XML配置、单例管理、AOP日志增强。做完这个练习,对Spring的理解会上一个台阶。你不妨也试试。
✅ 工厂模式:负责对象的创建和依赖装配
✅ 单例模式:负责Bean的缓存和生命周期管理
✅ 代理模式:负责横切逻辑的织入和增强
✅ 三者组合:构成了依赖注入容器的核心骨架
最后说一句:别怕手写框架。你写一次,胜过读十遍源码。嗯,今天就到这里。
公众号:蓝海资料掘金营,微信deep3321