一、環境準備
準備開發環境
-
建立一個Maven專案
-
pom.xml新增依賴
-
resources下新增spring的配置檔案applicationContext.xml
最終專案的結構如下:
二、構造方法例項化
構造方法例項化
:
步驟1:準備需要被建立的類
準備一個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:類中提供建構函式測試
在BookDaoImpl類中新增一個無參建構函式,並列印一句話,方便觀察結果。
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}
執行程式,如果控制檯有列印建構函式中的輸出,說明Spring容器在建立物件的時候也走的是建構函式
步驟5:將建構函式改成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 ...");
}
}
執行程式,能執行成功,說明內部走的依然是建構函式,能訪問到類中的私有構造方法,顯而易見Spring底層用的是反射
步驟6:建構函式中新增一個引數測試
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底層使用的是類的無參構造方法。
三、分析Spring的錯誤資訊
接下來,我們主要研究下Spring的報錯資訊
-
錯誤資訊從下往上依次檢視,因為上面的錯誤大都是對下面錯誤的一個包裝,最核心錯誤是在最下面
-
Caused by: java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.
<init>
()-
Caused by 翻譯為
引起
,即出現錯誤的原因 -
java.lang.NoSuchMethodException:丟擲的異常為
沒有這樣的方法異常
-
com.itheima.dao.impl.BookDaoImpl.
<init>
():哪個類的哪個方法沒有被找到導致的異常,<init>
()指定是類的構造方法,即該類的無參構造方法
-
如果最後一行錯誤獲取不到錯誤資訊,接下來檢視第二層:
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:巢狀的意思,後面的異常內容和最底層的異常是一致的
-
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found;
-
Caused by:
引發
-
BeanInstantiationException:翻譯為
bean例項化異常
-
No default constructor found:沒有一個預設的建構函式被發現
-
看到這其實錯誤已經比較明顯,給大家個練習,把倒數第三層的錯誤分析下吧:
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>
()。
建立bean異常,錯誤建立bean:例項化bean失敗
因為每一個類預設都會提供一個無參建構函式,所以其實真正在使用這種方式的時候,我們什麼也不需要做。這也是我們以後比較常用的一種方式。
四、靜態工廠例項化
接下來研究Spring中的第二種bean的建立方式靜態工廠例項化
:
4.1 工廠方式建立bean
在講這種方式之前,我們需要先回顧一個知識點是使用工廠來建立物件的方式:
(1)準備一個OrderDao和OrderDaoImpl類
public interface OrderDao {
public void save();
}
public class OrderDaoImpl implements OrderDao {
public void save() {
System.out.println("order dao save ...");
}
}
(2)建立一個工廠類OrderDaoFactory並提供一個==靜態方法==
//靜態工廠建立物件
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
(3)編寫AppForInstanceOrder執行類,在類中通過工廠獲取物件
public class AppForInstanceOrder {
public static void main(String[] args) {
//通過靜態工廠建立物件
OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();
}
}
(4)執行後,可以檢視到結果
如果程式碼中物件是通過上面的這種方式來建立的,如何將其交給Spring來管理呢?
4.2 靜態工廠例項化
這就要用到Spring中的靜態工廠例項化的知識了,具體實現步驟為:
(1)在spring的配置檔案application.properties中新增以下內容:
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
class:工廠類的類全名
factory-mehod:具體工廠類中建立物件的方法名
對應關係如下圖:
(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)執行後,可以檢視到結果
看到這,可能有人會問了,你這種方式在工廠類中不也是直接new物件的,和我自己直接new沒什麼太大的區別,而且靜態工廠的方式反而更復雜,這種方式的意義是什麼?
主要的原因是:
-
在工廠的靜態方法中,我們除了new物件還可以做其他的一些業務操作,這些操作必不可少,如:
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");//模擬必要的業務操作
return new OrderDaoImpl();
}
}
之前new物件的方式就無法新增其他的業務內容,重新執行,檢視結果:
介紹完靜態工廠例項化後,這種方式一般是用來相容早期的一些老系統,所以瞭解為主。
五、例項工廠與FactoryBean
接下來繼續來研究Spring的第三種bean的建立方式例項工廠例項化
:
5.1 環境準備
(1)準備一個UserDao和UserDaoImpl類
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
(2)建立一個工廠類OrderDaoFactory並提供一個普通方法,注意此處和靜態工廠的工廠類不一樣的地方是方法不是靜態方法
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
(3)編寫AppForInstanceUser執行類,在類中通過工廠獲取物件
public class AppForInstanceUser {
public static void main(String[] args) {
//建立例項工廠物件
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通過例項工廠物件建立物件
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
}
(4)執行後,可以檢視到結果
對於上面這種例項工廠的方式如何交給Spring管理呢?
5.2 例項工廠例項化
具體實現步驟為:
(1)在spring的配置檔案中新增以下內容:
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
例項化工廠執行的順序是:
-
建立例項化工廠物件,對應的是第一行配置
-
呼叫物件中的方法來建立bean,對應的是第二行配置
-
factory-bean:工廠的例項物件
-
factory-method:工廠物件中的具體建立物件的方法名,對應關係如下:
-
factory-mehod:具體工廠類中建立物件的方法名
(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)執行後,可以檢視到結果
例項工廠例項化的方式就已經介紹完了,配置的過程還是比較複雜,所以Spring為了簡化這種配置方式就提供了一種叫FactoryBean
的方式來簡化開發。
5.3 FactoryBean的使用
具體的使用步驟為:
(1)建立一個UserDaoFactoryBean的類,實現FactoryBean介面,重寫介面的方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始例項工廠中建立物件的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所建立類的Class物件
public Class<?> getObjectType() {
return UserDao.class;
}
}
(2)在Spring的配置檔案中進行配置
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
(3)AppForInstanceUser執行類不用做任何修改,直接執行
這種方式在Spring去整合其他框架的時候會被用到。
檢視原始碼會發現,FactoryBean介面其實會有三個方法,分別是:
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
方法一:getObject(),被重寫後,在方法中進行物件的建立並返回
方法二:getObjectType(),被重寫後,主要返回的是被建立類的Class物件
方法三:沒有被重寫,因為它已經給了預設值,從方法名中可以看出其作用是設定物件是否為單例,預設true,從意思上來看,我們猜想預設應該是單例,如何來驗證呢?
思路很簡單,就是從容器中獲取該物件的多個值,列印到控制檯,檢視是否為同一個物件。
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);
}
}
列印結果,如下:
通過驗證,會發現預設是單例,那如果想改成單例具體如何實現?
只需要將isSingleton()方法進行重寫,修改返回為false,即可
//FactoryBean建立物件
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始例項工廠中建立物件的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
public boolean isSingleton() {
return false;
}
}
重新執行AppForInstanceUser,檢視結果
從結果中可以看出現在已經是非單例了,但是一般情況下我們都會採用單例,也就是採用預設即可。所以isSingleton()方法一般不需要進行重寫。
六、bean例項化小結
(1)bean是如何建立的呢
構造方法
(2)Spring的IOC例項化物件的三種方式分別是:
-
構造方法(常用)
-
靜態工廠(瞭解)
-
例項工廠(瞭解)
-
FactoryBean(實用)
-
這些方式中,重點掌握構造方法
和FactoryBean
即可。
需要注意的一點是,構造方法在類中預設會提供,但是如果重寫了構造方法,預設的就會消失,在使用的過程中需要注意,如果需要重寫構造方法,最好把預設的構造方法也重寫下。