Spring中Bean的三種實例化方式詳解
一、環(huán)境準(zhǔn)備
準(zhǔn)備開發(fā)環(huán)境
- 創(chuàng)建一個Maven項目
- pom.xml添加依賴
- resources下添加spring的配置文件applicationContext.xml
最終項目的結(jié)構(gòu)如下:

二、構(gòu)造方法實例化
在上述的環(huán)境下,我們來研究下Spring中的第一種bean的創(chuàng)建方式構(gòu)造方法實例化:
步驟1:準(zhǔn)備需要被創(chuàng)建的類
準(zhǔn)備一個BookDao和BookDaoImpl類
public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}步驟2:將類配置到Spring容器
<?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>步驟3:編寫運行程序
public class AppForInstanceBook {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}步驟4:類中提供構(gòu)造函數(shù)測試
在BookDaoImpl類中添加一個無參構(gòu)造函數(shù),并打印一句話,方便觀察結(jié)果。
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}運行程序,如果控制臺有打印構(gòu)造函數(shù)中的輸出,說明Spring容器在創(chuàng)建對象的時候也走的是構(gòu)造函數(shù)

步驟5:將構(gòu)造函數(shù)改成private測試
public class BookDaoImpl implements BookDao {
private BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}運行程序,能執(zhí)行成功,說明內(nèi)部走的依然是構(gòu)造函數(shù),能訪問到類中的私有構(gòu)造方法,顯而易見Spring底層用的是反射

步驟6:構(gòu)造函數(shù)中添加一個參數(shù)測試
public class BookDaoImpl implements BookDao {
private BookDaoImpl(int i) {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}運行程序,程序會報錯,說明Spring底層使用的是類的無參構(gòu)造方法。

三、分析Spring的錯誤信息
接下來,我們主要研究下Spring的報錯信息
錯誤信息從下往上依次查看,因為上面的錯誤大都是對下面錯誤的一個包裝,最核心錯誤是在最下面
Caused by: java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.
<init>()
- Caused by 翻譯為引起,即出現(xiàn)錯誤的原因
- java.lang.NoSuchMethodException:拋出的異常為沒有這樣的方法異常
- com.itheima.dao.impl.BookDaoImpl.
<init>():哪個類的哪個方法沒有被找到導(dǎo)致的異常,<init>()指定是類的構(gòu)造方法,即該類的無參構(gòu)造方法
如果最后一行錯誤獲取不到錯誤信息,接下來查看第二層:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.
<init>()
nested:嵌套的意思,后面的異常內(nèi)容和最底層的異常是一致的
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found;
- Caused by: 引發(fā)
- BeanInstantiationException:翻譯為bean實例化異常
- No default constructor found:沒有一個默認(rèn)的構(gòu)造函數(shù)被發(fā)現(xiàn)
看到這其實錯誤已經(jīng)比較明顯,給大家個練習(xí),把倒數(shù)第三層的錯誤分析下吧:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bookDao' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.
<init>()。
創(chuàng)建bean異常,錯誤創(chuàng)建bean:實例化bean失敗
因為每一個類默認(rèn)都會提供一個無參構(gòu)造函數(shù),所以其實真正在使用這種方式的時候,我們什么也不需要做。這也是我們以后比較常用的一種方式。
四、靜態(tài)工廠實例化
接下來研究Spring中的第二種bean的創(chuàng)建方式靜態(tài)工廠實例化:
4.1 工廠方式創(chuàng)建bean
在講這種方式之前,我們需要先回顧一個知識點是使用工廠來創(chuàng)建對象的方式:
(1)準(zhǔn)備一個OrderDao和OrderDaoImpl類
public interface OrderDao {
public void save();
}
public class OrderDaoImpl implements OrderDao {
public void save() {
System.out.println("order dao save ...");
}
}(2)創(chuàng)建一個工廠類OrderDaoFactory并提供一個==靜態(tài)方法==
//靜態(tài)工廠創(chuàng)建對象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}(3)編寫AppForInstanceOrder運行類,在類中通過工廠獲取對象
public class AppForInstanceOrder {
public static void main(String[] args) {
//通過靜態(tài)工廠創(chuàng)建對象
OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();
}
}(4)運行后,可以查看到結(jié)果

如果代碼中對象是通過上面的這種方式來創(chuàng)建的,如何將其交給Spring來管理呢?
4.2 靜態(tài)工廠實例化
這就要用到Spring中的靜態(tài)工廠實例化的知識了,具體實現(xiàn)步驟為:
(1)在spring的配置文件application.properties中添加以下內(nèi)容:
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
class:工廠類的類全名
factory-mehod:具體工廠類中創(chuàng)建對象的方法名
對應(yīng)關(guān)系如下圖:

(2)在AppForInstanceOrder運行類,使用從IOC容器中獲取bean的方法進(jìn)行運行測試
public class AppForInstanceOrder {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}(3)運行后,可以查看到結(jié)果

看到這,可能有人會問了,你這種方式在工廠類中不也是直接new對象的,和我自己直接new沒什么太大的區(qū)別,而且靜態(tài)工廠的方式反而更復(fù)雜,這種方式的意義是什么?
主要的原因是:
在工廠的靜態(tài)方法中,我們除了new對象還可以做其他的一些業(yè)務(wù)操作,這些操作必不可少,如:
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");//模擬必要的業(yè)務(wù)操作
return new OrderDaoImpl();
}
}之前new對象的方式就無法添加其他的業(yè)務(wù)內(nèi)容,重新運行,查看結(jié)果:

