☰
Spring6工厂模式全解析:BeanFactory、FactoryBean与ObjectProvider实战
2026/9/26 7:22:27 网站建设 项目流程

Spring6 这套笔记写到第 4 篇,终于轮到工厂模式了。这个主题放在 IoC 容器的学习路径里特别合适,因为 Spring 本身就是个超级工厂——BeanFactory 这个名字已经明说了,它就是个工厂;ApplicationContext 是增强版工厂。Spring 的整个 IoC 容器,本质上是把“new 对象”的权力从调用方手里收走,统一交给一个工厂来管理和发放。这个工厂不只负责创建,还负责装配、初始化、代理、销毁。

这篇笔记想把这层包装撕开,从最简单的工厂模式讲起,一路看到它在 Spring6 里的落地形态:BeanFactory、FactoryBean、@Bean 方法、ObjectProvider,以及大家写业务代码时最常用到的“策略工厂”范式。不管你是在读 Spring 源码,还是在项目里想要优雅地干掉 switch-case 分支创建逻辑,这篇笔记都适用。

1. 工厂模式为什么是 Spring 的底层逻辑

1.1 三种工厂形态与 Spring 的映射

不用急着背定义。你可以把工厂模式理解成一句话:把“创建谁”和“怎么创建”从调用方手里拿过来,交给一个专门的地方负责。调用方只关心“我要一个能用的东西”,不关心它从哪来、怎么组装、是单例还是每次都新建。

经典的书把工厂模式拆成了三类:

  • 简单工厂:一个工厂类里放 switch-case 或 if-else,根据参数返回不同的产品。缺点是每加一个产品就要改工厂。
  • 工厂方法:定义一个创建产品的抽象方法,由子类决定具体创建哪一种。把“创建逻辑”下沉到子类。
  • 抽象工厂:针对“产品族”的创建,可以创建一组相互关联的产品对象,而不是单个产品。

如果把这三种形态映射到 Spring6 里,你会发现 Spring 全是它们的影子:简单工厂对应@Bean+ 手动 if-else 或者 Map 收集;工厂方法对应某个 Configuration 类的@Bean方法;抽象工厂对应 Spring 的自动配置机制——AutoConfiguration根据条件决定给你装配哪一组组件。下面这张表是我经常用来给学生做对照的:

工厂模式形态Spring 中的典型落点典型场景
简单工厂@Bean方法配合@Qualifier、Map<String, Interface>注入按类型或名称从容器取 Bean
工厂方法每个@Configuration类里的@Bean方法声明式定义 Bean 的创建过程
抽象工厂Spring Boot AutoConfiguration + Condition根据条件装配一组相关组件

但真正值得注意的点是:Spring 的工厂模式不是某一个类,而是一整套容器机制。BeanFactory管生命周期,FactoryBean管复杂对象的创建,@Bean方法管声明式装配,ObjectProvider管延迟获取。四个机制玩明白了,工厂模式在 Spring6 里你就通关了。

1.2 Spring6 里工厂思想放在哪几个关键位置

Spring6 的代码基线是 Java 17,底层换成了 Jakarta EE 9+ 的命名空间,但核心的工厂思想没变。我在读源码时,发现有几个地方是理解整个容器的钥匙:

第一个是DefaultListableBeanFactory。这个类名很长,但它就是 Spring 容器最核心的“大工厂车间”。它内部维护了beanDefinitionMap(Bean 定义的注册表)和singletonObjects(单例池),每次getBean()的时候,先查单例池,没有再根据 BeanDefinition 去创建。整个流程走一遍,就是工厂模式的工业化版本。

第二个是FactoryBean<T>接口。这个接口是给“创建过程特别复杂的 Bean”准备的。比如你创建一个 Redis 连接池,希望在 Spring 启动后得到一个 JedisPool 实例,这个实例的创建过程涉及一堆参数组装和校验,直接在@Bean方法里写会显得很乱,这时可以实现FactoryBean,把创建逻辑封装在getObject()里。

第三个是@Bean方法本身。很多人没意识到,@Configuration类里的每个@Bean方法就是一个工厂方法。当你调用这个工厂方法时,Spring 会介入拦截,保证默认情况下同一个@Bean方法只执行一次,返回的都是同一个单例。这个特性就是 Spring6 中@Configuration的proxyBeanMethods属性控制的——默认true,用 CGLIB 代理拦截;设为false则不做拦截,每次调用方法都会新建对象。

