本文已收录在[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` 方法参数)让实例工厂的使用更直观。