介紹完靜態(tài)工廠實例化后,這種方式一般是用來兼容早期的一些老系統(tǒng),所以了解為主。
五、實例工廠與FactoryBean
接下來繼續(xù)來研究Spring的第三種bean的創(chuàng)建方式實例工廠實例化:
5.1 環(huán)境準(zhǔn)備
(1)準(zhǔn)備一個UserDao和UserDaoImpl類
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}(2)創(chuàng)建一個工廠類OrderDaoFactory并提供一個普通方法,注意此處和靜態(tài)工廠的工廠類不一樣的地方是方法不是靜態(tài)方法
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}(3)編寫AppForInstanceUser運行類,在類中通過工廠獲取對象
public class AppForInstanceUser {
public static void main(String[] args) {
//創(chuàng)建實例工廠對象
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通過實例工廠對象創(chuàng)建對象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
}(4)運行后,可以查看到結(jié)果

對于上面這種實例工廠的方式如何交給Spring管理呢?
5.2 實例工廠實例化
具體實現(xiàn)步驟為:
(1)在spring的配置文件中添加以下內(nèi)容:
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/> <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
實例化工廠運行的順序是:
創(chuàng)建實例化工廠對象,對應(yīng)的是第一行配置
調(diào)用對象中的方法來創(chuàng)建bean,對應(yīng)的是第二行配置
- factory-bean:工廠的實例對象
- factory-method:工廠對象中的具體創(chuàng)建對象的方法名,對應(yīng)關(guān)系如下:

factory-mehod:具體工廠類中創(chuàng)建對象的方法名
(2)在AppForInstanceUser運行類,使用從IOC容器中獲取bean的方法進(jìn)行運行測試
public class AppForInstanceUser {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
}
}(3)運行后,可以查看到結(jié)果

實例工廠實例化的方式就已經(jīng)介紹完了,配置的過程還是比較復(fù)雜,所以Spring為了簡化這種配置方式就提供了一種叫FactoryBean的方式來簡化開發(fā)。
5.3 FactoryBean的使用
具體的使用步驟為:
(1)創(chuàng)建一個UserDaoFactoryBean的類,實現(xiàn)FactoryBean接口,重寫接口的方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始實例工廠中創(chuàng)建對象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所創(chuàng)建類的Class對象
public Class<?> getObjectType() {
return UserDao.class;
}
}(2)在Spring的配置文件中進(jìn)行配置
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
(3)AppForInstanceUser運行類不用做任何修改,直接運行

這種方式在Spring去整合其他框架的時候會被用到。
查看源碼會發(fā)現(xiàn),F(xiàn)actoryBean接口其實會有三個方法,分別是:
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}方法一:getObject(),被重寫后,在方法中進(jìn)行對象的創(chuàng)建并返回
方法二:getObjectType(),被重寫后,主要返回的是被創(chuàng)建類的Class對象
方法三:沒有被重寫,因為它已經(jīng)給了默認(rèn)值,從方法名中可以看出其作用是設(shè)置對象是否為單例,默認(rèn)true,從意思上來看,我們猜想默認(rèn)應(yīng)該是單例,如何來驗證呢?
思路很簡單,就是從容器中獲取該對象的多個值,打印到控制臺,查看是否為同一個對象。
public class AppForInstanceUser {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDao) ctx.getBean("userDao");
UserDao userDao2 = (UserDao) ctx.getBean("userDao");
System.out.println(userDao1);
System.out.println(userDao2);
}
}打印結(jié)果,如下:

通過驗證,會發(fā)現(xiàn)默認(rèn)是單例,那如果想改成單例具體如何實現(xiàn)?
只需要將isSingleton()方法進(jìn)行重寫,修改返回為false,即可
//FactoryBean創(chuàng)建對象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始實例工廠中創(chuàng)建對象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
public boolean isSingleton() {
return false;
}
}重新運行AppForInstanceUser,查看結(jié)果

從結(jié)果中可以看出現(xiàn)在已經(jīng)是非單例了,但是一般情況下我們都會采用單例,也就是采用默認(rèn)即可。所以isSingleton()方法一般不需要進(jìn)行重寫。
六、bean實例化小結(jié)
(1)bean是如何創(chuàng)建的呢
構(gòu)造方法
(2)Spring的IOC實例化對象的三種方式分別是:
構(gòu)造方法(常用)
靜態(tài)工廠(了解)
實例工廠(了解)
FactoryBean(實用)
這些方式中,重點掌握構(gòu)造方法和FactoryBean即可。
需要注意的一點是,構(gòu)造方法在類中默認(rèn)會提供,但是如果重寫了構(gòu)造方法,默認(rèn)的就會消失,在使用的過程中需要注意,如果需要重寫構(gòu)造方法,最好把默認(rèn)的構(gòu)造方法也重寫下。
以上就是Spring中Bean的三種實例化方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Bean實例化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaWeb登錄界面登錄失敗在同一頁面進(jìn)行提示的解決
這篇文章主要介紹了JavaWeb登錄界面登錄失敗在同一頁面進(jìn)行提示的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
springboot項目打包發(fā)布部署的過程及jar和war的區(qū)別
Spring Boot使用了內(nèi)嵌容器,因此它的部署方式也變得非常簡單靈活,可以將Spring Boot項目打包成JAR包來獨立運行,Spring Boot項目既可以生成WAR包發(fā)布,也可以生成JAR包發(fā)布,那么它們有什么區(qū)別呢2022-11-11
JAVA SpringBoot統(tǒng)一日志處理原理詳解
這篇文章主要介紹了SpringBoot的統(tǒng)一日志處理原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09
對Java ArrayList的自動擴(kuò)容機(jī)制示例講解
今天小編就為大家分享一篇對Java ArrayList的自動擴(kuò)容機(jī)制示例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10

