零、驀然回首
- Struts2:web層,比較簡單(難點:ValueStack值棧、攔截器)
- Hibernate:dao層,知識點雜(學了不用,默認設置夠用了)
- Spring:service層,重要,(講多少用多少)
Spring課程概述:
- Spring day01:基礎(IoC控制反轉、DI依賴注入)、整合JUnit、整合Web
- Spring day02:AOP切面編程、JDBCTemplate
- Spring day03:事務管理、SSH整合
一、Spring框架概述
1.1、什麼是Spring?
- Spring是一個開源框架,Spring是於2003 年興起的一個
輕量級
的Java 開發框架,由Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中闡述的部分理念和原型衍生而來。它是爲了解決企業應用開發的複雜性
而創建的。該框架的主要優勢之一就是其分層架構
,分層架構允許使用者選擇使用哪一個組件,同時爲 J2EE 應用程序開發提供集成的框架。Spring使用基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅限於服務器端的開發。從簡單性
、可測試性
和鬆耦合
的角度而言,任何Java應用都可以從Spring中受益。Spring的核心是控制反轉(IoC)
和面向切面(AOP)
。簡單來說,Spring是一個分層的
JavaSE/EEfull-stack(一站式)
輕量級開源框架。 - 輕量級:與EJB對比,依賴資源少,消耗的資源少。
- 分層:full-stack(一站式),每一個層都提供解決方案。
web層:struts,
spring-MVC
service層:spring
dao層:hibernate,mybatis,jdbcTemplate
-->spring-data
1.2、Spring由來(兩本書)
- 《Expert One-to-One J2EE Design and Development》
- 《Expert One-to-One J2EE Development without EJB》
1.3、Spring核心
- Spring的核心是:控制反轉(IoC)和麪向切面(AOP)
- 官網:http://www.springsource.org
1.4、Spring優點
- 方便解耦,簡化開發(高內聚低耦合)
- Spring就是一個大工廠(容器),可以將所有對象創建和依賴關係維護,交給Spring管理。
- Spring工廠就是用於生成Bean。
- AOP編程的支持
- Spring提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能。
- 聲明式事務的支持
- 只需要通過配置就可以完成對事務的管理,而無需手動編程。
- 方便程序的測試
- Spring對Junit4 支持,可以通過註解方便的測試Spring 程序。
- 方便集成各種優秀框架
- Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持。
- 降低JavaEE API的使用難度
- Spring 對JavaEE 開發中非常難用的一些API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些API應用難度大大降低。
1.5、Spring體系結構
- Spring 框架是一個分層架構,它包含一系列的功能要素,並被分爲大約20個模塊。這些模塊分爲Core Container、Data Access/Integration、Web、AOP(Aspect Oriented Programming)、Instrumentation和測試部分,
- 如下圖所示:
二、Spring入門案例:IoC【掌握】
- 編寫流程
- 下載Spring最新開發包
- 複製Spring開發jar包到工程
- 編寫Spring核心配置文件
- 在程序中讀取Spring配置文件,通過Spring框架獲得Bean,完成相應操作
2.1、導入jar包
4 + 1:4個核心jar包(beans、core、context、expression)+ 1個依賴jar包(com.springsource.org.apache.commons.logging-1.1.1.jar)
Spring核心開發包簡介:
- spring-core-3.2.2.RELEASE.jar
- 包含Spring框架基本的核心工具類,Spring其它組件要都要使用到這個包裏的類,是其它組件的基本核心。
- spring-beans-3.2.2.RELEASE.jar
- 所有應用都要用到的,它包含訪問配置文件、創建和管理bean。
- 以及進行Inversion of Control(IoC) / Dependency Injection(DI)操作相關的所有類。
- spring-context-3.2.2.RELEASE.jar
- Spring提供在基礎IoC功能上的擴展服務,此外還提供許多企業級服務的支持。
- 如郵件服務、任務調度、JNDI定位、EJB集成、遠程訪問、緩存以及各種視圖層框架的封裝等。
- spring-expression-3.2.2.RELEASE.jar
- Spring表達式語言。
- com.springsource.org.apache.commons.logging-1.1.1.jar
- 第三方的主要用於處理日誌。
2.2、編寫目標類
- 創建UserService接口和實現類
- 獲得UserService實現類的實例
- 在之前開發中,我們是直接new一個對象即可。即:`private IUserDao dao = new IUserDaoImpl();`
- 在學習Spring之後,將由Spring來創建對象的實例 --> 即:`IoC 控制反轉(Inverse of Control)` 之後需要實例對象時,從Spring工廠(容器)中獲得即可,需要將實現類的全限定名稱配置到xml文件中。
UserService.java
package com.itheima.a_ioc; public interface UserService { void addUser(); }
UserServiceImpl.java
package com.itheima.a_ioc; public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("a_ioc add user"); } }
2.3、編寫spring配置文件
- 位置:任意,開發中一般在classpath下(src)
- 名稱:任意,開發中常用 applicationContext.xml
- 內容:添加schema約束
- 約束文件位置:spring-framework-3.2.0.RELEASE\docs\spring-framework-reference\html\xsd-config.html
beans.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"> <!-- 配置service <bean> 配置需要創建的對象 id :用於之後從Spring容器中獲得實例時使用的 class :需要創建實例的全限定類名 注意:id的值剛開始學習的時候我們寫userServiceId,在實際開發中寫userService。 --> <bean id="userServiceId" class="com.itheima.a_ioc.UserServiceImpl"></bean> </beans>
2.4、測試代碼
TestIoC.java
package com.itheima.a_ioc; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestIoC { @Test public void demo01() { // 之前開發,自己手寫new出對象 UserService userService = new UserServiceImpl(); userService.addUser(); } @Test public void demo02() { // 現在從spring容器中獲得對象實例 // 1 、獲得容器 String xmlPath = "com/itheima/a_ioc/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); // 2、獲得內容 ,注意此時不需要自己new出對象了,都是從spring容器中獲得 UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); } }
- IoC(Inverse of Control)反轉控制的概念,就是將原本在程序中手動創建UserServiceImpl對象的控制權,交由Spring框架管理。 簡單說,就是創建UserServiceImpl對象的控制權被反轉到了Spring框架。
三、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>
3.1、編寫目標類
- 創建BookDao接口和實現類
- 創建BookService接口和實現類
- 將dao和service配置到 xml文件中
- 使用api測試
3.1.1、dao
BookDao.java
package com.itheima.b_di; public interface BookDao { void save(); }
BookDaoImpl.java
package com.itheima.b_di; public class BookDaoImpl implements BookDao { @Override public void save() { System.out.println("b_di add book"); } }
3.1.2、service
BookService.java
package com.itheima.b_di; public interface BookService { void addBook(); }
BookServiceImpl.java
package com.itheima.b_di; public class BookServiceImpl implements BookService { // 方式1:之前,接口 = 實現類 // private BookDao bookDao = new BookDaoImpl(); // 方式2:現在,接口 + setter private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } @Override public void addBook() { this.bookDao.save(); } }
3.2、編寫spring配置文件
bean.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"> <!-- 模擬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="com.itheima.b_di.BookServiceImpl"> <!-- 將dao實例設置給service實例 --> <property name="bookDao" ref="bookDaoId"></property> <!-- 用於進行屬性注入 --> </bean> <!-- 創建dao實例 --> <bean id="bookDaoId" class="com.itheima.b_di.BookDaoImpl"></bean> </beans>
3.3、測試代碼
TestDI.java
package com.itheima.b_di; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.itheima.a_ioc.UserService; import com.itheima.a_ioc.UserServiceImpl; public class TestDI { @Test public void demo01() { // 之前開發,自己手寫new出對象 // BookService bookService = new BookServiceImpl(); // bookService.addBook(); } @Test public void demo02() { // 現在從spring容器中獲得對象實例 // 1 、獲得容器 String xmlPath = "com/itheima/b_di/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); // 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實例。
四、MyEclipse中編寫applicationContext.xml時的提示設置
- 法一:電腦聯網
- 法二:電腦沒有聯網
- 步驟1、確定`xsd文件`位置:spring-framework-3.2.0.RELEASE\schema\beans
- 步驟2、複製路徑:http://www.springframework.org/schema/beans/spring-beans.xsd
- 步驟三:在`Preferences`中搜索`xml catalog`,並添加約束提示
五、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 com.itheima.b_di; import org.junit.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 TestDI { @Test public void demo01() { // 之前開發,自己手寫new出對象 // BookService bookService = new BookServiceImpl(); // bookService.addBook(); } @Test public void demo02() { // 現在從spring容器中獲得對象實例,使用的是ApplicationContext // 1 、獲得容器 String xmlPath = "com/itheima/b_di/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); // 採取非延時加載,當配置文件被加載時,就進行對象的實例化。 // 2、獲得內容 ,注意此時不需要自己new出對象了,都是從spring容器中獲得 BookService bookService = (BookService) applicationContext.getBean("bookServiceId"); bookService.addBook(); } @Test public void demo03() { // 現在從spring容器中獲得對象實例,使用的是BeanFactory,裏面需要一個Resource,該Resource又是一個接口,需要找它的實現類ClassPathResource // 1 、獲得容器 String xmlPath = "com/itheima/b_di/beans.xml"; BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(xmlPath)); // 2、獲得內容 ,注意此時不需要自己new出對象了,都是從spring容器中獲得 BookService bookService = (BookService) beanFactory.getBean("bookServiceId"); // 採取延遲加載,第一次調用getBean(); 時纔會初始化Bean(即實例化對象)。 bookService.addBook(); } }
六、裝配Bean:基於XML
6.1、實例化Bean 的三種方式
- 3種bean實例化方式:
- 使用`默認構造方法`實例化
- 使用`靜態工廠方法`實例化
- 使用`實例工廠方法`實例化
6.1.1、使用默認構造方法實例化
格式:
<bean id="從Spring容器中獲得實例時使用的" class="需要創建實例的全限定類名"></bean> 例如:<bean id="userServiceId" class="com.itheima.a_ioc.UserServiceImpl"></bean>
示例中用到的 UserService.java 和 UserServiceImpl.java 代碼同上 2.2、編寫目標類 的代碼,這裏不再贅述!
在spring容器中進行配置: beans.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="com.itheima.a_ioc.UserServiceImpl"></bean> </beans>
測試代碼: TestIoC.java
package com.itheima.a_ioc; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestIoC { @Test public void demo01() { // 之前開發,自己手寫new出對象 UserService userService = new UserServiceImpl(); // 直接手動創建實例 userService.addUser(); } @Test public void demo02() { // 現在從spring容器中獲得對象實例 // 1 、獲得容器 String xmlPath = "com/itheima/a_ioc/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); // 2、獲得內容 ,注意此時不需要自己new出對象了,都是從spring容器中獲得 UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); } }
6.1.2、使用靜態工廠方法實例化
- 靜態工廠:常用與spring整合其他框架(工具)時。
- 靜態工廠:用於生成實例對象,所有的方法必須是static。
示例中用到的 UserService.java 和 UserServiceImpl.java 代碼同上 2.2、編寫目標類 的代碼,這裏不再贅述!
格式:
<bean id="" class="工廠全限定類名" factory-method="靜態方法名稱">
在spring容器中進行配置: beans.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="com.itheima.c_inject.b_static_factory.MyBeanFactory" factory-method="createService"></bean> </beans>
靜態工廠類代碼:
package com.itheima.c_inject.b_static_factory; public class MyBeanFactory { /** * 創建實例的靜態工廠,所有的方法必須是靜態的(static)。 * * @return */ public static UserService createService() { return new UserServiceImpl(); } // 還有創建其他實例的靜態工廠 // ...... }
測試代碼: TestStaticFactory.java
package com.itheima.c_inject.b_static_factory; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 第二種實例化Bean 的方式 :使用靜態工廠方法實例化 * */ public class TestStaticFactory { @Test public void demo01() { // 以前:使用自定義的靜態實例工廠 UserService userService = MyBeanFactory.createService(); userService.addUser(); } @Test public void demo02() { // 現在:使用spring 工廠:將自定義的靜態工廠創建的實例交給spring管理 String xmlPath = "com/itheima/c_inject/b_static_factory/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); 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說一下,去拿就可以了。
6.1.3、使用實例工廠方法實例化
- 實例工廠:必須先有工廠的實例對象,然後通過實例對象去創建對象。特點:提供所有的方法都是“非靜態”的。
示例中用到的 UserService.java 和 UserServiceImpl.java 代碼同上 2.2、編寫目標類 的代碼,這裏不再贅述!
在spring容器中進行配置: beans.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>
靜態工廠類代碼:
package com.itheima.c_inject.c_factory; public class MyBeanFactory { /** * 創建實例的工廠,所有方法非靜態 * * @return */ public UserService createService() { return new UserServiceImpl(); } // 還有創建其他實例的工廠 // ...... }
測試代碼: TestFactory.java
package com.itheima.c_inject.c_factory; 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管理 String xmlPath = "com/itheima/c_inject/c_factory/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換 // UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); } }
6.2、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可以表示唯一引用。
- 在spring容器中添加以下配置:
示例:
6.3、Bean 的作用域
- Bean 的作用域:用於確定spring所創建bean 的實例個數。
- 取值:
- singleton 單例,默認值。
- prototype 多例,每執行一次getBean() 將獲得一個實例。例如:在struts整合spring時,需要配置action爲多例。
- 配置示例:例如:
< bean id="xxx" class="xxx" scope="xxx">
- 默認情況下會在容器啓動時初始化bean,但我們可以指定Bean節點的
lazy-init="true"
來延遲初始化bean,這時候,只有第一次獲取bean會才初始化bean。 例如:< bean id="xxx" class="cn.itheima.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應用的模塊化和用戶中心化。
示例代碼如下: 示例中用到的 UserService.java 和 UserServiceImpl.java 代碼同上 2.2、編寫目標類 的代碼,這裏不再贅述!
在spring容器中進行配置: beans.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
package com.itheima.d_scope; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestScope { @Test public void demo01() { // 現在:使用spring 工廠 String xmlPath = "com/itheima/d_scope/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService1 = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換 UserService userService2 = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換 // 默認Bean的作用域是單例,所以打印的對象的地址是一樣的 // System.out.println(userService1); // com.itheima.c_inject.d_scope.UserServiceImpl@2ac273d3 // System.out.println(userService2); // com.itheima.c_inject.d_scope.UserServiceImpl@2ac273d3 // 現在在配置文件中添加scope屬性,值爲prototype,此時Bean的作用域變爲多例了,再次打印,輸出地址不一樣了 System.out.println(userService1); // com.itheima.c_inject.d_scope.UserServiceImpl@66480dd7 System.out.println(userService2); // com.itheima.c_inject.d_scope.UserServiceImpl@52a86356 } }
6.4、Bean 的生命週期
6.4.1、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
。
6.4.2、Bean 的初始化和銷燬
目標方法執行前和執行後,將進行Bean的初始化或銷燬。
示例:< bean id="xxx" class="xxx" init-method="初始化的方法名稱" destroy-method="銷燬的方法名稱"></bean>
示例代碼如下:
編寫目標類代碼: UserService.java
package com.itheima.c_inject.e_lifecycle; public interface UserService { void addUser(); }
UserServiceImpl.java
package com.itheima.c_inject.e_lifecycle; 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("我的銷燬方法"); } }
編寫配置文件: beans.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="com.itheima.c_inject.e_lifecycle.UserServiceImpl" init-method="myInit" destroy-method="myDestory"></bean> </beans>
編寫測試代碼:
package com.itheima.c_inject.e_lifecycle; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestLifecycle { @Test public void demo01() throws Exception { // 現在:使用spring 工廠(spring 容器) String xmlPath = "com/itheima/c_inject/e_lifecycle/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); // 要想使我的銷燬方法也執行,必須要求: // 1.容器必須先close,我的銷燬方法纔會執行; // 2.必須是單例的(spring所創建該bean的實例個數只有一個)即bean中的scope配置成默認即可。 // 因爲此close方法在接口 ApplicationContext 中沒有定義,而在實現類中提供了該方法,我們可以使用反射,因爲反射最後執行的就是實現類中的方法。 applicationContext.getClass().getMethod("close").invoke(applicationContext); } }
6.4.3、BeanPostProcessor 後處理Bean
- 是由spring提供的一種機制,只要實現類實現此接口BeanPostProcessor,並將該實現類提供給spring容器,spring容器將自動執行兩個方法:
- 在初始化方法前執行before()方法,在初始化方法後執行after()方法。配置格式:
< bean class="">
這句代碼的意思就是:把實現類提供給了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
package com.itheima.e_lifecycle; public interface UserService { void addUser(); }
UserServiceImpl.java
package com.itheima.e_lifecycle; 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
package com.itheima.e_lifecycle; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; 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; } }); // 代理的處理程序 } }
配置文件示例代碼如下: beans.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="com.itheima.e_lifecycle.UserServiceImpl" init-method="myInit" destroy-method="myDestory"></bean> <!-- 將後處理的實現類註冊給spring --> <bean class="com.itheima.e_lifecycle.MyBeanPostProcessor"></bean> </beans>
測試示例代碼如下: TestLifecycle.java
package com.itheima.c_inject.e_lifecycle; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestLifecycle { @Test public void demo01() throws Exception { // 現在:使用spring 工廠(spring 容器) String xmlPath = "com/itheima/e_lifecycle/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); 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());
6.5、依賴注入Bean 的屬性(xml)
- 注入依賴對象可以採用:
手工裝配
或自動裝配
。- 手動裝配:一般進行配置信息都採用手動裝配。
- 基於xml裝配
- 構造方法注入
- 屬性setter方法注入
- 接口注入(spring不支持)
- 基於註解裝配 => 之後講解
- 基於xml裝配
- 自動裝配:在struts 和spring 整合的時候使用自動裝配。
- byType:按類型裝配
- byName:按名稱裝配
- constructor:按構造裝配
- autodetect:不確定裝配(即自動裝配)
- 手動裝配:一般進行配置信息都採用手動裝配。
- 在
實際應用中
建議使用手工裝配
,因爲自動裝配會產生未知情況,開發人員無法預見最終的裝配結果。
6.5.1、構造方法
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的配置:
<?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="com.itheima.f_xml.a_constructor.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>
6.5.2、setter方法
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"> <!--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="com.itheima.f_xml.b_setter.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="com.itheima.f_xml.b_setter.Address"> <property name="addr" value="山西運城"></property> <property name="tel" value="911"></property> </bean> <bean id="companyAddrId" class="com.itheima.f_xml.b_setter.Address"> <property name="addr" value="百子灣"></property> <property name="tel" value="120"></property> </bean> </beans>
6.5.3、P命名空間 [瞭解]
- 是對"setter方法注入"進行簡化,替換
< property name="屬性名稱">
, 而是在< bean p:屬性名稱="普通值" 和 p:屬性名稱-ref="引用值">
- p命名空間使用前提:必須添加命名空間。如下圖所示:
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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--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="com.itheima.f_xml.c_p.Address" p:addr="河南信陽" p:tel="119"> </bean> <bean id="companyAddrId" class="com.itheima.f_xml.c_p.Address" p:addr="青年路" p:tel="110"> </bean> </beans>
6.5.4、SpEL [瞭解]
- 對
< property>
進行統一編程,所有的內容都使用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的配置:
<?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>
- SpEL擴展閱讀書籍:
6.5.5、集合注入
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"> <!--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="com.itheima.f_xml.e_collection.CollectionData"> <property name="arrayData"> <array> <value>cmj</value> <value>lxy</value>3 <value>明軍</value> <value>曉藝</value> </array> </property> <property name="listData"> <list> <value>琴棋書畫</value> <value>擼啞鈴</value> <value>花鳥魚蟲</value> <value>擼娃娃</value> </list> </property> <property name="setData"> <set> <value>看電影</value> <value>運動</value> <value>創作</value> <value>旅行</value> </set> </property> <property name="mapData"> <map> <entry key="bruce" value="布魯斯"></entry> <entry> <key><value>lucy</value></key> <value>露西</value> </entry> </map> </property> <property name="propsData"> <props> <prop key="處女座">內心善良</prop> <prop key="天蠍座">宅心仁厚</prop> <prop key="緣定今生">此生不悔</prop> </props> </property> </bean> </beans>
七、裝配Bean:基於annotation(註解)
- 註解:就是一個類,格式:@註解名稱
- 開發中:使用註解 取代 xml配置文件。
1. @Component 取代 <bean class=""></bean> @Component("id的值") 2. web開發中,提供3個@Component註解衍生註解(功能一樣)取代 <bean class=""></bean> @Repository :dao層 @Service :service層 @Controller :web層 注意:SpringMVC 中推薦使用註解哦! 3. 依賴注入,給私有字段設置,也可以給setter方法設置 普通值:@Value("") 引用值: 方式1:按照【類型】注入 @Autowired 方式2:按照【類型+名稱】注入1 @Autowired @Qualifier("名稱") 方式3:按照【名稱】注入2 @Resource("名稱") 4. 生命週期 @PostConstruct :初始化 @PreDestroy :銷燬 5. 作用域 @Scope("prototype") 多例
註解使用前提,必須添加命名空間,讓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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 組件掃描:掃描含有註解的類 --> <context:component-scan base-package="com.itheima.g_annotation.a_ioc"></context:component-scan> </beans>
示例截圖:
演示: 單例、多例、初始化、銷燬 UserService.java
package com.itheima.g_annotation.c_other; public interface UserService { void addUser(); }
UserServiceImpl.java
package com.itheima.g_annotation.c_other; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; @Service("userServiceId") // 單例 @Scope("prototype") // 多例 public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("c_other add user"); } @PostConstruct // 初始化 public void myInit() { System.out.println("我的初始化方法"); } @PreDestroy // 銷燬 public void myDestory() { System.out.println("我的銷燬方法"); } }
beans.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 組件掃描:掃描含有註解的類 --> <context:component-scan base-package="com.itheima.g_annotation.c_other"></context:component-scan> </beans>
測試代碼: TestOther.java
package com.itheima.g_annotation.c_other; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestOther { @Test public void demo01() { // 現在:使用spring 工廠 String xmlPath = "com/itheima/g_annotation/c_other/beans.xml"; ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService1 = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換 UserService userService2 = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換 // 默認Bean的作用域是單例,所以打印的對象的地址是一樣的 // System.out.println(userService1); // com.itheima.c_inject.d_scope.UserServiceImpl@2ac273d3 // System.out.println(userService2); // com.itheima.c_inject.d_scope.UserServiceImpl@2ac273d3 // 現在在配置文件中添加scope屬性,值爲prototype,此時Bean的作用域變爲多例了,再次打印,輸出地址不一樣了 System.out.println(userService1); // com.itheima.c_inject.d_scope.UserServiceImpl@66480dd7 System.out.println(userService2); // com.itheima.c_inject.d_scope.UserServiceImpl@52a86356 applicationContext.close(); } }