Spring容器创建对象的三种方式

简介: 本文详解 Spring 容器如何通过反射机制实例化 Bean,涵盖默认构造函数、静态工厂方法与实例工厂方法三种方式,并结合 XML 配置与实战代码演示。内容还对比了 factory-method 与 factory-bean 的区别,总结使用场景与最佳实践,助你掌握 Spring IOC 底层原理。欢迎关注作者,获取更多 Java 架构干货。
本文已收录在[Github](https://github.com/Java-Edge/Java-Interview-Tutorial),**关注我,紧跟本系列专栏文章,咱们下篇再续!** - 🚀 魔都架构师 | 全网30W技术追随者 - 🔧 大厂分布式系统/数据中台实战专家 - 🏆 主导交易系统百万级流量调优 & 车联网平台架构 - 🧠 AIGC应用开发先行者 | 区块链落地实践者 - 🌍 以技术驱动创新,我们的征途是改变世界! - 👉 实战干货:[编程严选网](http://www.javaedge.cn/) ## 0 前言 Spring 容器只负责“根据描述(BeanDefinition)”去实例化对象、注入依赖以及管理生命周期。真正的实例化工作是由* **Java 反射** *完成的——无论是* `Class.newInstance()`*(普通构造函数),还是* `Method.invoke(...)`*(工厂方法)。* ## 1 默认的无参构造器 ```javascript public class HelloWorld { public HelloWorld() { System.out.println("spring 在默认的情况下,使用默认的构造函数"); } public void sayHello() { System.out.println("hello"); } } ``` spring容器解析spring的配置文件,利用Java反射来创建对象: ```java public class TestHelloWorld { @Test public void testHelloWorld() { // 启动sping容器:读取类路径下的 applicationContext.xml,解析成多个 BeanDefinition,并把它们注册到 DefaultListableBeanFactory ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 从spring容器把对象取出获取 Bean:根据 bean id(或别名)返回已经创建好的实例 HelloWorld helloWorld = (HelloWorld)context.getBean("helloWorld"); helloWorld.sayHello(); // 别名验证:通过 context.getBean("JavaEdge") 说明别名与原 id 指向同一个对象,调用 sayHello() 的输出相同 HelloWorld alias = (HelloWorld)context.getBean("JavaEdge"); alias.sayHello(); } ``` ```xml ``` **``**:只是在容器内部维护一个 `Map`(别名 → 原 bean name),不产生新对象。 ## 2 静态工厂方法 在spring内部调用HelloWorldFactory内部的getInstance内部方法。 而该方法的内容,就是创建对象的过程,是由程序员来完成的,这就是静态工厂 ```javascript public class TestHelloWorld { @Test public void testHelloWorldFactory(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloWorld helloWorld = (HelloWorld)context.getBean("helloWorldFactory"); helloWorld.sayHello(); } ``` ```java public class HelloWorldFactory { public static HelloWorld getInstance(){ return new HelloWorld(); } } ``` ```xml ``` ## 3 实例工厂 * 1.spring容器(beans)创建了一个实例工厂的bean * 2.该bean 调用了工厂方法的getInstance 方法产生对象 ```java @Test public void testHelloWorldFactory2() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloWorld helloWorld = (HelloWorld)context.getBean("helloWorld3"); helloWorld.sayHello(); } ``` ```java public class HelloWorldFactory2 { public HelloWorld getInstance(){ return new HelloWorld(); } } ``` ```xml ``` ### 工作原理 1. 容器先实例化 `helloWorldFactory2` → 调用默认构造函数得到 `HelloWorldFactory2` 对象。 2. 当创建 `helloWorld3` 时,容器拿到已经存在的 `helloWorldFactory2` 实例,使用反射调用其 `getInstance()` 方法,返回 `HelloWorld`。 ### 常见坑 | 坑点 | 说明 | | :----------------------------------------------------------- | :------------------------------------------- | | 如果 `factory-bean` 对应的 bean **作用域** 为 `prototype`,每次获取 `helloWorld3` 都会重新创建工厂实例 → 可能导致意外的对象状态。 | 建议把工厂设为单例(默认),除非有特殊需求。 | | 工厂方法可以带参数,需在 `` 中用 `` 或 `` 注入这些参数。 | 示例中未涉及,但值得知道。 | ### factory-method V.S factory-bean 前者用于 *静态* 方法,后者用于 *实例* 方法。二者可以组合使用,但不能同时出现在同一个 `` 中。 ## 4 总结 *Spring 容器读取 XML 配置后,会通过* **反射** *完成三类 bean 的实例化:* - 普通构造函数 - 静态工厂方法 - 实例工厂方法 此外,容器支持别名(alias)机制,使同一个对象可以用多个名字访问。 选型: - 普通构造函数适用于 *无参*、创建成本低的类 - 静态/实例工厂提供 **更大的灵活性**(可以在工厂内部做复杂初始化、返回子类或缓存对象) - 别名帮助在不同模块/配置文件中统一引用同一个 bean,提升可维护性 ### 最佳实践 ```java // 推荐使用 @Configuration + @Bean 而不是 XML(Spring Boot / Spring Framework 5+) @Configuration public class HelloWorldConfig { @Bean(name = {"helloWorld", "三毛"}) // 同时注册别名 public HelloWorld helloWorld() { return new HelloWorld(); // 普通构造函数方式 } @Bean public HelloWorld helloWorldFromStaticFactory() { return HelloWorldFactory.getInstance(); // 静态工厂 } @Bean public HelloWorldFactory2 helloWorldFactory2() { return new HelloWorldFactory2(); // 实例工厂本身(单例) } @Bean public HelloWorld helloWorldFromInstanceFactory(HelloWorldFactory2 factory) { return factory.getInstance(); // 通过实例工厂创建 } } ``` - **配置类** 替代 XML,类型安全、IDE 可直接跳转。 - `@Bean(name = {"id1","id2"})` 同时声明别名。 - 依赖注入(如 `helloWorldFromInstanceFactory` 方法参数)让实例工厂的使用更直观。
目录
相关文章
|
4月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
4月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
576 2
|
9月前
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
|
XML Java 数据格式
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
|
XML Java 数据格式
Spring容器的本质
本文主要讨论Spring容器最核心的机制,用最少的代码讲清楚Spring容器的本质。
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
399 12
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
537 12
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
451 6
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
350 1
|
XML 安全 Java
Spring Boot中使用MapStruct进行对象映射
本文介绍如何在Spring Boot项目中使用MapStruct进行对象映射,探讨其性能高效、类型安全及易于集成等优势,并详细说明添加MapStruct依赖的步骤。
544 0