Spring中Bean的三種實(shí)例化方式詳解
一、環(huán)境準(zhǔn)備
準(zhǔn)備開發(fā)環(huán)境
- 創(chuàng)建一個(gè)Maven項(xiàng)目
- pom.xml添加依賴
- resources下添加spring的配置文件applicationContext.xml
最終項(xiàng)目的結(jié)構(gòu)如下:
二、構(gòu)造方法實(shí)例化
在上述的環(huán)境下,我們來研究下Spring中的第一種bean的創(chuàng)建方式構(gòu)造方法實(shí)例化:
步驟1:準(zhǔn)備需要被創(chuàng)建的類
準(zhǔn)備一個(gè)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:編寫運(yùn)行程序
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è)無參構(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 ..."); } }
運(yùn)行程序,如果控制臺有打印構(gòu)造函數(shù)中的輸出,說明Spring容器在創(chuàng)建對象的時(shí)候也走的是構(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 ..."); } }
運(yùn)行程序,能執(zhí)行成功,說明內(nèi)部走的依然是構(gòu)造函數(shù),能訪問到類中的私有構(gòu)造方法,顯而易見Spring底層用的是反射
步驟6:構(gòu)造函數(shù)中添加一個(gè)參數(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 ..."); } }
運(yùn)行程序,程序會(huì)報(bào)錯(cuò),說明Spring底層使用的是類的無參構(gòu)造方法。
三、分析Spring的錯(cuò)誤信息
接下來,我們主要研究下Spring的報(bào)錯(cuò)信息
錯(cuò)誤信息從下往上依次查看,因?yàn)樯厦娴腻e(cuò)誤大都是對下面錯(cuò)誤的一個(gè)包裝,最核心錯(cuò)誤是在最下面
Caused by: java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.
<init>
()
- Caused by 翻譯為引起,即出現(xiàn)錯(cuò)誤的原因
- java.lang.NoSuchMethodException:拋出的異常為沒有這樣的方法異常
- com.itheima.dao.impl.BookDaoImpl.
<init>
():哪個(gè)類的哪個(gè)方法沒有被找到導(dǎo)致的異常,<init>
()指定是類的構(gòu)造方法,即該類的無參構(gòu)造方法
如果最后一行錯(cuò)誤獲取不到錯(cuò)誤信息,接下來查看第二層:
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實(shí)例化異常
- No default constructor found:沒有一個(gè)默認(rèn)的構(gòu)造函數(shù)被發(fā)現(xiàn)
看到這其實(shí)錯(cuò)誤已經(jīng)比較明顯,給大家個(gè)練習(xí),把倒數(shù)第三層的錯(cuò)誤分析下吧:
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異常,錯(cuò)誤創(chuàng)建bean:實(shí)例化bean失敗
因?yàn)槊恳粋€(gè)類默認(rèn)都會(huì)提供一個(gè)無參構(gòu)造函數(shù),所以其實(shí)真正在使用這種方式的時(shí)候,我們什么也不需要做。這也是我們以后比較常用的一種方式。
四、靜態(tài)工廠實(shí)例化
接下來研究Spring中的第二種bean的創(chuàng)建方式靜態(tài)工廠實(shí)例化
:
4.1 工廠方式創(chuàng)建bean
在講這種方式之前,我們需要先回顧一個(gè)知識點(diǎn)是使用工廠來創(chuàng)建對象的方式:
(1)準(zhǔn)備一個(gè)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)建一個(gè)工廠類OrderDaoFactory并提供一個(gè)==靜態(tài)方法==
//靜態(tài)工廠創(chuàng)建對象 public class OrderDaoFactory { public static OrderDao getOrderDao(){ return new OrderDaoImpl(); } }
(3)編寫AppForInstanceOrder運(yùn)行類,在類中通過工廠獲取對象
public class AppForInstanceOrder { public static void main(String[] args) { //通過靜態(tài)工廠創(chuàng)建對象 OrderDao orderDao = OrderDaoFactory.getOrderDao(); orderDao.save(); } }
(4)運(yùn)行后,可以查看到結(jié)果
如果代碼中對象是通過上面的這種方式來創(chuàng)建的,如何將其交給Spring來管理呢?
4.2 靜態(tài)工廠實(shí)例化
這就要用到Spring中的靜態(tài)工廠實(shí)例化的知識了,具體實(shí)現(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運(yùn)行類,使用從IOC容器中獲取bean的方法進(jìn)行運(yù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)運(yùn)行后,可以查看到結(jié)果
看到這,可能有人會(huì)問了,你這種方式在工廠類中不也是直接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)容,重新運(yùn)行,查看結(jié)果:
介紹完靜態(tài)工廠實(shí)例化后,這種方式一般是用來兼容早期的一些老系統(tǒng),所以了解為主。
五、實(shí)例工廠與FactoryBean
接下來繼續(xù)來研究Spring的第三種bean的創(chuàng)建方式實(shí)例工廠實(shí)例化:
5.1 環(huán)境準(zhǔn)備
(1)準(zhǔn)備一個(gè)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)建一個(gè)工廠類OrderDaoFactory并提供一個(gè)普通方法,注意此處和靜態(tài)工廠的工廠類不一樣的地方是方法不是靜態(tài)方法
public class UserDaoFactory { public UserDao getUserDao(){ return new UserDaoImpl(); } }
(3)編寫AppForInstanceUser運(yùn)行類,在類中通過工廠獲取對象
public class AppForInstanceUser { public static void main(String[] args) { //創(chuàng)建實(shí)例工廠對象 UserDaoFactory userDaoFactory = new UserDaoFactory(); //通過實(shí)例工廠對象創(chuàng)建對象 UserDao userDao = userDaoFactory.getUserDao(); userDao.save(); }
(4)運(yùn)行后,可以查看到結(jié)果
對于上面這種實(shí)例工廠的方式如何交給Spring管理呢?
5.2 實(shí)例工廠實(shí)例化
具體實(shí)現(xiàn)步驟為:
(1)在spring的配置文件中添加以下內(nèi)容:
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/> <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
實(shí)例化工廠運(yùn)行的順序是:
創(chuàng)建實(shí)例化工廠對象,對應(yīng)的是第一行配置
調(diào)用對象中的方法來創(chuàng)建bean,對應(yīng)的是第二行配置
- factory-bean:工廠的實(shí)例對象
- factory-method:工廠對象中的具體創(chuàng)建對象的方法名,對應(yīng)關(guān)系如下:
factory-mehod:具體工廠類中創(chuàng)建對象的方法名
(2)在AppForInstanceUser運(yùn)行類,使用從IOC容器中獲取bean的方法進(jìn)行運(yù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)運(yùn)行后,可以查看到結(jié)果
實(shí)例工廠實(shí)例化的方式就已經(jīng)介紹完了,配置的過程還是比較復(fù)雜,所以Spring為了簡化這種配置方式就提供了一種叫FactoryBean
的方式來簡化開發(fā)。
5.3 FactoryBean的使用
具體的使用步驟為:
(1)創(chuàng)建一個(gè)UserDaoFactoryBean的類,實(shí)現(xiàn)FactoryBean接口,重寫接口的方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> { //代替原始實(shí)例工廠中創(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運(yùn)行類不用做任何修改,直接運(yùn)行
這種方式在Spring去整合其他框架的時(shí)候會(huì)被用到。
查看源碼會(huì)發(fā)現(xiàn),F(xiàn)actoryBean接口其實(shí)會(huì)有三個(gè)方法,分別是:
T getObject() throws Exception; Class<?> getObjectType(); default boolean isSingleton() { return true; }
方法一:getObject(),被重寫后,在方法中進(jìn)行對象的創(chuàng)建并返回
方法二:getObjectType(),被重寫后,主要返回的是被創(chuàng)建類的Class對象
方法三:沒有被重寫,因?yàn)樗呀?jīng)給了默認(rèn)值,從方法名中可以看出其作用是設(shè)置對象是否為單例,默認(rèn)true,從意思上來看,我們猜想默認(rèn)應(yīng)該是單例,如何來驗(yàn)證呢?
思路很簡單,就是從容器中獲取該對象的多個(gè)值,打印到控制臺,查看是否為同一個(gè)對象。
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é)果,如下:
通過驗(yàn)證,會(huì)發(fā)現(xiàn)默認(rèn)是單例,那如果想改成單例具體如何實(shí)現(xiàn)?
只需要將isSingleton()方法進(jìn)行重寫,修改返回為false,即可
//FactoryBean創(chuàng)建對象 public class UserDaoFactoryBean implements FactoryBean<UserDao> { //代替原始實(shí)例工廠中創(chuàng)建對象的方法 public UserDao getObject() throws Exception { return new UserDaoImpl(); } public Class<?> getObjectType() { return UserDao.class; } public boolean isSingleton() { return false; } }
重新運(yùn)行AppForInstanceUser,查看結(jié)果
從結(jié)果中可以看出現(xiàn)在已經(jīng)是非單例了,但是一般情況下我們都會(huì)采用單例,也就是采用默認(rèn)即可。所以isSingleton()方法一般不需要進(jìn)行重寫。
六、bean實(shí)例化小結(jié)
(1)bean是如何創(chuàng)建的呢
構(gòu)造方法
(2)Spring的IOC實(shí)例化對象的三種方式分別是:
構(gòu)造方法(常用)
靜態(tài)工廠(了解)
實(shí)例工廠(了解)
FactoryBean(實(shí)用)
這些方式中,重點(diǎn)掌握構(gòu)造方法和FactoryBean即可。
需要注意的一點(diǎn)是,構(gòu)造方法在類中默認(rèn)會(huì)提供,但是如果重寫了構(gòu)造方法,默認(rèn)的就會(huì)消失,在使用的過程中需要注意,如果需要重寫構(gòu)造方法,最好把默認(rèn)的構(gòu)造方法也重寫下。
以上就是Spring中Bean的三種實(shí)例化方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Bean實(shí)例化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaWeb登錄界面登錄失敗在同一頁面進(jìn)行提示的解決
這篇文章主要介紹了JavaWeb登錄界面登錄失敗在同一頁面進(jìn)行提示的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11springboot項(xiàng)目打包發(fā)布部署的過程及jar和war的區(qū)別
Spring Boot使用了內(nèi)嵌容器,因此它的部署方式也變得非常簡單靈活,可以將Spring Boot項(xiàng)目打包成JAR包來獨(dú)立運(yùn)行,Spring Boot項(xiàng)目既可以生成WAR包發(fā)布,也可以生成JAR包發(fā)布,那么它們有什么區(qū)別呢2022-11-11JAVA SpringBoot統(tǒng)一日志處理原理詳解
這篇文章主要介紹了SpringBoot的統(tǒng)一日志處理原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09對Java ArrayList的自動(dòng)擴(kuò)容機(jī)制示例講解
今天小編就為大家分享一篇對Java ArrayList的自動(dòng)擴(kuò)容機(jī)制示例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10java實(shí)現(xiàn)會(huì)反彈的小球示例
這篇文章主要介紹了java實(shí)現(xiàn)會(huì)反彈的小球示例,需要的朋友可以參考下2014-04-04解決springboot啟動(dòng)報(bào)錯(cuò)bean找不到的問題
這篇文章主要介紹了解決springboot啟動(dòng)報(bào)錯(cuò)bean找不到原因,本文給大家分享完美解決方案,通過圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03