第四个是ObjectProvider<T>。这是 Spring 5.1 引入、在 Spring6 里被官方大力推荐的延迟工厂注入方式。你注入的不再是一个直接可用的 Bean,而是一个“提供者”,等实际要用了再通过getObject()或getIfAvailable()去容器里拿。这个设计对解决循环依赖和可选依赖非常有用。

1.3 为什么不是所有 Bean 都必须走 FactoryBean

说到这里你可能会问:难道每个 Bean 都要手写一个 FactoryBean 吗?不是的,这是新手最容易误解的地方。

FactoryBean是给少数“特殊创建逻辑”准备的接口,大多数 Bean 用@Component扫描或@Bean方法就够了。你可以这样理解:普通 Bean 是“流水线上标准化生产”的产品,FactoryBean是“定制车间”里手工打造的产品。Spring 设计FactoryBean的初衷,是让你在容器标准创建流程之外,还能插入自定义的创建逻辑。

那 Spring 自己内部用 FactoryBean 做了什么?最有名的例子有两个:

  • ProxyFactoryBean:AOP 代理对象的创建。你定义切面后,Spring 通过它生成代理对象。
  • SqlSessionFactoryBean:MyBatis-Spring 集成时,它负责创建SqlSessionFactory,因为 SqlSessionFactory 的创建需要读取配置文件、解析 Mapper、注册类型别名,逻辑量很大。

所以在 Spring6 的项目里,当你发现某个对象的创建过程特别复杂,或者需要根据运行时配置动态决定返回类型时,才值得考虑实现FactoryBean;否则用@Bean方法即可。

2. Spring6 工厂模式核心接口与延迟获取机制解析

2.1 BeanFactory 与 FactoryBean:名字像、职责完全不同

这是 Spring 里最容易混淆的两个名字,面试也经常问。一句话区分:BeanFactory 是容器的根接口,管“所有 Bean 的获取与生命周期”;FactoryBean 是一个 Bean,管“某一个 Bean 的创建逻辑”。

BeanFactory的使命很宏大:提供getBean()、containsBean()、isSingleton()、getType()等方法,是整个 IoC 容器对外的门面。FactoryBean<T>的使命很具体:它的泛型 T 代表它最终要创建的产品类型,它本身也是一个 Bean,注册在容器里,但当别人向容器索要这个 Bean 时,Spring 发现它是一个FactoryBean,会调用它的getObject(),把返回结果交给调用者。

举个具体的例子。假设我写了一个OrderFactoryBean implements FactoryBean<Order>,然后把它注册到容器。别人@Autowired一个Order类型时,容器检查发现当前处理的 Bean 是OrderFactoryBean,于是调用它的getObject(),拿到一个Order实例交给调用者。调用者感知不到OrderFactoryBean的存在。

这里有一个经典陷阱:如果你需要的是OrderFactoryBean本身,而不是它生产的Order,怎么办?答案是使用&前缀:applicationContext.getBean("&orderFactoryBean")。Spring 约定,带&前缀的 Bean 名,返回的是工厂本身;不带&的返回的是工厂生产的产品。这个细节在实际排查问题时非常关键,后面我会在问题章节详细讲。

2.2 FactoryBean 的 getObject 与 getObjectType 设计意图

FactoryBean接口总共三个抽象方法,我来逐个拆解:

