本節內容:
- Spring介紹
- Spring搭建
- Spring概念
- Spring配置講解
- 使用注解配置Spring
一、Spring介紹
1. 什么是Spring
Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由 RodJohnson 在其著作 Expert One-On-One J2EE Development and Design 中闡述的部分理念和原型衍生而來。它是為了解決企業應用開發的復雜性而創建的。框架的主要優勢之一就是其分層架構,分層架構允許使用者選擇使用哪一個組件,同時為 J2EE 應用程序開發提供集成的框架。Spring 使用基本的 JavaBean 來完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不僅限於服務器端的開發,去開發android也可以。從簡單性、可測試性和松耦合的角度而言,任何 Java 應用都可以從 Spring 中受益。Spring 的核心是控制反轉 (IoC)和面向切面(AOP)。簡單來說,Spring 是一個分層的 JavaSE/EEfull-stack(一站式) 輕量級 開源框架。
JavaEE開發分成三層結構:
- WEB層
- 業務層
- 持久層
三層架構中Spring的位置:
Spring是一個大的容器,其中裝了很多對象,之前三層架構在運行時,都需要自己來創建對象,比如在web層中需要使用service層中的,需要new。當使用了Spring之后,Spring中已經存好了項目中需要的對象。也就是在三層中,需要對象時不需要在寫new了,而是跟Spring要這個對象。
Spring是一站式框架:純Spring開發一個項目是完全沒問題的。正是因為Spring框架性質是屬於容器性質的(比如Spring之所以能處理請求,是因為容器中裝了能處理請求的框架,所以它在web層能處理請求),容器中裝什么對象,就有什么功能。所以可以一站式。
- WEB層:Spring MVC
- 業務層:Bean管理:(IoC)
- 持久層:Spring的JDBC模板。ORM模板用於整合其他的持久層框架。
2. 為什么要學習Spring
- 方便解耦,簡化開發
- Spring 就是一個大工廠,可以將所有對象創建和依賴關系維護,交給 Spring 管理
- AOP 編程的支持
- Spring 提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能
- 聲明式事務的支持
- 只需要通過配置就可以完成對事務的管理,而無需手動編程
- 方便程序的測試
- Spring 對 Junit4 支持,可以通過注解方便的測試 Spring 程序
- 方便集成各種優秀框架
- Spring 不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持 降低 JavaEE API 的使用難度
- Spring 對 JavaEE 開發中非常難用的一些 API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些 API 應用難度大大降低。
3. Spring 的版本
Spring 3.X 和 Spring4.X
Spring 3.0.2版本:將市面上常見的、支持整合進來的工具類全部進行了收錄,這個包里面有很多很多jar包。但是這是Spring堅持“做好事”的最后一個版本,沒有任何回報。
以Spring 4.2.4為例:解壓壓縮包spring-framework-4.2.4.RELEASE-dist.zip
- docs是Spring的文檔,API和開發規范
- lib下是Spring的jar包和源碼包
- schema下是Spring當中的約束文件
lib目錄下的包看起來很多,其實是3個一組:
二、Spring搭建示例
1. 下載導包
Spring官網:http://spring.io/
下載地址: http://repo.springsource.org/libs-release-local/org/springframework/spring
解壓好的lib下的jar包不會都用,針對需要選擇相應的jar包。
2. 創建web項目,引入Spring的開發包
在 web/WEB_INF/ 目錄下創建一個lib目錄,把下圖中的4個jar包放進lib目錄下。
Spring本身也是支持日志的,市面上已經有非常成熟的日志包了,Spring日志系統使用的是Apache開發出來的日志包。所以還需要導入Apache的日志方面的jar包,這部分jar包不在Spring解壓后的lib下,得去Apache官方網站下載。把com.springsource.org.apache.commons.logging-1.1.1.jar和com.springsource.org.apache.log4j-1.2.15.jar復制到lib目錄下。新版本的Spring應該不需要導入com.springsource.org.apache.log4j-1.2.15.jar,導了也不會錯。
點擊 File --> Project Structure,進入 Project Structure窗口,點擊 Modules --> 選中項目 --> 切換到 Dependencies 選項卡 --> 點擊下面的“+”,選擇 “JARs or directories...”,選擇創建的lib目錄。
3. 創建包,編寫一個類文件
User.java
package com.wisedu.springDemo; /** * Created by jkzhao on 12/7/17. */ public class User { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
4. 書寫Spring的配置文件,注冊對象到Spring容器
對象和框架交流,通過配置文件交流。
Spring的配置文件存放位置任意,放在src目錄下。名字也是任意,但是建議叫applicationContext.xml。
選中src,右鍵選擇New --> XML Configuration File --> Spring Config,輸入名字applicationContext,點擊OK。
默認生成的文件約束是寫了一些的。在我們這個例子中,上面默認生成的約束就夠用了。
下面開始配置約束,這個user對象要交給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"> <!-- 將User對象交給Spring容器管理 --> <!-- Bean元素:使用該元素描述需要Spring容器管理的對象 name屬性:給被管理的對象起個名字。獲得對象時根據該名稱獲得對象 class屬性:被管理對象的完整類名 id屬性:早年使用的,功能與name屬性一模一樣,id的規矩:名稱不能重復,不能使用特殊字符。所以加了name屬性,可以使用特殊字符,名稱也可以重復,但是不推薦名稱重復。 結論:盡量使用name屬性 --> <bean name="user" class="com.wisedu.springDemo.User"></bean> <!--屬性name的值隨意--> </beans>
5. 代碼測試
在 src 下創建一個包:com.wisedu.test,然后創建一個類文件testSpringDemo.java:
package com.wisedu.test; import org.junit.Test; import org.springframework.context.ApplicationContext; //這是一個接口,創建容器對象時需要找其實現類 import org.springframework.context.support.ClassPathXmlApplicationContext; import com.wisedu.springDemo.User; /** * Created by jkzhao on 12/7/17. */ public class testSpringDemo { @Test public void test1(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user"); //3.打印User對象 System.out.print(user); } }
運行該方法,然后可以發現打印出值了,也就是獲取到了User對象。
【注意】:上面在test1方法中,創建容器是使用 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 來創建的,每個項目中只有一個applicationContext對象,不是每個方法中都要創建一下。如何保證只創建一個?
之前學過一個ServletContext域對象,它在一個項目中只有一份,隨着程序的啟動而創建,隨着程序的停止而銷毀。這就用到了Listener。監聽器中有8個監聽器,使用其中一個監聽器:ServletContext域創建和銷毀的Listener。這樣當ServletContext創建時我們可以創建applicationContext對象,當ServletContext銷毀時,我們可以銷毀applicationContext對象。這樣applicationContext就和ServletContext“共生死了”。使用這個監聽器還有一個好處,在監聽器中可以非常方便地獲得事件源,也就意味着我們可以獲得ServletContext對象,這個容器被放進了ServletContext域對象中,說通俗點,就是被放進了application域中。Spring已經把這個監聽器寫好了,我們只需要在web.xml中配置下就可以了。當然還需要導入一個包:spring-web-4.2.4.RELEASE.jar
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
前面在講到Spring容器時說道,Spring的配置文件位置任意,Spring配置文件名字任意,所以這意味着還需要在web.xml中指明該配置文件的位置及名稱。
<!-- 可以讓spring容器隨着項目的啟動而創建,隨項目的關閉而銷毀--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--指定加載Spring配置文件的位置--> <context-param> <param-name>contextConfigLocation</param-name> <!--這個key不能改,背下來--> <param-value> classpath*:/applicationContext.xml </param-value> </context-param>
接着寫java代碼從application域中獲取容器。但是application域中存放東西是鍵值對存在的,我們得知道鍵才能取。Spring考慮到這種情況,准備了一個工具方法,把鍵封裝在工具方法中了。
//1.這里以Struts2為例獲取ServletContext對象 ServletContext sc = ServletActionContext.getServletContext(); //2.從sc中獲得ac容器 WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc); //3.從容器中獲得需要的對象 ...
三、Spring概念
1. IOC思想和DI技術
(1)IOC (Inverse Of Control):控制反轉,將我們創建對象的方式反轉了。
以前對象的創建是由開發人員自己維護,包括依賴關系也是自己注入。使用了Spring之后,對象的創建以及依賴關系可以由Spring完成創建以及注入。
(2)DI(Dependency Injection):依賴注入。將必須的屬性注入到對象當中,是實現IOC思想的必要條件。
需要有 IOC 的環境,Spring 創建這個類的過程中,Spring 將類的依賴的屬性設置進去。
注入方式:
- set方法注入
- 構造方法注入
- 字段注入:不建議。
注入類型:
- 值類型注入:8大基本數據類型。比如上面示例中的User對象,它里面有name和age兩個屬性,現在我們希望User對象創建出來后,name="Tom",age=18。這個"Tom"和18可以在配置文件中配置,這樣Spring會把我們配置的值自動交給屬性。
- 引用類型注入:將依賴的對象注入
所以實現IOC思想需要DI支持,DI對IOC提供了技術上的支撐。
2. Spring中的工廠(容器)
(1)ApplicationContext
ApplicationContext接口有兩個實現類:
- ClassPathXmlApplicationContext :加載類路徑下 Spring 的配置文件
- FileSystemXmlApplicationContext :從絕對路徑上加載本地磁盤下 Spring 的配置文件,基本用不着。
(2)BeanFactory(過時)
BeanFactory是個接口,是Spring框架在初創時創建的第一個接口。像這種初創的接口,功能一般是很少的,針對原始接口的實現類功能較為單一。下面是幅繼承關系圖。
(3)BeanFactory 和 ApplicationContext 的區別
BeanFactory接口的實現類的容器特點是:每次在獲得對象時才會創建對象。比如上面示例中的代碼,
User user = (User)ac.getBean("user"); //只有在獲取對象時才會創建對象
而ApplicationContext接口實現類的容器特點是:在加載applicationContext.xml(容器啟動)時候就會創建容器中配置的所有對象。除此以外,提供更多功能。
總結:在web開發中,使用applicationContext,在硬件資源匱乏的環境使用BeanFactory。
四、Spring配置詳解
1. Bean元素的配置
Bean元素:凡是交給Spring容器管理的對象都是由Bean來描述。
- name屬性:給被管理的對象起個名字。獲得對象時根據該名稱獲得對象
- class屬性:被管理對象的完整類名
- id屬性:早年使用的,功能與name屬性一模一樣,id的規矩:名稱不能重復,不能使用特殊字符。所以加了name屬性,可以使用特殊字符,名稱也可以重復,但是不推薦名稱重復。
結論:盡量使用name屬性。
2. Spring生成Bean的三種方式(三種對象創建方式)
對象的創建必須經過類的構造函數。
(1)空參構造創建 --最重要
在上面示例中的User對象中加一個無參構造函數
重新創建一個包,把applicationContext.xml復制進該包中,把測試類testSpringDemo.java復制進該包中,並修改該文件中applicationContext.xml的位置。
具體代碼如下:
User.java
package com.wisedu.springDemo; /** * Created by jkzhao on 12/7/17. */ public class User { private String name; private Integer age; public User() { System.out.println("User對象空構造方法"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
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"> <!-- 創建方式1:空參構造創建 --> <bean name="user" class="com.wisedu.springDemo.User"></bean> </beans>
testSpringDemo.java
package com.wisedu.createObject; import com.wisedu.springDemo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by jkzhao on 12/7/17. */ public class testSpringDemo { @Test //創建方式1:空參構造創建對象 public void test1(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/createObject/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user"); //3.打印User對象 System.out.print(user); //true } }
運行結果:
(2)靜態工廠方式 --了解
寫一個類,在類中建一個方法,該方法中把user創建出來。調用該方法創建user,然后交給Spring容器來管理,這樣就不是Spring來創建對象了,我們希望Spring調用這個方法創建對象
UserFactory.java
package com.wisedu.createObject; import com.wisedu.springDemo.User; /** * Created by jkzhao on 12/8/17. */ public class UserFactory { //在該方法中手動把user創建出來。調用該方法創建user,然后交給Spring容器來管理,這樣就不是Spring來創建對象了,我們希望Spring調用這個方法創建對象,如何配置呢 public static User createUser(){ System.out.println("靜態工廠創建User"); return new User(); } }
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"> <!-- 創建方式1:空參構造創建 --> <bean name="user" class="com.wisedu.springDemo.User"></bean> <!-- 創建方式2:靜態工廠創建 調用UserFactory的createUser方法來創建名為User2的對象放入容器 --> <bean name="user2" class="com.wisedu.createObject.UserFactory" factory-method="createUser"></bean> </beans>
testSpringDemo.java
package com.wisedu.createObject; import com.wisedu.springDemo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by jkzhao on 12/7/17. */ public class testSpringDemo { @Test //創建方式1:空參構造創建對象 public void test1(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/createObject/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user"); //3.打印User對象 System.out.print(user); //true } @Test //創建方式2:靜態工廠創建對象 public void test2(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/createObject/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user2"); //3.打印User對象 System.out.print(user); } }
(3)實例工廠方式 --了解
public User createUser2(){ System.out.println("實例工廠創建User"); return new User(); }
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"> <!-- 創建方式1:空參構造創建 --> <bean name="user" class="com.wisedu.springDemo.User"></bean> <!-- 創建方式2:靜態工廠創建 調用UserFactory的createUser方法來創建名為User2的對象放入容器 --> <bean name="user2" class="com.wisedu.createObject.UserFactory" factory-method="createUser"></bean> <!-- 創建方式3:實例工廠創建 首先將UserFactory作為普通的bean配置到Spring中,然后再去調用UserFactory對象的createUser2方法 --> <bean name="user3" factory-bean="userFactory" factory-method="createUser2"></bean> <bean name="userFactory" class="com.wisedu.createObject.UserFactory"></bean> </beans>
測試方法:
@Test //創建方式3:實例工廠創建對象 public void test3(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/createObject/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user3"); //3.打印User對象 System.out.print(user); }
3. bean元素的scope屬性
scope屬性是在bean元素上加的。
- singleton :默認值,單例對象。被標識為單例的對象在Spring容器中只會存在一份實例。
- prototype :多例的。被標識為多例的對象每次在獲得時才會創建,並且每次創建都是新的對象。單例對象是在容器啟動時就創建了。
- request :WEB 項目中,Spring 創建一個 Bean 的對象,將對象存入到 request 域中。對象與request生命周期一致。 --基本不用
- session :WEB 項目中,Spring 創建一個 Bean 的對象,將對象存入到 session 域中。對象與session生命周期一致。 --基本不用
- globalSession :WEB 項目中,應用在 Porlet 環境。如果沒有 Porlet 環境那么 globalSession 相當於 session。 --基本不用
在未來的開發中,絕大多數scope的取值都是使用默認值,但是Spring與Struts2整合時,action對象要交給Spring來管理,action這個bean得配置為prototype。因為Struts2從架構上來說,每次請求都會創建一個新的action。
4. bean元素的生命周期屬性 --了解
通過配置<bean>標簽上的 init-method 作為 Bean 的初始化的時候執行的方法,Spring會在對象創建之后立即調用。配置 destroy-method 作為 Bean 的銷毀的時候執行的方法。
【注意】:銷毀方法想要執行,需要是單例創建的 Bean,而且在工廠關閉的時候,Bean 才會被銷毀.
5. Spring的分模塊配置文件
實際開發中,可能會把Sprign的配置寫到多個文件中,這時可以在Spring主配置文件中引入其他Spring配置文件。
<!-- 導入其他Spring配置文件--> <import resource="applicationContext.xml" />
6. Spring中的Bean的屬性注入
(1)set方法注入屬性 --最重要
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"> <!--set方式注入:值類型和引用類型--> <bean name="user" class="com.wisedu.springDemo.User"> <!--為User對象中名為name的屬性注入tom作為值--> <property name="name" value="tom"></property> <property name="age" value="18"></property> <!--為car屬性注入下方配置的car對象--> <property name="car" ref="car"></property> </bean> <!--必須將car對象配置到容器中--> <bean name="car" class="com.wisedu.springDemo.Car"> <property name="name" value="蘭博基尼"></property> <property name="color" value="黃色"></property> </bean> </beans>
User.java
package com.wisedu.springDemo; /** * Created by jkzhao on 12/7/17. */ public class User { private String name; private Integer age; private Car car; public User() { System.out.println("User對象空構造方法"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
Car.java
package com.wisedu.springDemo; /** * Created by jkzhao on 12/8/17. */ public class Car { private String name; private String color; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Car{" + "name='" + name + '\'' + ", color='" + color + '\'' + '}'; } }
測試方法:
@Test public void test1(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/injection/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user"); //3.打印User對象 System.out.println(user); }
運行結果:
(2)構造函數注入屬性 --重要
前提:類中得有帶有參數的構造函數。
在上面的User.java中添加擁有兩個參數的構造方法:
public User(String name, Car car) { System.out.println("User(String name, Car car)"); this.name = name; this.car = car; }
在applicationContext.xml文件中添加配置:
<!-- ============================================== --> <!-- 構造函數注入 --> <bean name="user2" class="com.wisedu.springDemo.User"> <constructor-arg name="name" value="jerry"></constructor-arg> <constructor-arg name="car" ref="car"></constructor-arg> </bean>
測試方法:
@Test public void test2(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/injection/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user2"); //3.打印User對象 System.out.println(user); }
運行結果:
但是假設我們User類中有多個構造方法,里面參數位置不一樣,代表實際業務中不同的邏輯,比如:
public User(String name, Car car) { System.out.println("User(String name, Car car)"); this.name = name; this.car = car; } public User(Car car, String name) { System.out.println("User(Car car, String name)"); this.name = name; this.car = car; }
我們如何在配置文件中指定走哪個構造方法呢?需要index屬性,index=0表示該屬性是構造方法中的第一個參數,index=1表示該屬性是構造方法中的第二個參數。
<!-- ============================================== --> <!-- 構造函數注入 --> <bean name="user2" class="com.wisedu.springDemo.User"> <constructor-arg name="name" value="jerry" index="0"></constructor-arg> <constructor-arg name="car" ref="car" index="1"></constructor-arg> </bean>
還有一種情況,構造方法是這樣的,代表另一種業務。
public User(String name, Car car) { //業務1 System.out.println("User(String name, Car car)"); this.name = name; this.car = car; } public User(Car car, String name) { //業務2 System.out.println("User(Car car, String name)"); this.name = name; this.car = car; } public User(Integer name, Car car) { //業務3 System.out.println("User(Integer name, Car car)"); this.name = name + ""; this.car = car; }
配置:
<!-- ============================================== --> <!-- 構造函數注入 --> <bean name="user2" class="com.wisedu.springDemo.User"> <!--name屬性:構造函數的參數名--> <!--index屬性:構造函數的參數索引--> <!--type屬性:構造函數的參數的類型--> <constructor-arg name="name" value="999" index="0" type="java.lang.Integer"></constructor-arg> <constructor-arg name="car" ref="car" index="1"></constructor-arg> </bean>
(3)p名稱空間注入屬性 --了解
需要引入p名稱空間。
<!-- ============================================== --> <!-- p名稱空間注入,本質走的還是set方法,只不過這種寫法是Spring新發明了,簡化了set的寫法。 --> <!--1.需要p名稱空間:xmlns:p="http://www.springframework.org/schema/p" 2.使用p:屬性注入 值類型:p:屬性名="值" 引用類型:p:屬性名-ref="bean名稱" --> <bean name="user3" class="com.wisedu.springDemo.User" p:name="jack" p:age="20" p:car-ref="car"></bean>
(4)spel注入屬性 --了解
<!-- ============================================== --> <!-- spel注入:Spring Expression Language,Spring表達式語言,類似el、jstl表達式--> <bean name="user4" class="com.wisedu.springDemo.User"> <property name="name" value="#{user.name}"></property> <!--找name="user"的對象,找它的屬性name--> <property name="age" value="#{user3.age}"></property> <property name="car" ref="car"></property> <!--引用類型不允許使用spel表達式--> </bean>
(5)復雜類型的注入
前面講的注入類型要么就是值,要么就是對象。如果遇到數組、Map、List或properties,該如何注入?直接使用set方式注入這幾種復雜類型。
CollectionBean.java
package com.wisedu.injection; import java.util.*; /** * Created by jkzhao on 12/8/17. */ public class CollectionBean { private Object[] arr; private List list; //list和set的注入方式一樣 private Map map; private Properties prop; //Properties就是個map public Object[] getArr() { return arr; } public void setArr(Object[] arr) { this.arr = arr; } public List getList() { return list; } public void setList(List list) { this.list = list; } public Map getMap() { return map; } public void setMap(Map map) { this.map = map; } public Properties getProp() { return prop; } public void setProp(Properties prop) { this.prop = prop; } @Override public String toString() { return "CollectionBean{" + "arr=" + Arrays.toString(arr) + ", list=" + list + ", map=" + map + ", prop=" + prop + '}'; } }
applicationContext.xml
<!--復雜類型注入--> <bean name="cb" class="com.wisedu.injection.CollectionBean"> <!--如果數組中只准備注入一個值(對象),直接使用value或ref即可。比如下面的配置直接往數組中注入了一個值tom--> <!--<property name="arr" value="tom"></property>--> <!--如果數組中准備注入多個元素--> <property name="arr"> <array> <value>tom</value> <value>jerry</value> <ref bean="user4"/> </array> </property> <!--如果List中只准備注入一個值(對象),直接使用value或ref即可。--> <!--<property name="list" value="jack"></property>--> <!--如果list中准備注入多個元素--> <property name="list"> <list> <value>jack</value> <value>rose</value> <ref bean="user3"/> </list> </property> <!--map類型注入--> <property name="map"> <map> <entry key="url" value="jdbc:mysql://crm"></entry> <entry key="car" value-ref="car"></entry> <entry key-ref="user3" value-ref="user4"></entry> </map> </property> <!--Properties類型注入--> <property name="prop"> <props> <prop key="driverClass">com.jdbc.mysql.Driver</prop> <prop key="userName">root</prop> <prop key="password">123456</prop> </props> </property> </bean>
測試方法:
@Test public void test5(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/injection/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) CollectionBean cb = (CollectionBean)ac.getBean("cb"); //3.打印User對象 System.out.println(cb); }
五、使用注解配置Spring(代替xml配置)
Hibernate、Struts2、Spring都支持注解方式配置。注解是JDK1.5開始引入的,這三大框架都是在JDK1.5以前出來的。但是在開發中很少使用注解來配置Hibernate和Struts2。
1. 注解示例
新建一個包,com.wisedu.annotation。
(1)Spring 4.2.4使用注解還需要導入一個包
spring-aop-4.2.4.RELEASE.jar 老版本不需要。
(2)為主配置文件引入新的命名空間(約束)
在默認生成的applicationContext.xml文件中添加配置
xmlns:context="http://www.springframework.org/schema/context"
在 xsi:schemaLocation= 后面添加如下配置:
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
(3)在主配置文件中開啟使用注解代理配置文件(打開使用注解的開發)
<?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"> <!--component可以理解為對象,我們把對象放進容器中,對Spring來說就是把組件放進來了--> <!--base-package: 掃描你給的這個包下所有的類,看上面有沒有注解。掃描到注解,會啟動注解配置。 如果這個包下面還有子包的話,還會掃描子包下的類 --> <context:component-scan base-package="com.wisedu"></context:component-scan> </beans>
(4)在類中使用注解完成配置
User.java
package com.wisedu.annotation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; /** * Created by jkzhao on 12/7/17. */ //@Component //組件 //<bean name="user" class="com.wisedu.annotation.User" /> @Component("user") public class User { private String name; private Integer age; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
其中,@Component()是Spring在注冊Bean的一個早期注解,后來又出現了這么幾個注解:@Service()、@Controller、@Repository()
一開始只有@Component(),但是后來使用者向Spring反饋,在注冊項目中所有對象的時候,都有這同一個注解,這樣很難區分這個對象屬於哪一層,所以又增加3個注解。這3個注解和@Component()沒有任何區別,但是這3個注解通過看名字,就知道注冊的對象屬於哪一層。
- @Service():注冊service層對象
- @Controller:注冊web層對象
- @Repository():注冊Dao層對象
(5)測試
test.java
package com.wisedu.annotation; import com.wisedu.annotation.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; /** * Created by jkzhao on 12/7/17. */ @RunWith(SpringJUnit4ClassRunner.class) //幫我們創建容器 @ContextConfiguration("classpath:com/wisedu/annotation/applicationContext.xml") //指定創建容器時使用哪個文件 //@ContextConfiguration(locations = "classpath:com/wisedu/annotation/applicationContext.xml") public class test { //將名為user的對象注入到u變量中 @Resource(name = "user") private User u; @Test public void test3(){ System.out.print(u); } @Test public void test1(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/annotation/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user"); //3.打印User對象 System.out.print(user); } }
2. 注解形式下對象的作用范圍
@Scope(scopeName = "prototype") //默認是單例的
package com.wisedu.annotation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; /** * Created by jkzhao on 12/7/17. */ //@Component //組件 //<bean name="user" class="com.wisedu.annotation.User" /> @Component("user") @Scope(scopeName = "prototype") //默認是單例的 public class User { private String name; private Integer age; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
測試方法:
@Test public void test2(){ //1.創建容器對象(創建Spring的工廠類) ApplicationContext ac = new ClassPathXmlApplicationContext("com/wisedu/annotation/applicationContext.xml"); //ClassPathXmlApplicationContext(從類路徑下加載xml的Application容器)是org.springframework.context.ApplicationContext的實現類 //2.向容器"要"User對象(通過工廠解析XML獲取Bean實例) User user = (User)ac.getBean("user"); User user2 = (User)ac.getBean("user"); User user3 = (User)ac.getBean("user"); //3.打印User對象 System.out.print(user==user2); }
3. 屬性的注入
(1)值賦值
在成員變量上面加注釋@Value(),或者把這個注釋放到這個成員變量的set方法上。
User.java
package com.wisedu.annotation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; /** * Created by jkzhao on 12/7/17. */ //@Component //組件 //<bean name="user" class="com.wisedu.annotation.User" /> @Component("user") @Scope(scopeName = "prototype") //默認是單例的 public class User { @Value("tom") private String name; private Integer age; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } @Value(value = "18") public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
這兩種的區別:
加在成員變量上,Spring在給這個對象賦值的時候,通過反射的Field賦值。而放在成員的set方法上,走的set方法賦值的(推薦使用)。
雖然結果是一樣的,但是放在成員變量上破壞了對象的封裝性。本來這個屬性私有,就是為了封裝,提供get和set方法操作,加在成員變量上就是Spring直接去操作對象了。
所以在set方法上加是推薦上,但是在成員變量上加很清晰,方便,在set方法上加很別扭,這就很矛盾。實際使用上兩種都可以。
另外,上面的示例中:@Value(value="18")和@Value("18")是一樣的,這是一個結論(直接記住):如果注解中的屬性如果只有一個需要賦值,並且這個屬性名是value的話,寫的時候可以忽略屬性的鍵,也就是忽略"value"。包括前面寫的@Component("user"),沒寫鍵的,都是給value賦值的。
(2)引用類型賦值
首先你必須把賦值的對象給注冊到容器中,比如上面示例中的car。
多種方式實現對象賦值:
第一種:
User.java代碼片段:
@Component("user") @Scope(scopeName = "prototype") //默認是單例的 public class User { @Value("tom") private String name; private Integer age; @Autowired //自動裝配。根據類型來檢測掃描容器當中符合這個屬性類型的對象,如果檢測到了,找到這個對象,賦值給這個屬性 @Qualifier("car2") private Car car;
...
其中,Car.java代碼片段:
@Component("car") public class Car { @Value("蘭博基尼") private String name; @Value("屎黃色") private String color; ...
運行結果:
這種注入引用類型有個缺陷,假如我把這個Car注冊到好幾遍這個容器當中,假如在容器中有3輛車。比如這個在applicationContext.xml中在配置輛車。
<bean name="car2" class="com.wisedu.annotation.Car"> <property name="name" value="特斯拉"></property> <property name="color" value="藍色"></property> </bean>
這樣Spring中就有兩輛車了。這樣我們無法控制具體將哪輛車注入Car屬性。這個時候我們可以給 @Autowired 加個輔助注解,
@Autowired @Qualifier("car2") private Car car;
再次運行:
第二種:當引用對象有多個的時候,需要把該對象注入給某屬性時,不建議使用上面兩個注解,有個注解不是自動裝配,直接“指名道姓”,叫@Resource(name = "car2")
//@Autowired //自動裝配。根據類型來檢測掃描容器當中符合這個屬性類型的對象,如果檢測到了,找到這個對象,賦值給這個屬性 //@Qualifier("car2") @Resource(name = "car2") //手動注入,指定注入哪個名稱的對象 private Car car;
4. 初始化和銷毀方法
User.java代碼片段:
@PostConstruct //在對象被創建后調用,類似於之前學習的init-method public void init(){ System.out.println("初始化方法"); } @PreDestroy //在對象銷毀之前調用,類似於之前學習的destory-method public void destory(){ System.out.println("銷毀方法"); }
【注意】:如果想執行銷毀方法,必須將User對象改為單例的。
Spring的Bean管理方式的比較:
XML和注解:
- XML:結構清晰
- 注解:開發方便(屬性注入)
實際開發中還有一種 XML 和注解整合開發:
- Bean 有 XML 配置,但是使用的屬性使用注解注入