Java匹馬行天下之J2EE框架開發——Spring—>用IDEA開發Spring程式(01)
一、心動不如行動
一、建立專案
*注:在IDEA中我建立的Maven專案,不瞭解Maven的朋友可以看我之前的部落格“我們一起走進Maven——知己知彼”,瞭解Maven後可以看我之前的部落格“Maven的安裝與配置”,自行安裝,行動起來吧。
二、載入依賴
在pom.xml檔案中新增Spring依賴和日誌相關依賴
<dependencies> <!--測試相關--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--Spring核心基礎依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!--日誌相關--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
三、專案結構
在main目錄下面建立java和resources目錄
四、基礎程式碼實現(練手)
- 建立TestDao介面和實現類
- 獲得TestDao實現類的例項
- 在之前開發中,我們是直接new一個物件即可。即:`private TestDao dao = new TestDaoImpl();`
- 在學習Spring之後,將由Spring來建立物件的例項 --> 即:`IoC 控制反轉(Inverse of Control)`
建立dao包,在dao包下建立TestDao介面和TestDao介面的實現類,結構如下圖:
TestDao介面程式碼示例:
package dao; public interface TestDao { public void sayHello(); }
TestDaoImpl實現類程式碼示例:
package dao; public class TestDaoImpl implements TestDao{ @Override public void sayHello() { System.out.println("Hello,Spring!"); } }
在resources資源目錄點選右鍵,依次選擇New-->XML Configuration File-->Spring Config,建立applicationContext.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"> <!--把testDao物件的建立權交給Spring-->
<!-- <bean> 配置需要建立的物件
id :用於之後從Spring容器中獲得例項時使用的
class :需要建立例項的全限定類名-->
<bean id="testDao" class="dao.TestDaoImpl"></bean> </beans>
建立test包,在test包下建立測試類SpringTest
package test;
import dao.TestDao; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTest {
@Test
public void demo1(){
// 之前開發,自己手寫new出物件
TestDao dao= new TestDaoImpl();
dao.sayHello();
}
@Test
public void demo2() {
// 現在從spring容器中獲得物件例項
// 1 、獲得容器
//初始化Spring容器ApplicationContext,載入配置檔案
ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得
//通過容器獲取testDao例項
TestDao testDao = (TestDao) application.getBean("testDao");
testDao.sayHello();
}
}
- IoC(Inverse of Control)反轉控制的概念,就是將原本在程式中手動建立TestDaoImpl物件的控制權,交由Spring框架管理。
- 簡單說,就是建立TestDaoImpl物件的控制權被反轉到了Spring框架。
點選測試方法左側的執行按鈕,選擇Run,測試程式碼
執行後控制檯顯示結果
專案執行成功!!!!!!!
五、Spring入門案例:DI(掌握)
- DI :Dependency Injection :依賴注入
- is a :是一個,繼承。
- has a:有一個,成員變數,依賴。
class B { private A a; // B類依賴A類,B類使用A類。 } 依賴:一個物件需要使用另一個物件。 注入:通過setter方法進行另一個物件例項設定。
例如:
class BookServiceImpl { // 之前開發:介面 = 實現類(service和dao耦合了,寫死了,知道具體的實現類是誰,那麼我的具體實現類變化,那麼這行程式碼也得跟著變) // private BookDao bookDao = new BookDaoImpl(); // spring之後(解耦:service實現類使用了dao的介面,這樣就不知道具體的實現類是誰了) private BookDao bookDao; setter方法 } 模擬spring執行過程 建立service例項:BookService bookService = new BookServiceImpl(); => IoC <bean> 建立dao例項:BookDao bookDao = new BookDaoImple(); => IoC 將dao設定給service:bookService.setBookDao(bookDao); => DI <property>
具體程式碼實現:
實現步驟:
- 建立BookDao介面和實現類
- 建立BookService介面和實現類
- 將dao和service配置到 applicationContext.xml檔案中
- 使用api測試
專案結構:
載入依賴:
在pom.xml檔案中新增Spring依賴和日誌相關依賴
<dependencies> <!--測試相關--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--Spring核心基礎依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!--日誌相關--> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
BookDao介面和實現類
package dao; public interface BookDao { void save(); }
package dao; public class BookDaoImpl implements BookDao { @Override public void save() { System.out.println("實現新增功能"); } }
BookService介面和實現類
package Service; public interface BookService { void addBook(); }
package Service; import dao.BookDao; import dao.BookDaoImpl; public class BookServiceImpl implements BookService { //方式一:之前,介面=實現類 //private BookDao bookDao= new BookDaoImpl(); //方式二:現在,介面+setter方法 private BookDao bookDao; public void setBookDao(BookDao bookDao){ this.bookDao=bookDao; } @Override public void addBook() { this.bookDao.save(); } }
將dao和service配置到 applicationContext.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 :用於之後從Spring容器中獲得例項時使用的 class :需要建立例項的全限定類名--> <!-- 模擬spring執行過程 建立service例項:BookService bookService = new BookServiceImpl(); => IoC <bean> 建立dao例項:BookDao bookDao = new BookDaoImple(); => IoC 將dao例項設定給service例項:bookService.setBookDao(bookDao); => DI <property> <property> 用於進行屬性注入 name : Bean的屬性名稱,通過setter方法獲得 setBookDao => BookDao => bookDao ref :另一個Bean的id值的引用 --> <!-- 建立service例項 --> <bean id="bookServiceId" class="Service.BookServiceImpl"> <!-- 將dao例項設定給service例項 --> <property name="bookDao" ref="bookDaoId"></property> <!-- 用於進行屬性注入 --> </bean> <!-- 建立dao例項 --> <bean id="bookDaoId" class="dao.BookDaoImpl"></bean> </beans>
使用api測試
建立test包,在test包下建立測試類SpringTest
package test; import Service.BookService; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTest { @Test public void Test1(){ // 之前開發,自己手寫new出物件 // BookService bookService = new BookServiceImpl(); // bookService.addBook(); } @Test public void Test2(){ // 現在從spring容器中獲得物件例項 // 1 、獲得容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得 BookService bookService = (BookService) applicationContext.getBean("bookServiceId"); bookService.addBook(); } }
- DI:Dependency Injection 依賴注入,在Spring框架負責建立Bean物件時,動態的將依賴物件注入到Bean元件。
- getBean("bookServiceId"); 從spring容器中獲得指定名稱物件的例項時,會先判斷本例項物件是否需要使用其他例項化物件,由於設定了< property name="bookDao" ref="bookDaoId">< /property>,說明需要使用其他例項化物件,所以就根據其他Bean的id值的引用,去spring容器中獲得指定名稱物件的例項,相當於將dao例項設定給service例項。
執行後控制檯顯示結果
這就成功了,開心一下,通過這兩個案例,多Spring有了初步的理解,加油!
六、Spring的核心API(瞭解)
api整體瞭解即可,之後不使用,在學習過程需要。
- BeanFactory :這是一個
工廠
,用於生成任意Bean。採取延遲載入,第一次呼叫getBean(); 時才會初始化Bean。(即例項化物件)
- ApplicationContext :是BeanFactory的子介面,功能更強大。(國際化處理、事件傳遞、Bean自動裝配、各種不同應用層的Context實現)。
採取非延時載入,當配置檔案被載入時,就進行物件的例項化。
- ClassPathXmlApplicationContext 用於載入classpath(類路徑/src)下的xml
- 載入xml執行時位置 --> /WEB-INF/classes/xxx.xml
- FileSystemXmlApplicationContext 用於載入指定碟符下的xml
- 載入xml執行時位置 --> /WEB-INF/xxx.xml
- 通過java web學習過的 ServletContext.getRealPath(); 獲得具體碟符
- ClassPathXmlApplicationContext 用於載入classpath(類路徑/src)下的xml
示例程式碼如下:
package test; import Service.BookService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; public class SpringTest { @Test public void Test1(){ // 之前開發,自己手寫new出物件 // BookService bookService = new BookServiceImpl(); // bookService.addBook(); } @Test public void Test2(){ // 現在從spring容器中獲得物件例項 // 1 、獲得容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 採取非延時載入,當配置檔案被載入時,就進行物件的例項化。 // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得 BookService bookService = (BookService) applicationContext.getBean("bookServiceId"); bookService.addBook(); } @Test public void demo3() { // 現在從spring容器中獲得物件例項,使用的是BeanFactory,裡面需要一個Resource,該Resource又是一個介面,需要找它的實現類ClassPathResource // 1 、獲得容器 BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得 BookService bookService = (BookService) beanFactory.getBean("bookServiceId"); // 採取延遲載入,第一次呼叫getBean(); 時才會初始化Bean(即例項化物件)。 bookService.addBook(); } }
七、裝配Bean:基於XML
3種bean例項化方式:
- 使用`預設構造方法`例項化
- 使用`靜態工廠方法`例項化
- 使用`例項工廠方法`例項化
使用預設構造方法例項化
格式:
<bean id="從Spring容器中獲得例項時使用的" class="需要建立例項的全限定類名"></bean> 例如:<bean id="userServiceId" class="Service.UserServiceImpl"></bean>
示例中用到的 UserService.java 和 UserServiceImpl.java 程式碼同上面例項中的程式碼,這裡不再贅述!
在spring容器中進行配置:
applicationContext.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 的方式 :使用預設構造方法例項化,即要例項化的Bean必須要提供預設構造方法 -->
<bean id="userServiceId" class="Service.UserServiceImpl"></bean>
</beans>
測試程式碼:
public class TestIoC { @Test public void demo01() { // 之前開發,自己手寫new出物件 UserService userService = new UserServiceImpl(); // 直接手動建立例項 userService.addUser(); } @Test public void demo02() { // 現在從spring容器中獲得物件例項 // 1 、獲得容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得 UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); } }
使用`靜態工廠方法`例項化
- 靜態工廠:常用與spring整合其他框架(工具)時。
- 靜態工廠:用於生成例項物件,所有的方法必須是static。
示例中用到的 UserService.java 和 UserServiceImpl.java 程式碼同上面例項中的程式碼,這裡不再贅述!
格式:
<bean id="" class="工廠全限定類名" factory-method="靜態方法名稱">
在spring容器中進行配置:
applicationContext.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 的方式 :使用靜態工廠方法例項化
將自定義的靜態工廠建立的例項交給spring管理
class 自定義靜態工廠全限定類名
factory-method 靜態方法名
-->
<bean id="userServiceId" class="Service.MyBeanFactory" factory-method="createService"></bean> </beans>
靜態工廠類程式碼:
public class MyBeanFactory { /** * 建立例項的靜態工廠,所有的方法必須是靜態的(static)。 * * @return */ public static UserService createService() { return new UserServiceImpl(); } // 還有建立其他例項的靜態工廠 // ...... }
測試程式碼:
TestStaticFactory.java
/** * 第二種例項化Bean 的方式 :使用靜態工廠方法例項化 * */ public class TestStaticFactory { @Test public void demo01() { // 以前:使用自定義的靜態例項工廠 UserService userService = MyBeanFactory.createService(); userService.addUser(); } @Test public void demo02() { // 現在:使用spring 工廠:將自定義的靜態工廠建立的例項交給spring管理
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換
// UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
}
}
注意:當使用JDK版本為1.8時,執行上面的測試程式碼會出現一個問題: java.lang.IllegalArgumentException,
問題解決連結:使用Junit測試一個 spring靜態工廠例項化bean 的例子,所有程式碼都沒有問題,但是出現java.lang.IllegalArgumentException異常
小結:在以後的開發中,工廠類不需要我們去手寫,因為別人已經寫好了,我們通過編寫配置檔案
,把別人寫好的工廠類拿來,寫上要用的方法名,然後把它生產後的例項給Spring存起來,以後我們要用什麼例項,跟Spring說一下,去拿就可以了。
使用`例項工廠方法`例項化
例項工廠:必須先有工廠的例項物件,然後通過例項物件去建立物件。特點:提供所有的方法都是“非靜態”的。
示例中用到的 UserService.java 和 UserServiceImpl.java 程式碼同上面例項中的程式碼,這裡不再贅述!
在spring容器中進行配置:
applicationContext.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 的方式 :使用例項工廠方法例項化 -->
<!--建立工廠例項 -->
<bean id="myBeanFactoryId" class="com.itheima.c_inject.c_factory.MyBeanFactory" ></bean>
<!--通過工廠例項,獲得物件 factory-bean 工廠例項名稱 factory-method 普通方法名稱 -->
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"></bean> </beans>
靜態工廠類程式碼:
public class MyBeanFactory { /** * 建立例項的工廠,所有方法非靜態 * * @return */ public UserService createService() { return new UserServiceImpl(); } // 還有建立其他例項的工廠 // ...... }
測試程式碼:
TestFactory.java
package test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 第三種例項化Bean 的方式 :使用例項工廠方法例項化 * */ public class TestFactory { @Test public void demo01() { // 以前:使用自定義的例項工廠 // 1、建立工廠例項 MyBeanFactory myBeanFactory = new MyBeanFactory(); // 2、通過工廠例項,獲得物件 UserService userService = myBeanFactory.createService(); userService.addUser(); } @Test public void demo02() { // 現在:使用spring 工廠:將自定義的例項工廠建立的例項交給spring管理
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換
// UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
}
}
-
一、Bean的種類
-
普通bean :之前操作的都是普通bean。例如:
< bean id="xxx" class="A" >
,這句程式碼的意思是:Spring直接建立A的例項,並返回。 -
FactoryBean :是一個特殊的bean,
具有工廠生成物件能力
,但是隻能生成特定的物件。
想要生產物件的bean 必須實現FactoryBean 介面,此介面提供方法getObject(); 用於獲得特定bean。- 示例:
< bean id="xxx" class="FB">
,這句程式碼的意思是:Spring會先建立FB例項,然後呼叫getObject(); 方法,並返回方法的返回值。
即相當於如下兩行程式碼:
FB fb = new FB();
return fb.getObject();
- 示例:
-
BeanFactory 和 FactoryBean 對比?
- BeanFactory :是一個生產bean的工廠,用於生成任意的bean。
- FactoryBean :是一個特殊的bean,用於生成另一個特定的bean。
- 例如:ProxyFactoryBean ,此工廠bean用於生產代理物件的例項。
< bean id="xxx" class="….ProxyFactoryBean">
,這句程式碼的意思是:獲得代理物件的例項。即AOP使用。
- 例如:ProxyFactoryBean ,此工廠bean用於生產代理物件的例項。
-
spring容器中bean元素id和name屬性的區別?
-
在spring容器中新增以下配置:
示例:< bean id="userServiceId" class="com.itheima.a_ioc.UserServiceImpl">
-
bean節點中id和name的區別:
-
區別一:
- id : 指定唯一例項引用
- name : 可以指定多個例項引用,例如name="名稱1, 名稱2"
-
區別二:
-
id :id的命名要滿足XML對ID屬性命名規範
例如:必須以字母開始,可以使用字母、數字、連字元、下劃線、句話、冒號 -
name : 如果Bean的名稱中含有特殊字元,就需要使用name屬性
例如 :< bean name="# boy" class="cn.itheima.ioc.Boy"/>
-
因為name屬性可以相同,所以後出現Bean會覆蓋之前出現的同名的Bean。
-
總結:專案開發的時候,強烈要求用id,因為id可以表示唯一引用。
-
-
-
二、Bean的作用域
Bean 的作用域:用於確定spring所建立bean 的例項個數。
- 取值:
- singleton 單例,預設值。
- prototype 多例,每執行一次getBean() 將獲得一個例項。例如:在struts整合spring時,需要配置action為多例。
- 配置示例:預設情況下會在容器啟動時初始化bean,但我們可以指定Bean節點的
lazy-init="true"
來延遲初始化bean,這時候,只有第一次獲取bean會才初始化bean。- 例如:`< bean id="xxx" class="xxx" scope="xxx">`
例如:< bean id="xxx" class="service.UserServiceImpl" lazy-init="true">
- 如果想對所有bean都應用延遲初始化,可以在根節點beans設定
default-lazy-init="true"
,
例如:< beans default-lazy-init="true“>
- Portlet是基於java的web元件,由portlet容器管理,並由容器處理請求,生產動態內容。
- Portals使用portlets作為可插拔使用者介面元件,提供資訊系統的表示層。
- 作為利用servlets進行web應用程式設計的下一步,portlets實現了web應用的模組化和使用者中心化。
在spring容器中進行配置:
applicationContext.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="userServiceId" class="com.itheima.d_scope.UserServiceImpl" scope="prototype"></bean> </beans>
測試程式碼:
TestScope.java
public class TestScope { @Test public void demo01() { // 現在:使用spring 工廠
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = applicationContext.getBean("userServiceId", UserService.class);
// 這種方式底層會自動轉換
UserService userService2 = applicationContext.getBean("userServiceId", UserService.class);
// 這種方式底層會自動轉換
// 預設Bean的作用域是單例,所以列印的物件的地址是一樣的
// System.out.println(userService1); // service.UserServiceImpl@2ac273d3
// System.out.println(userService2); // service.UserServiceImpl@2ac273d3
// 現在在配置檔案中新增scope屬性,值為prototype,此時Bean的作用域變為多例了,再次列印,輸出地址不一樣了
System.out.println(userService1);
//service.UserServiceImpl@66480dd7
System.out.println(userService2);
//service.UserServiceImpl@52a86356
}
}
-
三、Bean的生命週期
Bean 的生命週期詳情
- instantiate bean 物件例項化。
- populate properties 封裝屬性。
- 如果Bean實現 BeanNameAware,則表示執行
setBeanName
。 - 如果Bean實現 BeanFactoryAware 或者 ApplicationContextAware,則表示設定例項工廠(
setBeanFactory
)或者上下文物件(setApplicationContext
)。 - 如果存在類實現 BeanPostProcessor(後處理Bean),則表示執行
postProcessBeforeInitialization
。 - 如果Bean實現 InitializingBean,則表示執行
afterPropertiesSet
。 - 呼叫
,則表示指定初始化方法
init
。 - 如果存在類實現 BeanPostProcessor(處理Bean),則表示執行
postProcessAfterInitialization
。 - 執行業務處理
- 如果Bean實現 DisposableBean,則表示執行
destroy
。 - 呼叫
,則表示指定銷燬方法
customerDestroy
。
-
四、Bean 的初始化和銷燬
目標方法執行前和執行後,將進行Bean的初始化或銷燬。
示例:<bean id="xxx" class="xxx" init-method="初始化的方法名稱" destroy-method="銷燬的方法名稱"></bean>
示例程式碼如下:
編寫目標類程式碼:
UserService.java
public interface UserService { void addUser(); }
UserServiceImpl.java
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("e_lifecycle add user"); } public void myInit() { System.out.println("我的初始化方法"); } public void myDestory() { System.out.println("我的銷燬方法"); } }
在spring容器中進行配置:
applicationContext.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"> <!--
init-method 用於配置初始化方法,用於準備資料等使用場景
destroy-method 用於配置銷燬方法,用於清理資源等使用場景
-->
<bean id="userServiceId" class="service.UserServiceImpl"
init-method="myInit" destroy-method="myDestory"></bean> </beans>
編寫測試程式碼:
public class TestLifecycle { @Test public void demo01() throws Exception { // 現在:使用spring 工廠(spring 容器)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser(); // 要想使我的銷燬方法也執行,必須要求:
// 1.容器必須先close,我的銷燬方法才會執行;
// 2.必須是單例的(spring所建立該bean的例項個數只有一個)即bean中的scope配置成預設即可。
// 因為此close方法在介面 ApplicationContext 中沒有定義,而在實現類中提供了該方法,我們可以使用反射,因為反射最後執行的就是實現類中的方法。
applicationContext.getClass().getMethod("close").invoke(applicationContext); } }
-
五、BeanPostProcessor 後處理Bean
- 是由spring提供的一種機制,只要實現類實現此介面BeanPostProcessor,並將該實現類提供給spring容器,spring容器將自動執行兩個方法:在初始化方法前執行before()方法,在初始化方法後執行after()方法。配置格式:
這句程式碼的意思就是:把實現類提供給了spring容器。
- Factory hook(勾子) that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
- spring提供工廠勾子,用於修改例項物件,可以生成代理物件。(是AOP底層)
谷歌翻譯:Factory hook(勾子),允許自定義修改新的bean例項,例如:檢查標記介面或用代理包裝它們。
我們來模擬這句話的意思:
before() => postProcessAfterInitialization(Object bean, String beanName) after() => postProcessBeforeInitialization(Object bean, String beanName) A a = new A(); a = B.before(a); // 將a的例項物件傳遞給後處理bean,可以什麼都沒做,也可以做一些事情,比如:生成jdk代理物件並返回給a,這樣a就從例項物件變成代理物件了,此時的a就具有了AOP功能;再比如,如果把null返回給a,再用a去呼叫方法,就會出現空指標異常。 a.init(); a = B.after(a); // 以下是AOP演示: // 我們現在在後處理Bean 程式碼執行完之後,把jdk代理物件返回給a。讓a在呼叫addUser()之前先做一些事情 // 之前要做的事情 a.addUser(); // 在目標方法的前後可以做一些事情,例如:開啟事務、提交事務、效能監控(前後時間)等等 // 之後要做的事情 a.destroy();
目標類示例程式碼如下:
UserService.java
public interface UserService { void addUser(); }
UserServiceImpl.java
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("e_lifecycle add user"); } public void myInit() { System.out.println("我的初始化方法"); } public void myDestory() { System.out.println("我的銷燬方法"); } }
實現類示例程式碼如下:
MyBeanPostProcessor.java
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("執行了前方法:" + beanName); return bean; } @Override public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException { System.out.println("執行了後方法:" + beanName); // 傳入的引數bean是我們的目標物件,此時我們的目標物件只有一個介面,那麼我們的代理物件也只有一個介面 // 生成jdk代理物件 return Proxy.newProxyInstance( MyBeanPostProcessor.class.getClassLoader(), // 代理物件 bean.getClass().getInterfaces(), // 目標物件 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("---開啟事務---"); Object obj = method.invoke(bean, args); // 執行目標方法,本例中的目標方法是addUser System.out.println("---關閉事務---"); return obj; } }); // 代理的處理程式 } }
在spring容器中進行配置:
applicationContext.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"> <!--
init-method 用於配置初始化方法,用於準備資料等使用場景
destroy-method 用於配置銷燬方法,用於清理資源等使用場景
-->
<bean id="userServiceId" class="service.UserServiceImpl"
init-method="myInit" destroy-method="myDestory"></bean>
<!-- 將後處理的實現類註冊給spring -->
<bean class="com.itheima.e_lifecycle.MyBeanPostProcessor"></bean> </beans>
測試示例程式碼如下:
TestLifecycle.java
public class TestLifecycle { @Test public void demo01() throws Exception { // 現在:使用spring 工廠(spring 容器) ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
// 要想使我的銷燬方法也執行,必須要求:
// 1.容器必須先close,我的銷燬方法才會執行;
// 2.必須是單例的(spring所建立該bean的例項個數只有一個)即bean中的scope配置成預設即可。
// 因為此close方法在介面 ApplicationContext 中沒有定義,而在實現類中提供了該方法,我們可以使用反射,因為反射最後執行的就是實現類中的方法。
applicationContext.getClass().getMethod("close").invoke(applicationContext);
}
}
- 執行結果截圖:
- 問題1:後處理bean作用某一個目標類,還是所有目標類?
答:所有。 - 問題2:如何只作用一個?
答:通過獲取"引數2"beanName進行控制。例如:"xxx".equals(method.getName());
-
六、依賴注入Bean 的屬性(xml)
- 注入依賴物件可以採用:
手工裝配
或自動裝配
。在實際應用中
建議使用手工裝配
,因為自動裝配會產生未知情況,開發人員無法預見最終的裝配結果。- 手動裝配:一般進行配置資訊都採用手動裝配。
- 基於xml裝配基於註解裝配 => 之後講解
- 構造方法注入
- 屬性setter方法注入
- 介面注入(spring不支援)
- 基於xml裝配基於註解裝配 => 之後講解
- 自動裝配:在struts 和spring 整合的時候使用自動裝配。
- byType:按型別裝配
- byName:按名稱裝配
- constructor:按構造裝配
- autodetect:不確定裝配(即自動裝配)
- 手動裝配:一般進行配置資訊都採用手動裝配。
構造方法
Bean物件類:
public class User { private Integer uid; private String username; private Integer age; public User(Integer uid, String username) { // 構造方法一 super(); this.uid = uid; this.username = username; } public User(String username, Integer age) { // 構造方法二 super(); this.username = username; this.age = age; } // 省略getter 和 setter 方法 // ......
在spring容器中進行配置:
applicationContext.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"> <!--6.5.1、構造方法注入
<constructor-arg> 用於配置構造方法的一個引數argument
name :引數的名稱
value :設定普通資料
ref :設定引用資料,一般是另一個bean 的id值
index :引數的索引號,從0開始 。如果只有索引,匹配到了多個構造方法時,預設使用第一個。
type :確定引數型別
例如1:name屬性開發中不常用,因為使用該屬性需要關聯要例項化物件的原始碼,否則name的值你就不知道。而一般開發中我們我們不會得到原始碼。
<constructor-arg name="username" value="李曉藝"></constructor-arg>
<constructor-arg name="age" value="26"></constructor-arg>
例如2:型別type 和 索引index (這兩者結合使用)
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
注意:在開發中為了指定執行的是哪個構造方法,一般使用index屬性和type屬性結合的方式。
-->
<bean id="userId" class="entity.User">
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean> </beans>
setter方法
在spring容器中進行配置:
applicationContext.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"> <!--6.5.2、setter方法注入
* 注入的是普通資料 時
<property name="" value="值"></property>
等效
<property name="">
<value>值</value>
</property>
* 注入的是引用資料時
<property name="" ref="另一個bean的id"></property>
等效
<property name="">
<ref bean="另一個bean的id"></ref>
</property>
-->
<bean id="personId" class="entity.Person">
<property name="pname" value="曉藝"></property>
<property name="age">
<value>26</value>
</property>
<property name="homeAddr" ref="homeAddrId"></property>
<property name="companyAddr">
<ref bean="companyAddrId"></ref>
</property>
</bean>
<bean id="homeAddrId" class="entity.Address">
<property name="addr" value="山西運城"></property>
<property name="tel" value="911"></property>
</bean>
<bean id="companyAddrId" class="entity.Address">
<property name="addr" value="百子灣"></property>
<property name="tel" value="120"></property>
</bean> </beans>
-
七、P名稱空間 [瞭解]
-
是對"setter方法注入"進行簡化,替換
,
而是在
-
p名稱空間使用前提:必須新增名稱空間。如下圖所示:
-
在spring容器中進行配置:
applicationContext.xml
<!--6.5.3、P名稱空間[瞭解] 是對"setter方法注入"進行簡化,替換`<property name="屬性名稱">`, 而是在`<bean p:屬性名稱="普通值" 和 p:屬性名稱-ref="引用值">` p名稱空間使用前提:必須新增名稱空間。 注意:開發中一般不這麼用,一般用於裝逼用。 --> <bean id="personId" class="com.itheima.f_xml.c_p.Person" p:pname="明軍" p:age="26" p:homeAddr-ref="homeAddrId" p:companyAddr-ref="companyAddrId"> </bean> <bean id="homeAddrId" class="entity.Address" p:addr="河南信陽" p:tel="119"> </bean> <bean id="companyAddrId" class="entity.Address" p:addr="青年路" p:tel="110"> </bean>
-
八、SpEL [瞭解]
- 對
進行統一程式設計,所有的內容都使用value。
格式:
#{123}、#{'bruce'}、#{2e5} :數字、字串、科學計數法(常量)
#{beanId} :引用另一個Bean
#{beanId.propName} :引用Bean 的屬性(操作資料)
#{beanId.toString()} :引用Bean 的方法(執行方法)
#{T(類).欄位|方法} :引用靜態方法或欄位,例如:T(java.lang.Math).PI
#{3 lt 4 == 4 ge 3} :運算子支援
#{user.name matches ‘[a-z]{6,}’} :正則表示式支援
#{likes[3]} :集合支援
示例程式碼如下:
在spring容器中進行配置:
applicationContext.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"> <!--6.5.4、SpEL
<property name="cname" value="#{'Bruce'}"></property>
<property name="cname" value="#{customerId.cname.toUpperCase()}"></property>
通過另一個bean,獲得屬性,再呼叫的方法。
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
?. 如果物件不為null,將呼叫方法,為null,也去呼叫方法,不報錯。
-->
<bean id="customerId" class="com.itheima.f_xml.d_SpEL.Customer">
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
<property name="pi" value="#{T(java.lang.Math).PI}"></property>
</bean> </beans>
-
九、集合注入
在spring容器中進行配置:
applicationContext.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"> <!--6.5.5、集合注入
集合的注入都是給<property>新增子標籤
陣列:<array>
List:<list>
Set:<set>
Map:<map> 注意:map存放的是k/v鍵值對,使用<entry>描述
Properties 使用 <props> 和 <prop> 描述,示例:<props><prop key=""></prop></props>
普通資料 放在:<value>
引用資料 放在:<ref>
-->
<bean id="collectionDataId" class="entity.CollectionData">
<property name="arrayData">
<array>
<value>cmj</value>
<value>lxy</value>3
<value>明軍</value>
<value>曉藝</value>
</array>