Spring中Bean的三種實例化方式詳解
一、環(huá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:準備需要被創(chuàng)建的類
準備一個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:沒有一個默認的構(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失敗
因為每一個類默認都會提供一個無參構(gòu)造函數(shù),所以其實真正在使用這種方式的時候,我們什么也不需要做。這也是我們以后比較常用的一種方式。
四、靜態(tài)工廠實例化
接下來研究Spring中的第二種bean的創(chuàng)建方式靜態(tài)工廠實例化
:
4.1 工廠方式創(chuàng)建bean
在講這種方式之前,我們需要先回顧一個知識點是使用工廠來創(chuàng)建對象的方式:
(1)準備一個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的方法進行運行測試
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)境準備
(1)準備一個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的方法進行運行測試
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的配置文件中進行配置
<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(),被重寫后,在方法中進行對象的創(chuàng)建并返回
方法二:getObjectType(),被重寫后,主要返回的是被創(chuàng)建類的Class對象
方法三:沒有被重寫,因為它已經(jīng)給了默認值,從方法名中可以看出其作用是設(shè)置對象是否為單例,默認true,從意思上來看,我們猜想默認應(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)默認是單例,那如果想改成單例具體如何實現(xiàn)?
只需要將isSingleton()方法進行重寫,修改返回為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)是非單例了,但是一般情況下我們都會采用單例,也就是采用默認即可。所以isSingleton()方法一般不需要進行重寫。
六、bean實例化小結(jié)
(1)bean是如何創(chuàng)建的呢
構(gòu)造方法
(2)Spring的IOC實例化對象的三種方式分別是:
構(gòu)造方法(常用)
靜態(tài)工廠(了解)
實例工廠(了解)
FactoryBean(實用)
這些方式中,重點掌握構(gòu)造方法和FactoryBean即可。
需要注意的一點是,構(gòu)造方法在類中默認會提供,但是如果重寫了構(gòu)造方法,默認的就會消失,在使用的過程中需要注意,如果需要重寫構(gòu)造方法,最好把默認的構(gòu)造方法也重寫下。
以上就是Spring中Bean的三種實例化方式詳解的詳細內(nèi)容,更多關(guān)于Spring Bean實例化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot項目打包發(fā)布部署的過程及jar和war的區(qū)別
Spring Boot使用了內(nèi)嵌容器,因此它的部署方式也變得非常簡單靈活,可以將Spring Boot項目打包成JAR包來獨立運行,Spring Boot項目既可以生成WAR包發(fā)布,也可以生成JAR包發(fā)布,那么它們有什么區(qū)別呢2022-11-11JAVA SpringBoot統(tǒng)一日志處理原理詳解
這篇文章主要介紹了SpringBoot的統(tǒng)一日志處理原理,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09