public interface FactoryBean<T> { // 返回工厂生产的对象实例 T getObject() throws Exception; // 返回生产对象的类型,用于类型匹配和依赖注入 Class<?> getObjectType(); // 是否单例:true 表示容器只保留一个产品实例,false 表示每次获取都是新对象 default boolean isSingleton() { return true; } }

getObject()是核心,你把复杂对象的创建过程写在这里,返回一个 T 类型的实例。这个方法在容器启动或首次获取 Bean 时被调用。getObjectType()也很关键,Spring 在做自动注入匹配时,需要知道你的工厂到底能生产什么类型,如果这里返回null,Spring 就不得不实例化工厂才能拿到产品类型,可能会导致提前初始化或者类型推断失败。

isSingleton()控制产品的缓存策略。默认返回true,工厂生产的产品会放入单例池,整个容器中只有一个;如果你设置为false,每次获取都会调用getObject()新建对象。这就相当于手动控制“产品”的作用域。

这背后的设计意图是什么呢?就是把“Bean 的创建细节”封装在一个可替换的工厂里,让 IoC 容器只认FactoryBean接口。容器不需要知道你的产品是三元组装的还是反射创建的,它只需要知道“这个工厂能生产某个类型的产品”,就可以在你注入该类型的时候,通过工厂把对象给你。

2.3 ObjectProvider:Spring6 中最推荐的延迟工厂注入方式

ObjectProvider<T>是 Spring6 项目里值得重点掌握的一个接口。你可以在注入点使用它,而不是直接注入目标对象:

@Service public class OrderService { // 不再直接注入 Handler private final ObjectProvider<PayHandler> handlerProvider; public OrderService(ObjectProvider<PayHandler> handlerProvider) { this.handlerProvider = handlerProvider; } public void pay() { // 需要时再获取 PayHandler handler = handlerProvider.getIfAvailable(); handler.pay(); } }

这样做的好处有三个:第一,延迟获取。容器启动时不会立即创建PayHandler,而是注入一个“提供者”,真正调用getIfAvailable()时才去容器拿实例。对于创建成本很高的 Bean,或者依赖了运行时配置的 Bean,这是非常实用的。第二,可选依赖。getIfAvailable()在没有 Bean 时返回null,比@Autowired(required = false)更优雅,也更容易写默认值逻辑。第三,规避循环依赖。两个 Bean 互相引用时,如果一边改成注入ObjectProvider<T>,容器的创建顺序就不需要同时满足双方了,生命周期问题自然缓解。

这个接口的功能远不止这些,它还提供了getIfUnique()(如果容器中只有一个才返回,多个则返回 null)、stream()(获取所有实现)、orderedStream()(按@Order排序获取所有实现)。这几个方法在策略模式 + 工厂模式的场景下,简直是为我们量身定做的工具。

我见过很多项目还是老写法:@Autowired private List<PayHandler> handlers;然后在方法里手动遍历。换成ObjectProvider后,代码更简洁,还能在运行时按需获取,不再被“启动时就必须收集全部实现”的约束绑死。Spring6 官方文档在依赖注入章节也明确推荐了ObjectProvider,建议你把老代码里的可选注入逐步换掉。

2.4 Spring6 里编程式获取 Bean 的更稳姿势

除了@Autowired和ObjectProvider,有时我们需要在非 Spring 管理的类里拿到 Bean。这时很多人直接注入ApplicationContext,然后调用getBean()。这种做法能用,但不够好。

更好的姿势是注入ObjectProvider或使用ApplicationContext.getBeanProvider(Class<T>)方法,在 Spring6 里这是官方建议的编程式获取方式。原因在于,getBeanProvider返回的是一个ObjectProvider<T>,你可以用getIfAvailable、getIfUnique、stream这些方法,代码读起来更清晰,而且保持了延迟获取的特性。

来看一段实用代码示例:你有一个工具类,里面需要拿到所有MessageHandler实现并按顺序处理消息:

@Component public class MessageDispatcher { private final ObjectProvider<MessageHandler> handlerProvider; public MessageDispatcher(ObjectProvider<MessageHandler> handlerProvider) { this.handlerProvider = handlerProvider; } public void dispatch(Message msg) { handlerProvider.orderedStream() .filter(handler -> handler.supports(msg.getType())) .findFirst() .ifPresent(handler -> handler.handle(msg)); } }

orderedStream()会按实现类上的@Order注解排序,返回一个有序流。你可以通过supports()方法筛选出能处理当前消息的那个实现。这种写法比“注入 Map 然后遍历”更优雅,也天然支持了开闭原则——新增一个MessageHandler实现类,不需要修改MessageDispatcher的任何代码。

3. 实操:用工厂模式重构多渠道订单处理器

3.1 场景与设计:接口、注解、自动收集

这一节,我们抛开理论,看一个我在订单中心项目里真实落地的工厂模式改造案例。场景很经典:系统对接了多个支付渠道,微信、支付宝、银联,每种渠道的支付参数、签名算法、回调验签都不一致。最初代码写在一个支付服务类里,用长长的 if-else 判断渠道类型:

public PayResult pay(PayRequest request) { if ("wechat".equals(request.getChannel())) { // 微信支付逻辑 } else if ("alipay".equals(request.getChannel())) { // 支付宝支付逻辑 } else if ("unionpay".equals(request.getChannel())) { // 银联支付逻辑 } else { throw new UnsupportedOperationException("不支持的渠道"); } }

每接一个新渠道,就要改动这个几百行的类,测试回归范围越来越大。后来我彻底重构,用“接口 + 注解 + 自动收集 + 工厂分发”的经典组合。设计如下:

  1. 定义一个PayHandler接口,包含support(String channel)和pay(PayRequest request)方法。
  2. 每种渠道写一个实现类,用@Component注册到容器。
  3. 用@Qualifier或 Map 注入,把所有PayHandler的实现自动收集到工厂类里。
  4. 工厂类根据request.getChannel()找到对应处理器并分发。

这套设计的好处是:新增渠道时,只需要添加新的实现类,工厂类不用改,老代码不用改。这正是工厂模式在业务代码里价值最大的应用。

3.2 核心代码实现与逐段说明

先定义接口:

public interface PayHandler { // 当前处理器支持的支付渠道 String channel(); // 支付方法 PayResult pay(PayRequest request); }

然后每个渠道实现自己的逻辑。这里以微信支付为例:

@Component public class WechatPayHandler implements PayHandler { @Override public String channel() { return "wechat"; } @Override public PayResult pay(PayRequest request) { // 实际的微信支付调用 // 构造参数、签名、调用微信支付 API return PayResult.success("wechat", request.getOrderId()); } }

支付宝和银联的实现类似,只是渠道值不同、内部逻辑不同。接下来是工厂类的核心代码:

@Component public class PayHandlerFactory { // 关键点:通过 Map 注入,容器会把所有 PayHandler 类型 Bean 收集进来,key 是 Bean 名 private final Map<String, PayHandler> handlerMap; public PayHandlerFactory(Map<String, PayHandler> handlerMap) { this.handlerMap = handlerMap; } public PayHandler getHandler(String channel) { return handlerMap.values().stream() .filter(handler -> handler.channel().equals(channel)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("不支持的支付渠道: " + channel)); } }

这里我要重点讲一个细节:Map<String, PayHandler>的 key 是什么?

Spring 在注入 Map 时,会把当前容器中所有匹配PayHandler类型的 Bean 的Bean 名作为 key,把Bean 实例作为 value。Bean 名默认是类名的首字母小写,比如wechatPayHandler、alipayPayHandler。所以如果你直接用handlerMap.get("wechat"),是拿不到东西的,因为 key 是类名映射后的名字,不是我们定义的渠道名。我在代码里用values().stream()过滤channel()值,就是为了避免依赖 Bean 名与渠道名的耦合约定。

另外,handlerMap通过构造器注入,这也是 Spring6 官方推荐的注入方式。构造器注入能保证依赖完整,不容易出现运行时的 NullPointerException,也方便单元测试时手动传入 mock 的 Map。

最后在调用侧,只需要简单一句:

payHandlerFactory.getHandler(request.getChannel()).pay(request);

从表面看,这段代码只是少了 if-else,但本质上是把“创建和分发”与“业务实现”彻底解耦了。以后新增渠道,你就只管加实现类,不用再动工厂。

3.3 扩展点:默认处理器与优雅降级

线上运行一段时间后,你大概率会遇到一个需求:某些渠道暂时没有接入,但用户又提交了请求,与其直接抛异常,不如友好降级。或者,某些新渠道处于灰度验证阶段,你想默认走一个兜底处理器。

这时,上面的orElseThrow就不合适了。我改造后的工厂逻辑如下:

public PayHandler getHandler(String channel) { return handlerMap.values().stream() .filter(handler -> handler.channel().equals(channel)) .findFirst() .orElse(defaultHandler); }

defaultHandler可以单独做成一个DefaultPayHandler,它的内部逻辑是记录日志、发送告警、返回“渠道暂不支持”的提示。如果还要做得更精细,可以结合配置中心动态指定某类渠道走默认处理器:

public PayHandler getHandler(String channel) { // 灰度期间,指定渠道走到默认处理器 if (downgradeChannels.contains(channel)) { return defaultHandler; } return handlerMap.values().stream() .filter(handler -> handler.channel().equals(channel)) .findFirst() .orElse(defaultHandler); }

这个扩展并不复杂,但它体现了工厂模式的另一个价值:把变化收敛在工厂内部,调用方完全无感知。你可以在轮询切换、灰度配置、故障降级这些需求上做文章,而不会对业务代码造成冲击。

3.4 落地时注意的 Spring 版本差异

我踩过一个比较隐蔽的坑:在 Spring Boot 2.x 和 Spring 6 之间,Map<String, Handler>注入的行为是一致的——都是按 Bean 名注入。但在结合泛型接口时会有细微差别。

假设你的接口定义带泛型:

public interface PayHandler<T extends PayRequest> { PayResult pay(T request); }

当你注入Map<String, PayHandler>时,Spring 会尝试用ResolvableType做类型匹配。如果实现类的泛型参数能对上,就顺利注入;如果接口的泛型信息丢失,比如实现了PayHandler<RawType>,Spring 在部分版本下可能因类型擦除而匹配失败。

所以在用工厂模式 + 泛型接口时,我建议你在实现类上明确写出泛型类型,比如:

@Component public class WechatPayHandler implements PayHandler<WechatPayRequest> { // ... }

这样 Spring 的ResolvableType机制能准确识别类型。如果是用反射做消息路由这类更复杂的泛型场景,一定要测试容器启动时能否正常注入 Map。这个问题在 Spring6 里依然存在,只是官方文档里没有特别强调。

4. 常见问题与排查技巧实录

4.1 案例一:FactoryBean 类型推断失败导致转换异常

有次在排查一个启动直接报错的问题时,我发现了典型的FactoryBean类型推断陷阱。

错误信息大概是:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'xxx' is expected to be of type 'ActualType' but the actual type was 'FactoryBeanType'

出现这个错误,通常是因为你在@Bean方法里返回了FactoryBean<T>类型,但调用的地方期望的是T类型。看下面这段问题代码:

@Configuration public class AppConfig { @Bean public OrderFactoryBean orderFactoryBean() { return new OrderFactoryBean(); } }

注入方:

@Autowired private Order order;

容器启动后,order实际是一个OrderFactoryBean对象,而不是Order。这和我们前面的预期——FactoryBean 会返回产品——是矛盾的。

原因在于:直接在@Bean方法里返回工厂实例,Spring 会把orderFactoryBean当成普通 Bean 注册,不会触发FactoryBean的处理逻辑。只有在 XML 配置或@Component扫描方式注册FactoryBean时,Spring 的BeanDefinition里会标记为 FactoryBean,然后才能正常触发getObject()。

这个坑的解法有两种。第一种是确保FactoryBean以组件形式注册,不要用@Bean直接注册;第二种是修正注入方:

@Autowired private ApplicationContext context; public void doSomething() { Order order = context.getBean(Order.class); }

但如果你真的需要在@Bean方法里注册工厂,而且想让 Spring 按工厂处理,就得直接返回FactoryBean类型并让 Spring 感知到它。比较稳妥的做法是使用@Component给OrderFactoryBean加注解,然后依赖注入时直接索要产品类型。

4.2 案例二:Map 注入拿不到 Bean 或拿到代理对象

用Map<String, PayHandler>注入策略时,有个容易忽视的问题:如果PayHandler实现类加了 AOP 切面,比如事务、日志、权限,Map 里存的是代理对象。

这通常不会引起程序错误,但如果你在工厂里用instanceof或获取注解来判断某类 Handler,容易拿到代理对象的类而不是目标类。另外,事务代理的channel()方法调用是没问题的,AOP 拦截正常,但如果你试图在工厂里通过handler.getClass().getAnnotation(...)获取自定义注解,记得要处理代理类的情况。

排查技巧:在临时调试代码里打印handlerMap的每个 value 的 class,看是不是包含$$EnhancerBySpringCGLIB$$字样,如果包含,你就知道它是代理对象。这时候访问目标注解应该用AopProxyUtils.getSingletonTargetClass(handler)而不是直接getClass()。

还有一个常见情况是“Map 注入拿到空的集合”。多半是接口类型写得不一致:比如实现在com.demo.handler包,而注入的泛型是com.demo.other.PayHandler,虽然类名一样,但包路径不同,Spring 不会匹配。建议把所有策略接口统一放到一个模块的 api 包里,从结构上规避这种问题。

4.3 案例三:循环依赖与 ObjectProvider 的配套使用

Spring6 里循环依赖的处理逻辑更严格了,默认不允许循环依赖的场景越来越多。我在改造老项目时遇到过这样一个案例:OrderService和PaymentService互相调用,两个类依赖对方。

按旧写法:

@Service public class OrderService { private final PaymentService paymentService; public OrderService(PaymentService paymentService) { this.paymentService = paymentService; } } @Service public class PaymentService { private final OrderService orderService; public PaymentService(OrderService orderService) { this.orderService = orderService; } }

这种构造器注入的循环依赖在 Spring 6 里直接无法启动。解决思路不是去调allowCircularReferences,而是从设计上打破循环。我用ObjectProvider改了一边:

@Service public class PaymentService { private final ObjectProvider<OrderService> orderServiceProvider; public PaymentService(ObjectProvider<OrderService> orderServiceProvider) { this.orderServiceProvider = orderServiceProvider; } }

在方法里需要用到OrderService时,再通过orderServiceProvider.getObject()获取。这样一来,Spring 创建PaymentService时不需要立即创建OrderService,循环依赖就解开了。

这种方式比@Lazy注解更干净,因为@Lazy是给整个注入点生成一个代理,而ObjectProvider是真正延迟到方法调用时才获取,语义更清晰,调试起来也不会有代理干扰。

4.4 工厂模式相关报错速查表

我把这些年遇到的和工厂模式相关的经典报错整理成了一张表,方便你出现问题时快速定位:

错误类型典型报错关键词常见原因解决思路
类型不匹配BeanNotOfRequiredTypeException@Bean方法返回了 FactoryBean,调用方却期望产品类型改注册方式或使用&前缀获取工厂
空 Map 注入Map 为 empty接口包路径不一致、实现类未扫描到、泛型类型擦除检查包扫描范围与类型一致性
NullPointerExceptionhandler 为 null工厂orElseThrow抛异常或 Map 注入失败加默认处理器或者改用getIfAvailable
启动循环依赖The dependencies of some of the beans in the application context form a cycle构造器注入相互引用用 ObjectProvider 或重构职责边界
FactoryBean 未被识别产品类型没有按工厂方式创建使用@Bean注册 FactoryBean 未触发工厂逻辑使用@Component注册或检查 Bean 定义
ObjectProvider 为 nullNullPointerException in constructor忘记加@Component注解或工厂未注入检查类是否被扫描到

4.5 排查三个顺手技巧

最后分享三个排查工厂模式相关代码的顺手技巧。

第一个技巧:启动时打印所有策略实现。在工厂类的构造器里临时加一行日志,把收集到的实现类 list 打印出来。这样能最快确认你注入的 Map 到底有哪些 Bean,类型是否如预期。日志打出来,八成的“为什么我的 Map 空着”问题当场就能解决。

public PayHandlerFactory(Map<String, PayHandler> handlerMap) { this.handlerMap = handlerMap; System.out.println("收集到 PayHandler 实现: " + handlerMap.keySet()); }

第二个技巧:善用ApplicationContext.getBeanProvider。你不需要把整个ApplicationContext注入到工具类或静态方法里,只需要在入口处拿到 provider,然后传递它。这样既能延迟获取,又保持了测试的便利性。

第三个技巧:遇到 FactoryBean 相关的问题,先在 Spring 容器启动时打印getBeanDefinitionNames(),查看被注册的 Bean 名。如果看到了名字里带&前缀的特殊条目——注意在getBeanDefinitionNames()的结果里,FactoryBean 本身的 Bean 名是普通名,不带&,&是getBean查询时才需要的语法。通过这个细节,可以确认哪些 Bean 是工厂注册的。这个信息对判断问题非常关键。

从我个人的实际经历来看,工厂模式在 Spring6 里不是一个孤立的“知识考点”,它几乎渗透在容器的每个角落——从BeanFactory这个根接口的名字,到FactoryBean处理复杂对象创建,再到ObjectProvider提供延迟获取能力,一整条线串下来,你对 Spring 的设计取舍就通透了。忘掉理论书上的三个分类,抓住一个本质:对象的创建权和控制权从调用方手里移交到容器手里,调用方只对“得到的对象”负责,创建过程永远可以替换、可以扩展。之后你在 Spring6 里写策略代码、看自动配置源码,都会顺畅得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询