一、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、資源
官網: http://spring.io
文檔: https://docs.spring.io/spring/docs/current/spring-framework-reference/、 https://github.com/waylau/spring-framework-4-reference
中文幫助: http://spring.cndocs.ml/
框架下載地址: 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實現的默認行為就是再啟動時將所有 singleton bean提前進行實例化。 通常這樣的提前實例化方式是好事,因為配置中或者運行環境的錯誤就會被立刻發現,否則可能要花幾個小時甚至幾天。如果你不想 這樣,你可以將單例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
