文章目錄
- 前言
- 一、創建兩個組件
- 二、使用傳統方式
- 源代碼
- 解釋
- 三、使用SpringBoot方法
- 源代碼
- 解釋
- 四、查看是否添加到組件中
- 查看
- 自定義組件名
- 配置類在容器中注冊的是單實例組件
- 配置類本身也是容器中的一個組件
- Configuration的proxyBeanMethods屬性:代理bean的方法
- `proxyBeanMethods` 參數詳解
- 詳細說明
- 1. `proxyBeanMethods = true`(默認)
- 2. `proxyBeanMethods = false`
- 選擇建議
- 總結
前言
@Configuration // 告訴 SpringBoot 這是一個配置類 == 配置文件
@Bean // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例
@Bean(“tom123”) // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例
IOC 容器(Inversion of Control Container)是實現 控制反轉 思想的一個運行期框架組件,用來集中創建、裝配、管理對象(Bean)及其生命周期。
一句話:原來由程序代碼 new 的對象,現在統一交給容器“注入”進來,代碼只聲明“我需要什么”,而不關心“怎么得到”。
- 控制反轉(IoC):對象創建與依賴綁定的“控制權”從業務代碼反轉到容器。
- 依賴注入(DI):容器把依賴對象“注入”到需要它的地方,常見方式:
- 構造函數注入
- Setter 注入
- 字段/注解注入
// 聲明組件
@Component
public class OrderService { }@RestController
public class OrderController {@Autowired // 容器注入private OrderService service;
}
啟動 Spring 后,容器掃描注解 → 創建 OrderService → 注入 OrderController,全程無 new。
一、創建兩個組件
我們先創建兩個組件User和Pet,也就是兩個類,放在bean包里面。
package com.hello.bean;/*** 用戶*/public class User { // 要把這兩個組件添加到容器中private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}// 添加帶參數的構造方法public User(String name, Integer age){this.name = name;this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";}}
package com.hello.bean;/*** 寵物*/
public class Pet {private String name;public String getName(){return name;};public void setName(String name){this.name = name;}// 添加構造器public Pet() { // 無參構造器}public Pet(String name) { // 帶參構造器this.name = name;}@Overridepublic String toString() {return "Pet{" +"name='" + name + '\'' +'}';}
}
二、使用傳統方式
要添加到容器中,首先在資源文件夾中新建beans.xml文件,然后進行組件的添加。
源代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.hello.bean.User"><property name="name" value="zhangsan"></property><property name="age" value="18"></property></bean><bean id="cat" class="com.hello.bean.Pet"><property name="name" value="tomcat"></property></bean>
</beans>
解釋
這個XML文件是一個典型的Spring框架配置文件,用于定義和配置Spring容器中的Bean。下面是對這個配置文件的詳細解釋:
-
XML聲明和命名空間:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
- 第一行是標準的XML聲明
- 定義了
beans
根元素和Spring的命名空間 - 指定了XML Schema定義文件的位置
-
User Bean定義:
<bean id="user" class="com.hello.bean.User"><property name="name" value="zhangsan"></property><property name="age" value="18"></property> </bean>
- 定義了一個ID為"user"的Bean
- 類路徑為
com.hello.bean.User
- 設置了兩個屬性:
name
屬性值為"zhangsan"age
屬性值為18
-
Pet Bean定義:
<bean id="cat" class="com.hello.bean.Pet"><property name="name" value="tomcat"></property> </bean>
- 定義了一個ID為"cat"的Bean
- 類路徑為
com.hello.bean.Pet
- 設置了
name
屬性值為"tomcat"
-
配置特點:
- 使用了setter注入方式(通過
<property>
元素) - 注入的都是簡單值(String和int)
- 沒有定義Bean之間的依賴關系
- 使用了setter注入方式(通過
-
對應的Java類結構:
假設的User類:package com.hello.bean;public class User {private String name;private int age;// 必須有setter方法public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;} }
假設的Pet類:
package com.hello.bean;public class Pet {private String name;public void setName(String name) {this.name = name;} }
-
使用場景:
- 這種配置方式在傳統的Spring XML配置中很常見
- 適用于簡單的Bean定義和屬性注入
- 在現代Spring Boot應用中,這種配置方式通常被注解配置(如
@Component
、@Configuration
)取代
注意:要使這個配置正常工作,對應的Java類必須有無參構造函數和相應的setter方法,因為這里使用的是setter注入方式。
三、使用SpringBoot方法
新建config.MyConfig類,在其中寫入代碼:
源代碼
package com.hello.config;import com.hello.bean.User;
import com.hello.bean.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration // 告訴 SpringBoot 這是一個配置類 == 配置文件
public class MyConfig {@Bean // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例public User user01() {return new User("zhangsan", 18);}@Bean // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例public Pet cat(){return new Pet("tomcat");}
}
解釋
使用 Java 配置類替代 XML 配置的 Spring Boot 配置示例,下面是對這段代碼的詳細解釋:
- 類級別注解:
@Configuration // 告訴 SpringBoot 這是一個配置類 == 配置文件
public class MyConfig {
@Configuration
注解標識這是一個 Spring 配置類,相當于 XML 配置文件- 配置類中可以定義多個
@Bean
方法,用于向 Spring 容器注冊組件
- User Bean 定義:
@Bean // 給容器中添加組件
public User user01() {return new User("zhangsan", 18);
}
@Bean
注解表示該方法會返回一個對象,該對象要注冊到 Spring 容器中- 默認情況下,Bean 的名稱(ID)就是方法名
user01
- 這里創建了一個 User 對象,使用構造器注入方式設置 name=“zhangsan” 和 age=18
- Pet Bean 定義:
@Bean // 給容器中添加組件
public Pet cat(){return new Pet("tomcat");
}
- 同樣使用
@Bean
注解,Bean 的名稱為cat
- 創建了一個 Pet 對象,設置 name=“tomcat”
-
與 XML 配置的對比:
- 等效于之前的 XML 配置,但使用了更現代的 Java 配置方式
- 不需要 setter 方法,直接使用構造器注入
- 類型安全,編譯器可以檢查類型錯誤
- 可以方便地添加邏輯(如條件判斷等)
-
對應的 Java 類結構:
假設的 User 類(需要對應的構造器):
package com.hello.bean;public class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}
}
假設的 Pet 類:
package com.hello.bean;public class Pet {private String name;public Pet(String name) {this.name = name;}
}
-
使用特點:
- 這是 Spring Boot 推薦的首選配置方式
- 配置類本身也是一個 Spring 組件(
@Configuration
是@Component
的派生注解) - 可以方便地與其他 Spring 特性(如
@Profile
,@Conditional
等)結合使用 - 支持方法間調用(Spring 會攔截確保單例)
-
獲取這些 Bean:
在應用中可以通過以下方式獲取:
// 通過類型獲取
User user = applicationContext.getBean(User.class);// 通過名稱獲取
Pet pet = (Pet) applicationContext.getBean("cat");
這種基于 Java 的配置方式比 XML 更類型安全、更靈活,是現代 Spring Boot 應用的主要配置方式。
四、查看是否添加到組件中
查看
在主程序中進行容器IOC中所有組件的輸出。
可以搜到對應的組件,說明組件已經添加成功。
自定義組件名
可以通過@Bean(“tom123”) 這樣的方式自定義組件名,這樣添加組件名稱就是不是默認的方法名。
@Bean("tom123") // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例public Pet cat(){return new Pet("tomcat");}
我們來進行組件名的輸出,點啟動項目。在控制臺觀察、搜索添加的組件。
public static void main(String[] args) {// 1、返回我們IOC容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);// 2、查看容器里面的所有組件String[] names = run.getBeanDefinitionNames();for (String name : names){System.out.println(name);}// 3、從容器中獲取組件// 拼接最終啟動成功信息String successMessage ="🎉 **啟動成功!** (ノ?ヮ?)ノ*:・゚?\n" +"? *服務已就緒,端口 8083* ?\n" +"💻 訪問地址:`http://localhost:8083`\n" +"💪 **Go! Go! Go!** (? ?_?)?";System.out.println(successMessage);}
這樣我們就給容器中注入了兩個組件。
配置類在容器中注冊的是單實例組件
package com.hello.config;import com.hello.bean.User;
import com.hello.bean.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 配置類里面使用@Bean標注在方法上給容器中添加組件,默認是單實例的*/
@Configuration // 告訴 SpringBoot 這是一個配置類 == 配置文件
public class MyConfig {@Bean // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例public User user01() {return new User("zhangsan", 18);}@Bean("tom123") // 給容器中添加組件。以方法名作為組件的id,返回類型就是組件類型。返回的值,就是組件在容器中的實例public Pet cat(){return new Pet("tomcat");}
}
我們可以來讀取組件對象進行驗證一下。
// 3、從容器中獲取組件Pet tom1 = run.getBean("tom123", Pet.class);Pet tom2 = run.getBean("tom123", Pet.class);System.out.println("兩個組件是否相同" + (tom1 == tom2));
配置類本身也是容器中的一個組件
當時一個類名上加了@Configuratio以后,這個類也會被加入到容器中,作為一個組件。
驗證:
MyConfig bean = run.getBean(MyConfig.class);System.out.println(bean);
Configuration的proxyBeanMethods屬性:代理bean的方法
應用場景:解決組件依賴問題。
* 3、proxyBeanMethods:代理bean的方法* full: 全量模式,IOC容器啟動時,會創建所有的單實例Bean(true)* light: 輕量模式,IOC容器啟動時,不會創建單實例Bean,而是當調用getBean方法時,才會創建Bean(false)
proxyBeanMethods
參數詳解
proxyBeanMethods
是 @Configuration
注解中的一個重要屬性,它決定了配置類中 @Bean
方法是否被代理。下面是它的取值含義表格:
值 | 含義 | 適用場景 | 性能影響 | 容器行為 |
---|---|---|---|---|
true (默認值) | 啟用代理,確保 @Bean 方法調用總是返回相同的實例(單例) | 需要保證單例的場景,方法間有依賴關系時 | 有輕微性能開銷(CGLIB代理) | Spring會攔截方法調用,檢查容器中是否已存在該Bean |
false | 禁用代理,直接調用方法 | 不需要方法間依賴,或追求啟動速度的場景 | 無代理開銷,啟動更快 | 每次調用方法都會真正執行,不保證單例 |
詳細說明
1. proxyBeanMethods = true
(默認)
@Configuration(proxyBeanMethods = true)
public class MyConfig {@Beanpublic User user() {return new User(pet()); // 無論調用多少次,返回的都是同一個Pet實例}@Bean public Pet pet() {return new Pet("tomcat");}
}
- 特點:方法間調用會被Spring攔截,確保單例
- 優點:保證Bean的單例性,方法間依賴安全
- 缺點:有CGLIB代理開銷
2. proxyBeanMethods = false
@Configuration(proxyBeanMethods = false)
public class MyConfig {@Beanpublic User user() {return new User(pet()); // 每次調用都會new新的Pet實例}@Bean public Pet pet() {return new Pet("tomcat");}
}
- 特點:相當于普通Java方法調用
- 優點:無代理開銷,啟動更快
- 缺點:不能保證方法間調用的單例性
選擇建議
考慮因素 | 推薦設置 |
---|---|
配置類中的@Bean 方法需要相互調用 | true |
需要嚴格的單例保證 | true |
追求應用啟動速度 | false |
配置類中沒有方法間調用 | false |
Spring Boot 2.2+ 的@Configuration 類 | 通常設為false |
在Spring Boot 2.2+中,很多場景下推薦使用proxyBeanMethods = false
以提高性能,特別是大型應用。
總結
本文全面對比了Spring Boot中兩種主要的組件配置方式:傳統XML配置和現代Java配置類方式,并深入分析了@Configuration
和@Bean
注解的核心特性。
-
配置方式演進
- XML配置是傳統Spring的經典方式,通過
<bean>
標簽定義組件,依賴setter注入 - Java配置類是現代Spring Boot推薦方式,使用
@Configuration
和@Bean
注解,更類型安全靈活
- XML配置是傳統Spring的經典方式,通過
-
注解核心功能
@Configuration
標記配置類,替代XML配置文件@Bean
注解方法向容器注冊組件,默認單例,方法名作為組件ID- 支持自定義組件名(
@Bean("name")
)和方法間依賴調用
-
高級特性
proxyBeanMethods
參數控制代理行為,平衡單例保證與性能- 配置類本身也是容器組件,可通過上下文獲取
- 與各種條件注解(
@Conditional
等)無縫集成
-
實踐建議
- 新項目優先使用Java配置類方式
- 簡單場景可設置
proxyBeanMethods=false
提升性能 - 需要嚴格單例和方法間依賴時保持默認
true
值 - 合理命名組件ID提高可讀性
現代Spring Boot應用開發中,基于注解的配置方式憑借其類型安全、代碼導航方便和與Java語言的天然契合等優勢,已成為事實標準。理解這些核心配置機制,是掌握Spring Boot自動配置原理的基礎,也是進行高效應用開發的關鍵。