Spring學習總結(一)——Spring實現IoC的多種方式
目錄
- 一、Spring框架概述
- 二、IoC基礎
- 三、使用XML配置的方式實現IOC
- 四、使用Spring註解配置IOC
- 4.1、修改BookDAO
- 4.2、修改BookService
- 4.3、修改IOC配置檔案IOCBeans02.xml
- 4.4、簡單示例
- 4.5、作用域用scope來指定
- 4.5 、Lazy延遲初始化Bean
- 4.6、初始化回撥註解與銷燬回撥註解
- 4.7、預設名稱
- 4.6、小結
- 五、自動裝配
- 六、零配置實現IOC
- 七、通過註解@Value獲取properties配置
- 八、示例下載
- 九、視訊
- 十、作業
- 十一、總結
一、Spring框架概述
Spring是一個開源免費的框架,為了解決企業應用開發的複雜性而建立。Spring框架是一個輕量級的解決方案,可以一站式地構建企業級應用。Spring是模組化的,所以可以只使用其中需要的部分。可以在任何web框架上使用控制反轉(IoC),也可以只使用Hibernate整合程式碼或JDBC抽象層。它支援宣告式事務管理、通過RMI或web服務實現遠端訪問,並可以使用多種方式持久化資料。它提供了功能全面的MVC框架,可以透明地整合AOP到軟體中。
Spring被設計為非侵入式的,這意味著你的域邏輯程式碼通常不會依賴於框架本身。在整合層(比如資料訪問層),會存在一些依賴同時依賴於資料訪問技術和Spring,但是這些依賴可以很容易地從程式碼庫中分離出來。
Spring框架是基於Java平臺的,它為開發Java應用提供了全方位的基礎設施支援,並且它很好地處理了這些基礎設施,所以你只需要關注你的應用本身即可。
Spring可以使用POJO(普通的Java物件,plain old java objects)建立應用,並且可以將企業服務非侵入式地應用到POJO。這項功能適用於Java SE程式設計模型以及全部或部分的Java EE。
那麼,做為開發者可以從Spring獲得哪些好處呢?
不用關心事務API就可以執行資料庫事務;
不用關心遠端API就可以使用遠端操作;
不用關心JMX API就可以進行管理操作;
不用關心JMS API就可以進行訊息處理。
①JMX,Java Management eXtension,Java管理擴充套件,是一個為應用程式、裝置、系統等植入管理功能的框架。JMX可以跨越一系列異構作業系統平臺、系統體系結構和網路傳輸協議,靈活的開發無縫整合的系統、網路和服務管理應用。
②JMS,Java Message Service,Java訊息服務,是Java平臺上有關面向訊息中介軟體(MOM)的技術規範,它便於訊息系統中的Java應用程式進行訊息交換,並且通過提供標準的產生、傳送、接收訊息的介面簡化企業應用的開發。
一句話概括:Spring是一個輕量級的控制反轉(IoC)和麵向切面(AOP)的容器(框架)。
1.1、資源
文件:https://docs.spring.io/spring/docs/current/spring-framework-reference/、https://github.com/waylau/spring-framework-4-reference
框架下載地址:http://repo.springsource.org/libs-release-local/org/springframework/spring/
教程:http://www.yiibai.com/spring
Git:https://github.com/spring-projects
原始碼:https://github.com/spring-projects/spring-framework
Jar包:https://github.com/spring-projects/spring-framework/releases
1.2、Spring歷史
2002年,Rod Jahnson在《Expert One-on-One J2EE Design and Development》書中首次推出了Spring框架雛形interface21框架。
2004年3月24日,Spring框架以interface21框架為基礎,經過重新設計,釋出了1.0正式版。
從2004年3月到現在,已經經歷了1.0、1.1、1.2、2.0、2.5、3.0、3.1幾個主要的版本
3.2.0版釋出 2013年5月5日13:53
3.2.10版釋出 2014年7月15日23:58
3.2.9版釋出 2014年5月20日12:22
4.0.0版釋出 2013年12月12日07:50
4.0.1版釋出 2014年1月28日20:55
4.1.6版釋出 2015年3月25日16:40
4.2.2版釋出 2015年10月15日12:57
4.2.5版釋出 2016年2月25日09:28
4.3.5版釋出 2016年12月21日11:34
4.3.6版釋出 2017年1月25日14:05
4.3.8版釋出 2017年4月18日13:49
4.3.9版釋出 2017年6月7日19:29
5.0.0版釋出 2017年9月28日11:28
5.0.1版釋出 2017年10月24日15:14
1.3、框架特徵與功能
輕量:從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架可以在一個大小隻有1MB多的JAR檔案裡釋出。並且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的物件不依賴於Spring的特定類。
控制反轉Ioc:Spring通過一種稱作控制反轉(IoC)的技術促進了低耦合。當應用了IoC,一個物件依賴的其它物件會通過被動的方式傳遞進來,而不是這個物件自己建立或者查詢依賴物件。你可以認為IoC與JNDI相反——不是物件從容器中查詢依賴,而是容器在物件初始化時不等物件請求就主動將依賴傳遞給它。
面向切面Aop:Spring提供了面向切面程式設計的豐富支援,允許通過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用物件只實現它們應該做的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支援。
容器:Spring包含並管理應用物件的配置和生命週期,在這個意義上它是一種容器,你可以配置你的每個bean如何被建立——基於一個可配置原型(prototype),你的bean可以建立一個單獨的例項或者每次需要時都生成一個新的例項——以及它們是如何相互關聯的。然而,Spring不應該被混同於傳統的重量級的EJB容器,它們經常是龐大與笨重的,難以使用。
框架:Spring可以將簡單的元件配置、組合成為複雜的應用。在Spring中,應用物件被宣告式地組合,典型地是在一個XML檔案裡。Spring也提供了很多基礎功能(事務管理、持久化框架整合等等),將應用邏輯的開發留給了你。
MVC:Spring的作用是整合,但不僅僅限於整合,Spring 框架可以被看做是一個企業解決方案級別的框架,Spring MVC是一個非常受歡迎的輕量級Web框架。
所有Spring的這些特徵使你能夠編寫更乾淨、更可管理、並且更易於測試的程式碼。它們也為Spring中的各種模組提供了基礎支援。
1.4、Spring組成
Spring 框架是一個分層架構,由 7 個定義良好的模組組成。Spring 模組構建在核心容器之上,核心容器定義了建立、配置和管理 bean 的方式
組成 Spring 框架的每個模組(或元件)都可以單獨存在,或者與其他一個或多個模組聯合實現。每個模組的功能如下:
- 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要元件是
BeanFactory
,它是工廠模式的實現。BeanFactory
使用控制反轉(IOC) 模式將應用程式的配置和依賴性規範與實際的應用程式程式碼分開。 - Spring 上下文:Spring 上下文是一個配置檔案,向 Spring 框架提供上下文資訊。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和排程功能。
- Spring AOP:通過配置管理特性,Spring AOP 模組直接將面向方面的程式設計功能整合到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何物件支援 AOP。Spring AOP 模組為基於 Spring 的應用程式中的物件提供了事務管理服務。通過使用 Spring AOP,不用依賴 EJB 元件,就可以將宣告性事務管理整合到應用程式中。
- Spring DAO:JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同資料庫供應商丟擲的錯誤訊息。異常層次結構簡化了錯誤處理,並且極大地降低了需要編寫的異常程式碼數量(例如開啟和關閉連線)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
- Spring ORM:Spring 框架插入了若干個 ORM 框架,從而提供了 ORM 的物件關係工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
- Spring Web 模組:Web 上下文模組建立在應用程式上下文模組之上,為基於 Web 的應用程式提供了上下文。所以,Spring 框架支援與 Jakarta Struts 的整合。Web 模組還簡化了處理多部分請求以及將請求引數繫結到域物件的工作。
- Spring MVC 框架:MVC 框架是一個全功能的構建 Web 應用程式的 MVC 實現。通過策略介面,MVC 框架變成為高度可配置的,MVC 容納了大量檢視技術,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能可以用在任何 J2EE 伺服器中,大多數功能也適用於不受管理的環境。Spring 的核心要點是:支援不繫結到特定 J2EE 服務的可重用業務和資料訪問物件。毫無疑問,這樣的物件可以在不同 J2EE 環境 (Web 或 EJB)、獨立應用程式、測試環境之間重用。
Spring是一個開源的框架,現在的Spring框架構成了一個體系平臺,通過Spring的官方網站http://www.springsource.org可以瞭解到,圍繞著Spring框架本身,還有許多其他優秀的專案:
SpringFramework(Core):核心專案
Spring Web Flow:工作流專案
Spring Security:安全專案
Spring Batch:批量資料處理專案
Spring Android:Android系統支援專案
Spring Social:社交專案
1.5、Spring Boot與Spring Cloud
Spring Boot 是 Spring 的一套快速配置腳手架,可以基於Spring Boot 快速開發單個微服務,Spring Cloud是一個基於Spring Boot實現的雲應用開發工具;Spring Boot專注於快速、方便整合的單個微服務個體,Spring Cloud關注全域性的服務治理框架;Spring Boot使用了約束優於配置的理念,很多整合方案已經幫你選擇好了,能不配置就不配置,Spring Cloud很大的一部分是基於Spring Boot來實現,Spring Boot可以離開Spring Cloud獨立使用開發專案,但是Spring Cloud離不開Spring Boot,屬於依賴的關係。
SpringBoot在SpringClound中起到了承上啟下的作用,如果你要學習SpringCloud必須要學習SpringBoot。
二、IoC基礎
控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴注入)是實現IoC的一種方法,也有人認為DI只是IoC的另一種說法。沒有IoC的程式中我們使用面向物件程式設計物件的建立與物件間的依賴關係完全硬編碼在程式中,物件的建立由程式自己控制,控制反轉後將物件的建立轉移給第三方,個人認為所謂控制反轉就是:獲得依賴物件的方式反轉了。
IoC是Spring框架的核心內容,使用多種方式完美的實現了IoC,可以使用XML配置,也可以使用註解,新版本的Spring也可以零配置實現IoC。Spring容器在初始化時先讀取配置檔案,根據配置檔案或元資料建立與組織物件存入容器中,程式使用時再從Ioc容器中取出需要的物件。
採用XML方式配置Bean的時候,Bean的定義資訊是和實現分離的,而採用註解的方式可以把兩者合為一體,Bean的定義資訊直接以註解的形式定義在實現類中,從而達到了零配置的目的。
控制反轉是一種通過描述(XML或註解)並通過第三方去生產或獲取特定物件的方式。在Spring中實現控制反轉的是IoC容器,其實現方法是依賴注入(Dependency Injection,DI)。
三、使用XML配置的方式實現IOC
假設專案中需要完成對圖書的資料訪問服務,我們定義好了IBookDAO介面與BookDAO實現類
建立maven專案:
IBookDAO介面如下:
package com.zhangguo.Spring051.ioc01; /** * 圖書資料訪問介面 */ public interface IBookDAO { /** * 新增圖書 */ public String addBook(String bookname); }
BookDAO實現類如下:
package com.zhangguo.Spring051.ioc01; /** * 圖書資料訪問實現類 */ public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "新增圖書"+bookname+"成功!"; } }
Maven專案的pom.xml如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>Spring051</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Spring051</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> <version>4.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency> </dependencies> </project>
業務類BookService如下:
package com.zhangguo.Spring051.ioc01; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 圖書業務類 */ public class BookService { IBookDAO bookDAO; public BookService() { //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans01.xml"); //從容器中獲得id為bookdao的bean bookDAO=(IBookDAO)ctx.getBean("bookdao"); } public void storeBook(String bookname){ System.out.println("圖書上貨"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
容器的配置檔案IOCBeans01.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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bookdao" class="com.zhangguo.Spring051.ioc01.BookDAO"></bean> </beans>
測試類Test如下:
package com.zhangguo.Spring051.ioc01; public class Test { @org.junit.Test public void testStoreBook() { BookService bookservice=new BookService(); bookservice.storeBook("《Spring MVC權威指南 第一版》"); } }
執行結果:
3.1、使用無參構造方法建立物件
如下所示,則上下文會使用無參構造方法建立物件
<?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="tom" class="spring02.Student"></bean> </beans>
3.2、使用有參構造方法建立物件
Person.java
package spring02; /**人*/ public abstract class Person { public String name; }
Student.java
package spring02; /**學生*/ public class Student extends Person { /**身高*/ public int height; /**有參構造方法*/ public Student(String name,int height){ this.name=name; this.height=height; } @Override public String toString() { return "Student{" + "height=" + height+",name="+name +'}'; } }
School.java
package spring02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class School { public static void main(String[] args) { //IoC容器 ApplicationContext ctx= new ClassPathXmlApplicationContext("bookbean01.xml","beans02.xml"); //從容器中獲取物件 Person tom=ctx.getBean("tom",Person.class); System.out.println(tom); } }
配置檔案beans02.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="tom" class="spring02.Student"> <constructor-arg name="name" value="張柏川"></constructor-arg> <constructor-arg name="height" value="195"></constructor-arg> </bean> </beans>
執行結果:
注意:如果在使用構造方法時不想通過引數名稱指定引數則可以直接使用索引,如:
<bean id="rose" class="spring02.Student"> <constructor-arg index="0" value="張柏芝"></constructor-arg> <constructor-arg index="1" value="196"></constructor-arg> </bean>
3.3、通過屬性賦值
Address地址類:
package spring02; /**地址*/ public class Address { /**國家*/ private String country; /**城市*/ private String city; public Address() { } public Address(String country, String city) { this.country = country; this.city = city; } @Override public String toString() { return "Address{" + "country='" + country + '\'' + ", city='" + city + '\'' + '}'; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
配置檔案beans02.xml:
<bean name="zhuhai" class="spring02.Address"> <property name="country" value="中國"></property> <property name="city" value="珠海"></property> </bean>
測試程式碼:
package spring02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class School { public static void main(String[] args) { //IoC容器 ApplicationContext ctx= new ClassPathXmlApplicationContext("bookbean01.xml","beans02.xml"); //從容器中獲取物件 Person tom=ctx.getBean("rose",Person.class); Address zhuhai=ctx.getBean("zhuhai",Address.class); System.out.println(zhuhai); } }
執行結果:
便捷方式:
<?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"> <bean id="mark" class="entities.Person" p:height="100" p:name="mark"> </bean> </beans>
3.4、物件引用
你可以使用id 或(和) name 屬性來指定bean的識別符號
Person
package spring02; /**人*/ public abstract class Person { /**姓名*/ public String name; /**地址*/ public Address address; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
Student
package spring02; /**學生*/ public class Student extends Person { /**身高*/ public int height; /**有參構造方法*/ public Student(String name,int height){ this.name=name; this.height=height; } /**有參構造方法*/ public Student(String name,int height,Address address){ this.name=name; this.height=height; this.address=address; } @Override public String toString() { return "Student{" + "height=" + height+",name="+name +'}'+address; } }
配置檔案
<?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 name="zhuhai" class="spring02.Address"> <property name="country" value="中國"></property> <property name="city" value="珠海"></property> </bean> <bean id="tom" class="spring02.Student"> <constructor-arg name="name" value="張柏川"></constructor-arg> <constructor-arg name="height" value="195"></constructor-arg> <constructor-arg name="address" ref="zhuhai"></constructor-arg> </bean> <bean id="rose" class="spring02.Student"> <constructor-arg index="0" value="張柏芝"></constructor-arg> <constructor-arg index="1" value="196"></constructor-arg> <constructor-arg index="2" ref="zhuhai"></constructor-arg> </bean> </beans>
測試程式碼:
package spring02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class School { public static void main(String[] args) { //IoC容器 ApplicationContext ctx= new ClassPathXmlApplicationContext("bookbean01.xml","beans02.xml"); //從容器中獲取物件 Person tom=ctx.getBean("tom",Person.class); Person rose=ctx.getBean("rose",Person.class); //Address zhuhai=ctx.getBean("zhuhai",Address.class); System.out.println(tom); System.out.println(rose); } }
執行結果:
3.5、物件作用域
從容器中取回的物件預設是單例的:
Person roseA=ctx.getBean("rose",Person.class); Person roseB=ctx.getBean("rose",Person.class); //Address zhuhai=ctx.getBean("zhuhai",Address.class); System.out.println(tom); System.out.println(roseA==roseB);
執行結果:
使用scope屬性可以指定作用域
<bean id="rose" class="spring02.Student" scope="prototype"> <constructor-arg index="0" value="張柏芝"></constructor-arg> <constructor-arg index="1" value="196"></constructor-arg> <constructor-arg index="2" ref="zhuhai"></constructor-arg> </bean>
測試程式碼:
//從容器中獲取物件 Person tom=ctx.getBean("tom",Person.class); Person roseA=ctx.getBean("rose",Person.class); Person roseB=ctx.getBean("rose",Person.class); //Address zhuhai=ctx.getBean("zhuhai",Address.class); System.out.println(tom); System.out.println(roseA==roseB);
執行結果:
3.6、延遲初始化bean
ApplicationContext
實現的預設行為就是再啟動時將所有singletonbean提前進行例項化。 通常這樣的提前例項化方式是好事,因為配置中或者執行環境的錯誤就會被立刻發現,否則可能要花幾個小時甚至幾天。如果你不想 這樣,你可以將單例bean定義為延遲載入防止它提前例項化。延遲初始化bean會告訴Ioc容器在第一次需要的時候才例項化而不是在容器啟動時就例項化。
在XML配置檔案中,延遲初始化通過<bean/>
元素的lazy-init
屬性進行控制,比如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.foo.AnotherBean"/>
XML:
<bean id="mark" class="entities.Person" lazy-init="true" scope="prototype"> <property name="name" value="mark"></property> <property name="height" value="185"></property> </bean>
用例:
@Test public void testMethod3() throws Exception { //通過spring配置檔案初始化一個容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("springCfg.xml"); Thread.sleep(2000); Person mark1=ctx.getBean("mark",Person.class); Thread.sleep(2000); Person mark2=ctx.getBean("mark",Person.class); System.out.println(mark1==mark2); }
結果:
3.7、回撥方法
3.7.1、初始化回撥函式
配置檔案:
<bean id="tom" class="spring02.Student" init-method="init" destroy-method="over"> <constructor-arg name="name" value="張柏川"></constructor-arg> <constructor-arg name="height" value="195"></constructor-arg> <constructor-arg name="address" ref="zhuhai"></constructor-arg> </bean>
Student類:
package spring02; import java.io.File; /**學生*/ public class Student extends Person { /**身高*/ public int height; /**有參構造方法*/ public Student(String name,int height){ this.name=name; this.height=height; } /**有參構造方法*/ public Student(String name,int height,Address address){ this.name=name; this.height=height; this.address=address; } @Override public String toString() { return "Student{" + "height=" + height+",name="+name +'}'+address; } public void init(){ System.out.println("物件被建立"); } public void over(){ System.out.println("物件被回收"); } }
執行結果:
3.7.2、析構回撥函式
實現 org.springframework.beans.factory.DisposableBean 介面,允許一個bean當容器需要其銷燬時獲得一次回撥。 DisposableBean 介面也只規定了一個方法:
void destroy() throws Exception;
建議不使用 DisposableBean 回撥介面,因為會與Spring耦合。使用@PreDestroy 註解或者指定一個普通的方法,但能由bean定義支援。基於XML配置的元資料,使用 <bean/> 的 destroy-method 屬性。例如,下面的定義:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
示例
public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } }
與下面效果相同:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
示例:
public class AnotherExampleBean implements DisposableBean { public void destroy() { // do some destruction work (like releasing pooled connections) } }
但是不與Spring耦合。
四、使用Spring註解配置IOC
上一個示例是使用傳統的xml配置完成IOC的,如果內容比較多則配置需花費很多時間,通過註解可以減輕工作量,但註解後修改要麻煩一些,偶合度會增加,應該根據需要選擇合適的方法。
4.1、修改BookDAO
package com.zhangguo.Spring051.ioc02; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書資料訪問實現類 */ @Component("bookdaoObj") public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "新增圖書"+bookname+"成功!"; } }
在類上增加了一個註解Component,在類的開頭使用了@Component註解,它可以被Spring容器識別,啟動Spring後,會自動把它轉成容器管理的Bean。
除了@Component外,Spring提供了3個功能基本和@Component等效的註解,分別對應於用於對DAO,Service,和Controller進行註解。
1:@Repository 用於對DAO實現類進行註解。
2:@Service 用於對業務層註解,但是目前該功能與 @Component 相同。
3:@Constroller用於對控制層註解,但是目前該功能與 @Component 相同。
4.2、修改BookService
package com.zhangguo.Spring051.ioc02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Component public class BookService { IBookDAO bookDAO; public void storeBook(String bookname){ //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans02.xml"); //從容器中獲得id為bookdao的bean bookDAO=(IBookDAO)ctx.getBean("bookdaoObj"); System.out.println("圖書上貨"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
將構造方法中的程式碼直接寫在了storeBook方法中,避免迴圈載入的問題。
4.3、修改IOC配置檔案IOCBeans02.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:p="http://www.springframework.org/schema/p" 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-4.3.xsd"> <context:component-scan base-package="com.zhangguo.Spring051.ioc02"></context:component-scan> </beans>
粗體字是新增的xml名稱空間與模式約束檔案位置。增加了註解掃描的範圍,指定了一個包,可以通過屬性設定更加精確的範圍如:
<context>標記常用屬性配置:
resource-pattern:對指定的基包下面的子包進行選取
<context>子標記:
include-filter:指定需要包含的包
exclude-filter:指定需要排除的包
<!-- 自動掃描com.zhangguo.anno.bo中的類進行掃描-->
<context:component-scan base-package="com.zhangguo.anno" resource-pattern="bo/*.class" />
<context:component-scan base-package="com.zhangguo.anno" >
<context:include-filter type="aspectj“ expression="com.zhangguo.anno.dao.*.*"/>
<context:exclude-filter type=“aspectj” expression=“com.zhangguo.anno.entity.*.*”/>
</context:component-scan>
include-filter表示需要包含的目標型別,exclude-filter表示需要排除的目標型別,type表示採的過濾型別,共有如下5種類型:
Filter Type | Examples Expression | Description |
annotation | org.example.SomeAnnotation | 註解了SomeAnnotation的類 |
assignable | org.example.SomeClass | 所有擴充套件或者實現SomeClass的類 |
aspectj | org.example..*Service+ | AspectJ語法表示org.example包下所有包含Service的類及其子類 |
regex | org\.example\.Default.* | Regelar Expression,正則表示式 |
custom | org.example.MyTypeFilter | 通過程式碼過濾,實現org.springframework.core.type.TypeFilter介面 |
expression表示過濾的表示式。
<!-- 1、如果僅希望掃描特定的類而非基包下的所有類,可使用resource-pattern屬性過濾特定的類 --> <context:component-scan base-package="com.zhangguo.Spring051" resource-pattern="ioc04/A*.class"> </context:component-scan>
只掃描com.zhangguo.Spring051.ioc04下所有名稱以A開始的類。
<!--2、掃描註解了org.springframework.stereotype.Repository的類 exclude-filter表示排除,include-filter表示包含,可以有多個--> <context:component-scan base-package="com.zhangguo.Spring051.ioc04"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" /> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
<!--3、aspectj型別,掃描dao下所有的類,排除entity下所有的類--> <context:component-scan base-package="com.zhangguo.anno" > <context:include-filter type="aspectj" expression="com.zhangguo.anno.dao.*.*"/> <context:exclude-filter type="aspectj" expression="com.zhangguo.anno.entity.*.*"/> </context:component-scan>
測試類
package com.zhangguo.Spring051.ioc02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans02.xml"); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC權威指南 第二版》"); } }
執行結果:
4.4、簡單示例
IBookDao介面
package spring11; public interface IBookDao { void add(); }
BookDao實現類
package spring11; import org.springframework.stereotype.Component; @Component("bookdao") public class BookDao implements IBookDao { public void add() { System.out.println("新增圖書成功!"); } }
MSBookDao實現類
package spring11; import org.springframework.stereotype.Component; @Component("bookdao") public class MSBookDao implements IBookDao { public void add() { System.out.println("新增圖書到SQLServer成功!"); } }
BookService測試類:
package spring11; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BookService { public static void main(String[] args) { //容器 ApplicationContext ctx= new ClassPathXmlApplicationContext(new String[]{"bookbean11.xml"}); //從容器中獲得物件 IBookDao dao=ctx.getBean("bookdao",IBookDao.class); dao.add(); } }
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:p="http://www.springframework.org/schema/p" 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-4.3.xsd"> <bean name="tom" class="spring02.Student" p:name="tom" p:height="224"/> <!--指定要掃描的包,如果有多個可以用逗號隔開--> <context:component-scan base-package="spring11"> <!--使用正則排除以B開頭的類--> <context:exclude-filter type="regex" expression="spring11\.B.*"></context:exclude-filter> </context:component-scan> </beans>
執行結果:
指定兩個bookdao是不正確的,重名了,但是因為在元件掃描中我們排除了一個所有也可以正確執行。
4.5、作用域用scope來指定
預設容器中的bean是單例的:
//從容器中獲得物件 IBookDao dao1=ctx.getBean("bookdao",IBookDao.class); IBookDao dao2=ctx.getBean("bookdao",IBookDao.class); System.out.println(dao1==dao2);
結果:
修改成原型
package spring11; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component("bookdao") @Scope("prototype") public class MSBookDao implements IBookDao { public void add() { System.out.println("新增圖書到SQLServer成功!"); } }
結果:
用來指定bean的作用域
singleton---單例 只建立一個物件。
prototype---原型 想建立多少個就建立多少了。
request---針對Web專案,不同的請求建立單獨的Bean物件,同一個請求共享一個Bean。
session---針對Web專案,不同的會話建立單獨的Bean物件,同一個會話共享一個Bean。
4.5 、Lazy延遲初始化Bean
預設情況下Spring IoC容器在初始化時將Bean建立好存放到容器中:
測試:
@Test public void testMethod6() throws Exception { //通過spring配置檔案初始化一個容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("springCfg2.xml"); Thread.sleep(6000); IBookDao dao=ctx.getBean("mssql",IBookDao.class); dao.add("<<Java EE>>"); }
結果:
此處等待6秒...
增加註解修改成延遲初始化bean
package dao; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Repository; @Repository("mssql") @Lazy public class SQLServerBookDao implements IBookDao { public SQLServerBookDao() { System.out.println("SQLServerBookDao構造方法被呼叫"); } /** * 新增圖書 * * @param name */ public void add(String name) { System.out.println("新增圖書到SQLServer資料庫成功:"+name); } }
再次測試結果:
然後
4.6、初始化回撥註解與銷燬回撥註解
4.6.1、@PostConstruct
@PostConstruct 初始化方法的註解方式 等同與在XML中宣告init-method=init
package dao; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; @Repository("mssql") @Lazy public class SQLServerBookDao implements IBookDao { public SQLServerBookDao() { System.out.println("SQLServerBookDao構造方法被呼叫"); } /** * 新增圖書 * * @param name */ public void add(String name) { System.out.println("新增圖書到SQLServer資料庫成功:"+name); } //init-method callback @PostConstruct public void init(){ System.out.println("SQLServerBookDao初建立完成了"); } }
結果
4.6.2、@PreDestroy
@PreDestroy 銷燬方法的註解方式 等同於在XML中宣告destory-method=destory
package dao; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Repository("mssql") @Lazy public class SQLServerBookDao implements IBookDao { public SQLServerBookDao() { System.out.println("SQLServerBookDao構造方法被呼叫"); } /** * 新增圖書 * * @param name */ public void add(String name) { System.out.println("新增圖書到SQLServer資料庫成功:"+name); } //init-method callback @PostConstruct public void init(){ System.out.println("SQLServerBookDao初建立完成了"); } //destory-method callback @PreDestroy public void destory(){ System.out.println("SQLServerBookDao準備銷燬了"); } }
4.7、預設名稱
如果使用Compont註解時不指定名稱,基於@Componet及其擴充套件(如@Servic和自定義等)標註和classpath-scan定義的Bean,註解有一個value屬性,如果提供了,那麼就此Bean的名字。如果不提供。就會使用Spring預設的命名機制,即簡單類名且第一個字母小寫
package spring11; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component() //未指定名稱 @Scope("prototype") public class MSBookDao implements IBookDao { public void add() { System.out.println("新增圖書到SQLServer成功!"); } }
測試:
package spring11; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BookService { public static void main(String[] args) { //容器 ApplicationContext ctx= new ClassPathXmlApplicationContext(new String[]{"bookbean11.xml"}); //從容器中獲得物件 IBookDao dao1=ctx.getBean("MSBookDao",IBookDao.class); IBookDao dao2=ctx.getBean("MSBookDao",IBookDao.class); System.out.println(dao1==dao2); dao1.add(); } }
結果:
在基於XML的配置中bean標籤還有很多屬性,如scope、Lazy、init-method、depends-on、Qualifier等。
從容器中獲取例項時也可以直接根據型別獲取
package spring12; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; @Component public class BookStore { @Autowired BookService service; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean12.xml"); BookStore store =ctx.getBean(BookStore.class); store.service.addNewBook(); A a=ctx.getBean("a",A.class); System.out.println(a); } } @Component class A{ } @Component class B extends A{ }
結果:
預設名稱時需要將首字母小寫,Camel命名規範:
package dao; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; @Component public class MySqlBookDao implements IBookDao { /** * 新增圖書 * * @param name */ public void add(String name) { System.out.println("新增圖書到MySQL資料庫成功:"+name); } }
測試:
@Test public void testMethod5() throws Exception { //通過spring配置檔案初始化一個容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("springCfg2.xml"); IBookDao bookDao=ctx.getBean("mySqlBookDao",IBookDao.class); bookDao.add("《Spring從入門到精通》"); }
結果:
4.6、小結
從配置檔案中我們可以看出我們並沒有宣告bookdaoObj與BookService型別的物件,但還是從容器中獲得了例項併成功運行了,原因是:在類的開頭使用了@Component註解,它可以被Spring容器識別,啟動Spring後,會自動把它轉成容器管理的Bean。
五、自動裝配
從上一個示例中可以看出有兩個位置都使用了ApplicationContext初始化容器後獲得需要的Bean,可以通過自動裝配簡化。
5.1、修改BookDAO
package com.zhangguo.Spring051.ioc03; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書資料訪問實現類 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "新增圖書"+bookname+"成功!"; } }
把註解修改成了Repository,比Component更貼切一些,非必要。
5.2、修改BookService
package com.zhangguo.Spring051.ioc03; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { @Autowired IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println("圖書上貨"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
將類BookService上的註解替換成了Service;在bookDao成員變數上增加了一個註解@Autowired,該註解的作用是:可以對成員變數、方法和建構函式進行註解,來完成自動裝配的工作,通俗來說就是會根據型別從容器中自動查到到一個Bean給bookDAO欄位。@Autowired是根據型別進行自動裝配的,如果需要按名稱進行裝配,則需要配合@Qualifier。另外可以使用其它註解,@ Resource :等同於@Qualifier,@Inject:等同於@ Autowired。
@Service用於註解業務層元件(我們通常定義的service層就用這個)
@Controller用於註解控制層元件(如struts中的action)
@Repository用於註解資料訪問元件,即DAO元件
@Component泛指元件,當元件不好歸類的時候,我們可以使用這個註解進行註解。
裝配註解主要有:@Autowired、@Qualifier、@Resource,它們的特點是:
1、@Resource預設是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照型別來裝配注入;
2、@Autowired預設是按照型別裝配注入的,如果想按照名稱來轉配注入,則需要結合@Qualifier一起使用;
3、@Resource註解是又J2EE提供,而@Autowired是由spring提供,故減少系統對spring的依賴建議使用@Resource的方式;如果Maven專案是1.5的JRE則需換成更高版本的。
4、@Resource和@Autowired都可以書寫註解在欄位或者該欄位的setter方法之上
5、@Autowired 可以對成員變數、方法以及建構函式進行註釋,而 @Qualifier 的註解物件是成員變數、方法入參、建構函式入參。
6、@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。
7、@Autowired 註釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,通過屬性required可以設定非必要。
8、@Resource裝配順序
8.1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則丟擲異常
8.2. 如果指定了name,則從上下文中查詢名稱(id)匹配的bean進行裝配,找不到則丟擲異常
8.3. 如果指定了type,則從上下文中找到型別匹配的唯一bean進行裝配,找不到或者找到多個,都會丟擲異常
8.4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始型別進行匹配,如果匹配則自動裝配;
package com.zhangguo.Spring051.ioc05; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { public IBookDAO getDaoofbook() { return daoofbook; } /* @Autowired @Qualifier("bookdao02") public void setDaoofbook(IBookDAO daoofbook) { this.daoofbook = daoofbook; }*/ @Resource(name="bookdao02") public void setDaoofbook(IBookDAO daoofbook) { this.daoofbook = daoofbook; } /* @Autowired @Qualifier("bookdao02") */ IBookDAO daoofbook; /* public BookService(@Qualifier("bookdao02") IBookDAO daoofbook) { this.daoofbook=daoofbook; }*/ public void storeBook(String bookname){ System.out.println("圖書上貨"); String result=daoofbook.addBook(bookname); System.out.println(result); } }
測試執行
package com.zhangguo.Spring051.ioc03; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans03.xml"); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC權威指南 第三版》"); } }
執行結果:
5.3、簡單示例
IBookDao
package spring12; /**圖書資料訪問介面*/ public interface IBookDao { /**新增新書*/ void save(String name); }
BookDao
package spring12; import org.springframework.stereotype.Repository; /** * 完成圖書資料訪問 */ @Repository public class BookDao implements IBookDao { public void save(String name) { System.out.println("新增圖書" + name + "到資料庫成功!"); } }
BookService
package spring12; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class BookService { @Autowired IBookDao bookDao; /**新增一本書*/ public void addNewBook(){ String bookname="《Spring MVC學習指南》"; bookDao.save(bookname); } }
配置bookbean12.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:p="http://www.springframework.org/schema/p" 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-4.3.xsd"> <!--指定要掃描的包,如果有多個可以用逗號隔開--> <context:component-scan base-package="spring12"> </context:component-scan> </beans>
BookStore
package spring12; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; @Component public class BookStore { @Autowired BookService service; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean12.xml"); BookStore store =ctx.getBean(BookStore.class); store.service.addNewBook(); } }
測試結果:
5.4、@Qualifier指定名稱
@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。qualifier的意思是合格者,通過這個標示,表明了哪個實現類才是我們所需要的
先看下面這個示例:
package spring13; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; @Service public class QualifierTest { @Autowired IBookDao dao; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean13.xml"); QualifierTest obj = ctx.getBean(QualifierTest.class); System.out.println(obj.dao); } } interface IBookDao { } @Repository class BookDaoA implements IBookDao { } @Repository class BookDaoB implements IBookDao { }
執行結果:
這樣報錯的原因是找到了多個Bean,Spring不知道選擇那一個。使用Qualifier指定名稱
package spring13; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; @Service public class QualifierTest { @Autowired @Qualifier("daoA") IBookDao dao; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean13.xml"); QualifierTest obj = ctx.getBean(QualifierTest.class); System.out.println(obj.dao); } } interface IBookDao { } @Repository("daoA") class BookDaoA implements IBookDao { } @Repository("daoB") class BookDaoB implements IBookDao { }
結果:
5.5、@Resource
@Resource裝配順序
如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則丟擲異常
如果指定了name,則從上下文中查詢名稱(id)匹配的bean進行裝配,找不到則丟擲異常
如果指定了type,則從上下文中找到型別匹配的唯一bean進行裝配,找不到或者找到多個,都會丟擲異常
如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始型別進行匹配,如果匹配則自動裝配;
package spring13; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class ResourceTest { @Resource(name = "carB") ICarDao dao; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean13.xml"); ResourceTest obj = ctx.getBean(ResourceTest.class); System.out.println(obj.dao); } } interface ICarDao { } @Repository("carA") class CarDaoA implements ICarDao { } @Repository("carB") class CarDaoB implements ICarDao { } /* @Resource裝配順序 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則丟擲異常 如果指定了name,則從上下文中查詢名稱(id)匹配的bean進行裝配,找不到則丟擲異常 如果指定了type,則從上下文中找到型別匹配的唯一bean進行裝配,找不到或者找到多個,都會丟擲異常 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始型別進行匹配,如果匹配則自動裝配; */
執行結果:
5.6、多種注入方式
示例:
package spring13; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class InjectTest { //注入給構造方法 @Autowired public InjectTest(IUserDao dao2) { this.dao2=dao2; } //注入給成員變數,欄位 @Resource IUserDao dao1; IUserDao dao2; IUserDao dao3; IUserDao dao4; //注入給屬性 @Autowired public void setDao3(IUserDao dao3) { this.dao3 = dao3; } //注入給方法引數 @Autowired public void injectDao4(IUserDao dao4, IUserDao dao5) { this.dao4 = dao4; System.out.println(dao5); } public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bookbean13.xml"); InjectTest obj = ctx.getBean(InjectTest.class); System.out.println(obj.dao1); System.out.println(obj.dao2); System.out.println(obj.dao3); System.out.println(obj.dao4); } } interface IUserDao { } @Scope("prototype") @Repository class UserDao implements IUserDao { }
結果:
六、零配置實現IOC
6.1、綜合示例
所謂的零配置就是不再使用xml檔案來初始化容器,使用一個型別來替代,
IBookDAO程式碼如下:
package com.zhangguo.Spring051.ioc06; /** * 圖書資料訪問介面 */ public interface IBookDAO { /** * 新增圖書 */ public String addBook(String bookname); }
IBookDAO的實現類BookDAO程式碼如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書資料訪問實現類 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "新增圖書"+bookname+"成功!"; } }
在BookDAO類上註解了@Repository當初始化時該類將被容器管理會生成一個Bean,可以通過構造方法測試。
業務層BookService程式碼如下:
package com.zhangguo.Spring051.ioc06; import javax.annotation.Resource; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { @Resource IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println("圖書上貨"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
類BookService將對容器管理因為註解了@Service,初始化時會生成一個單例的Bean,型別為BookService。在欄位bookDAO上註解了@Resource,用於自動裝配,Resource預設是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照型別來裝配注入。
新增一個用於替代原xml配置檔案的ApplicationCfg類,程式碼如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 容器的配置類 */ @Configuration @ComponentScan(basePackages="com.zhangguo.Spring051.ioc06") public class ApplicationCfg { @Bean public User getUser(){ return new User("成功"); } }
@Configuration相當於配置檔案中的<beans/>,ComponentScan相當於配置檔案中的context:component-scan,屬性也一樣設定
,@Bean相當於<bean/>,只能註解在方法和註解上,一般在方法上使用,原始碼中描述:@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),方法名相當於id。中間使用到了User,User類的程式碼如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; @Component("user1") public class User { public User() { System.out.println("建立User物件"); } public User(String msg) { System.out.println("建立User物件"+msg); } public void show(){ System.out.println("一個學生物件!"); } }
初始化容器的程式碼與以前有一些不一樣,具體如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器,註解配置應用程式容器,Spring通過反射ApplicationCfg.class初始化容器 ApplicationContext ctx=new AnnotationConfigApplicationContext(ApplicationCfg.class); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC權威指南 第四版》"); User user1=ctx.getBean("user1",User.class); user1.show(); User getUser=ctx.getBean("getUser",User.class); getUser.show(); } }
容器的初始化通過一個型別完成,Spring通過反射ApplicationCfg.class初始化容器,中間user1與getUser是否為相同的Bean呢?
答案是否定的,因為在ApplicationCfg中宣告的方法getUser當相於在xml檔案中定義了一個<bean id="getUser" class="..."/>,在User類上註解@Component("user1")相當於另一個<bean id="user1" class="..."/>。
執行結果:
小結:使用零配置和註解雖然方便,不需要編寫麻煩的xml檔案,但並非為了取代xml,應該根據例項需要選擇,或二者結合使用,畢竟使用一個類作為容器的配置資訊是硬編碼的,不好在釋出後修改。
6.2、零配置,由註解指定例項
package spring14; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; public class NoXMLIoC { public static void main(String[] args) { //基於型別的配置 ApplicationContext ctx=new AnnotationConfigApplicationContext(AppCfg.class); ICarDao dao1=ctx.getBean(ICarDao.class); dao1.add("Spring Pro"); } } interface ICarDao{ void add(String name); } @Repository class CarDao implements ICarDao{ public void add(String name) { System.out.println("新增"+name+"成功!"); } } @Configuration @ComponentScan(basePackages = "spring14") class AppCfg{ }
執行結果:
6.3、零配置,由方法指定例項
package spring14; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; public class NoXMLIoC { public static void main(String[] args) { //基於型別的配置 ApplicationContext ctx=new AnnotationConfigApplicationContext(AppCfg.class); ICarDao dao2=ctx.getBean("mysqlDao",ICarDao.class); dao2.add("Spring Pro"); } } interface ICarDao{ void add(String name); } class CarDao implements ICarDao{ public void add(String name) { System.out.println("新增"+name+"成功!"); } } @Configuration @ComponentScan(basePackages = "spring14") class AppCfg{ @Bean ICarDao mysqlDao(){ //方法名就是bean的name return new CarDao(); } }
執行結果:
package spring14; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; public class NoXMLIoC { public static void main(String[] args) { //基於型別的配置 ApplicationContext ctx=new AnnotationConfigApplicationContext(AppCfg.class); ICarDao dao1=ctx.getBean("oracleDao",ICarDao.class); dao1.add("Spring Pro Oracle"); ICarDao dao2=ctx.getBean("mysqlDao",ICarDao.class); dao2.add("Spring Pro MySQL"); System.out.println(dao1==dao2); } } interface ICarDao{ void add(String name); } @Repository("oracleDao") class CarDao implements ICarDao{ public void add(String name) { System.out.println("新增"+name+"成功!"); } } @Configuration @ComponentScan(basePackages = "spring14") class AppCfg{ @Bean ICarDao mysqlDao(){ //方法名就是bean的name return new CarDao(); } }
執行結果:
七、通過註解@Value獲取properties配置
@value註解可以實現為物件賦值,可以直接指定值也可以從properties檔案中獲取,這裡主要講解兩種方式:
7.1、使用XML實現
1、在resource目錄下建立一個properties檔案,如db.properties:
2、在Spring配置檔案中匯入資原始檔
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd "> <context:component-scan base-package="com.zhangguo.spring03.demo.v32"></context:component-scan> <!--屬性點位,location用於指定資源位置,ignore-unresolvable忽視不能解析的內容--> <context:property-placeholder location="db.properties" ignore-unresolvable="true"></context:property-placeholder> </beans>
3、通過@Value引用資原始檔中的內容
package com.zhangguo.spring03.demo.v32; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; @Component public class DbUtils { @Value("com.jdbc.driver.Oracle") private String driver; @Value("${URL}") private String location; @Value("${userName}") private String uid; @Value("${password}") private String pwd; @Override public String toString() { return "DbUtils{" + "driver='" + driver + '\'' + ", location='" + location + '\'' + ", uid='" + uid + '\'' + ", pwd='" + pwd + '\'' + '}'; } public String getDriver() { return driver; } public void setDriver(String driver) { this.driver = driver; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getUid() { return uid; } public void setUid(String uid) { this.uid = uid; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
4、測試
package com.zhangguo.spring03.demo.v32; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Client32 { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("springCfg32.xml"); UserDao dao=ctx.getBean(UserDao.class); System.out.println(dao.dbUtils); } }
5、結果
7.2、使用零配置註解方式實現
1、建立配置類
package com.zhangguo.spring03.demo.v33; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; @Configuration @ComponentScan("com.zhangguo.spring03.demo.v33") @PropertySource("db.properties") public class SpringCfg { }
使用@ImportResource可能會產生的異常(前言中不允許有內容):
"C:\Program Files\Java\jdk1.8.0_111\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.1\lib\idea_rt.jar=5997:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar;C:\Users\Administrator\Desktop\mybatis-generator-gui-0.8.4\Spring02\target\classes;H:\InstallFiles\javaKit\mavenRes\junit\junit\4.12\junit-4.12.jar;H:\InstallFiles\javaKit\mavenRes\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;H:\InstallFiles\javaKit\mavenRes\cglib\cglib\3.2.4\cglib-3.2.4.jar;H:\InstallFiles\javaKit\mavenRes\org\ow2\asm\asm\5.1\asm-5.1.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\ant\ant\1.9.6\ant-1.9.6.jar;H:\InstallFiles\javaKit\mavenRes\org\apache\ant\ant-launcher\1.9.6\ant-launcher-1.9.6.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-context\4.3.0.RELEASE\spring-context-4.3.0.RELEASE.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-aop\4.3.0.RELEASE\spring-aop-4.3.0.RELEASE.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-beans\4.3.0.RELEASE\spring-beans-4.3.0.RELEASE.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-core\4.3.0.RELEASE\spring-core-4.3.0.RELEASE.jar;H:\InstallFiles\javaKit\mavenRes\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;H:\InstallFiles\javaKit\mavenRes\org\springframework\spring-expression\4.3.0.RELEASE\spring-expression-4.3.0.RELEASE.jar;H:\InstallFiles\javaKit\mavenRes\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar" com.zhangguo.spring03.demo.v33.Client33 十月 22, 2018 11:30:15 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 資訊: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@685f4c2e: startup date [Mon Oct 22 11:30:15 CST 2018]; root of context hierarchy 十月 22, 2018 11:30:17 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 資訊: Loading XML bean definitions from class path resource [db.properties] 十月 22, 2018 11:30:17 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext refresh 警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 1 in XML document from class path resource [db.properties] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 前言中不允許有內容。 Exception in thread "main" org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 1 in XML document from class path resource [db.properties] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 前言中不允許有內容。 at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:399) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromImportedResources(ConfigurationClassBeanDefinitionReader.java:346) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:142) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:116) at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:333) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:243) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:273) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:98) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:681) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:523) at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84) at com.zhangguo.spring03.demo.v33.Client33.main(Client33.java:9) Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 前言中不允許有內容。 at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203) at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177) at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400) at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327) at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1472) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:994) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:602) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:505) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:841) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:770) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243) at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:339) at org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoader.java:76) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(XmlBeanDefinitionReader.java:429) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391) ... 16 more Process finished with exit code 1
2、測試
package com.zhangguo.spring03.demo.v33; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Client33 { public static void main(String[] args) { ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringCfg.class); UserDao dao=ctx.getBean(UserDao.class); System.out.println(dao.dbUtils); } }
3、結果
7.3、其它方式
假如有以下屬性檔案dev.properties, 需要注入下面的tag
tag=123
通過PropertyPlaceholderConfigurer
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="dev.properties" /> </bean>
程式碼
@Value("${tag}") private String tag;
通過PreferencesPlaceholderConfigurer
<bean id="appConfig" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"> <property name="location" value="dev.properties" /> </bean>
程式碼:
@Value("${tag}") private String tag;
通過PropertiesFactoryBean
<bean id="config" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location" value="dev.properties" /> </bean>
程式碼:
@Value("#{config['tag']}") private String tag;
通過util:properties
效果同PropertiesFactoryBean一樣
程式碼:
@Value("#{config['tag']}") private String tag;
八、示例下載
https://git.coding.net/zhangguo5/Spring.git
九、視訊
https://www.bilibili.com/video/av16071354/
十、作業
第一次作業
1.1、請完成所有基於XML配置Ioc上課示例。
1.2、請學會使用幫助文件
1.3、請定義一個Animal動物類(名稱name,重量weight),定義兩個Animal子類貓Cat(品種type)與狗Dog(顏色color)
1.4、以Animal、Cat、Dog為基礎練習3.1-3.6的所有知識點
1.5、請寫一個微型的Ioc框架(MiniIoc),實現最基礎的控制反轉功能(選做)
第二次作業
2.1、請完成所有基於註解、自動裝配的Ioc上課示例。
2.2、請定義一個Animal動物類(名稱name,重量weight),定義兩個Animal子類貓Cat(品種type)與狗Dog(顏色color)
2.3、以Animal、Cat、Dog為基礎練習基於註解與自動裝配所有知識點,這裡使用XML作為配置檔案
第三次作業
3.1、請完成所有基於零配置實現Ioc上課示例。
3.3、以Animal、Cat、Dog為基礎練習基於註解、自動裝配與零配置的綜合示例,儘量貫穿所有知識點,這裡不使用XML作為配置檔案
十一、總結
@Compent 要求Spring容器管理,被Spring掃描,應用於不確定的功能(寬泛)
@Repository 應用於資料訪問層 DAO
@Service 應用於業務層
@Controller 應用於控制層
註解要被Spring容器管理的類 -> 配置檔案指定要掃描的包 ->初始化容器,獲得bean
@Autowired 自動裝配,欄位(成員變數)、方法、屬性、構造, 不支援指定名稱,配合@Qualifier
@Resource 自動裝配,指定名稱,指定型別,不屬於Spring javax
@Qualifier 在自動裝配時指定物件的名稱,避免多個不唯一的例項
@Autowired()
@Qualifier("z")
X x;
@Resource(name="z")
X x;
X
@Compent("y")
Y:X
@Compent("z")
Z:X