Spring的兩大核心功能就是IoC和AOP,這篇文章主要介紹IoC。 簡單來講,在面向對象思想下,A類中有一個B類的屬性, 那麼咱們在建立A類時每每須要同時建立一個B類的對象,以便A類對其進行調用。可是,這樣的後果即是,A類和B類的耦合度太高。而所謂的IoC(控制反轉),其核心是DI,旨在提供一種更簡單的機制來設置組件依賴項(一般稱爲對象的協做者),並在整個生命週期中管理這些依賴項。IoC一般能夠分爲兩種子類型:依賴注入和依賴查找。java
public class ConstuctorInjecttion {
private Dependency dependency;
public ConstructorInjection(Dependency dependency) {
this.dependency = dependency;
}
}
複製代碼
構造函數注入屬於強制性的依賴注入,若是沒有依賴項的時候,就不能建立對象;所以,必須有依賴項。
複製代碼
public class SetterInjection {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
}
複製代碼
Setter注入屬於可選依賴注入,能夠在沒有依賴項的狀況下建立對象(調用默認的無參構造器), 而後經過調用setter方法來提供依賴項。
複製代碼
- 查找:
1.依賴拉取代碼必須主動得到對註冊表的依賴選項;
2.難以獨立於容器進行測試。
- 注入:
1.對代碼沒有任何影響;
2.能夠獨立於容器進行測試。
3.開發更加自由。
複製代碼
- 構造器注入:當對依賴項的依賴關係爲強制依賴的時候,推薦使用這種方式,經過調用對應的帶參數構造器來完成依賴的注入。
- setter方法注入:最簡單的注入方式。當對依賴項的依賴關係爲可選的時候,推薦使用這種方式。經過反射調用無參構造器來實例後,再調用對應的setter方法。這種方式的最大的好處就是:侵入性最小。
複製代碼
對於控制反轉,Spring的實現核心是基於依賴注入,而Spring的依賴注入的核心是BeanFactory接口。BeanFactory負責管理組件,包括依賴項以及他們的生命週期。可是,在實際的使用中,Beanfactory的使用並非不少,一般使用其擴展:ApplicationContext,其是BeanFactory的子接口。後面會有詳細介紹。
複製代碼
有三種配置形式,可是一般說兩種:xml文件和註解,(有些書籍上說第三種是自動裝配)。本文中主要介紹xml形式進行bean的配置。程序員
bean的配置方式有三種:spring
<!--
bean的配置方式1:經過全類名方式(反射)
id:IOC容器中惟一存在,若不指定會默認使用全類名
-->
<bean id="car" class="com.spring.demo.bean.Car">
<!--
複製代碼
<!--
bean的配置方式2:經過靜態工廠方法
id 屬性:指定 bean 的 id,用於從容器中獲取
class 屬性:指定靜態工廠的全限定類名
factory-method 屬性:指定生產對象的靜態方法
-->
<bean id="carByFactory" class="com.spring.demo.bean.StaticFactory"
factory-method="getCarBean">
</bean>
<!--
bean的配置方式2:經過實例工廠方法
此種方式是:先把工廠的建立交給 spring 來管理,而後在使用工廠的 bean 來調用裏面的方法
factory-bean 屬性:用於指定實例工廠 bean 的 id。
factory-method 屬性:用於指定實例工廠中建立對象的方法。
-->
<bean id="instanceFactory" class="com.spring.demo.bean.InstanceFactory"></bean>
<bean id="carByInstance" class="com.spring.demo.bean.Car" factory-bean="instanceFactory"
factory-method="getCarByInstance">
<property name="brand" value="大衆"/>
</bean>
複製代碼
依賴注入: Dependency Injection。 它是 spring 框架核心 ioc 的具體實現。咱們的程序在編寫時, 經過控制反轉, 把對象的建立交給了 spring,可是代碼中不可能出現沒有依賴的狀況。IoC 解耦只是下降他們的依賴關係,但不會消除。 例如:咱們的業務層仍會調用持久層的方法。那這種業務層和持久層的依賴關係, 在使用 Spring 以後, 就讓 Spring 來維護了。簡單的說,就是坐等框架把持久層對象傳入業務層,而不用咱們本身去獲取。 依賴注入的方式主要有兩種:數組
<!--
依賴注入方式1:屬性注入,屬性注入是經過setter方法注入bean的屬性值或者依賴的對象。
也是實際應用中最經常使用的方式。
name指定屬性值,value或者value節點指定屬性值,ref指定依賴的對象
-->
<property name="company" value="大衆"/>
<property name="brand" value="寶來"/>
<property name="price" value="200000.00"/>
<property name="maxSpeed" value="200"/>
</bean>
<!--
依賴注入方式2:構造器注入
經過構造方法注入屬性,可以保證bean在實例化後就能使用
沒有name屬性,默認按照構造器的變量順序加載,也能夠經過index或者type進行限制
index:指定參數在構造函數參數列表的索引位置
type:指定參數在構造函數中的數據類型
-->
<!--
若一個 bean 有多個構造器, 如何經過構造器來爲 bean 的屬性賦值
能夠根據 index 和 value 進行更加精確的定位. (瞭解)
也能夠經過name來具體制定屬性名進行綁定
若字面值中包含特殊字符, 則可使用 DCDATA 來進行賦值. (瞭解)
使用構造器方式注入屬性值時,可使用type或index來區別重載構造器
-->
<bean id="car1" class="com.spring.demo.bean.Car">
<constructor-arg value="寶馬" type="java.lang.String"/>
<constructor-arg value="mini" type="java.lang.String"/>
<constructor-arg value="200000.00" type="double"/>
<constructor-arg value="240" type="int"/>
</bean>
<!--
依賴注入方式3:工廠方法注入
由於這種方法比較少使用,這裏只是寫一下注釋,沒有demo
-->
複製代碼
BeanFactory是Spirng框架的基礎設施,面向Spring自己;ApplicationContext是BeanFactory的子接口,面向Spring框架的應用者,其提供了更多的功能。 ApplicationContext具備兩個只要的實現類:ClasspathXmlApplicationContext和FileSystemXmlApplicationContext;前者經過在類路徑下加載配置文件來完成bean的裝配,後者從文件系統下加載配置文件。項目結構以下所示: ![](img2018.cnblogs.com/blog/163258…bash
@Test
public void teStetter() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-ioc.xml");
Car car = (Car) context.getBean("car");
System.out.println(car);
}
複製代碼
字面值能夠用value標籤或者value屬性進行注入。基本類型及其包裝類、String等都是採用字面值的方式進行注入。當具備特殊字符的時候,須要特殊處理:session
<!--
value簡介:
基本類型及其包裝類、String均可以經過value標籤或者value屬性注入,
當字面值包含特殊字符的時候,可用<![CDATA[]]>包含
-->
<bean id="car2" class="com.spring.demo.bean.Car">
<property name="company" value="大衆"/>
<property name="brand">
<value><![CDATA[<DZ寶來>]]></value>
</property>
<property name="maxSpeed" value="200"/>
<property name="price" value="200000.00"/>
</bean>
複製代碼
spring中某一屬性不進行顯式聲明時,其會採用默認值,固然你能夠顯式的注入null:適應null標籤 同時,spring支持級聯屬性的注入。框架
當須要引用其餘的bean時(A類引用B類),能夠經過ref標籤或者ref屬性進行注入。其用法和value相似。泣別:value注入的是字面值,ref注入的是一個引用。固然,內部bean的注入也是這種方式。 示例以下:函數
<!--
引入其餘bean:
例如person有car屬性,須要引入,這是就不是使用value屬性了,
而是使用ref指定須要引入的bean
也能夠聲明一個內部bean,此時內部bean就無需再指定id了,道理和內部類類似
-->
<bean id="person" class="com.spring.demo.bean.Person">
<property name="name" value="麗麗"/>
<property name="age" value="23"/>
<property name="car" ref="car"/>
</bean>
<!-- innerBean -->
<bean id="person1" class="com.spring.demo.bean.Person">
<property name="name" value="麗麗"/>
<property name="age" value="23"/>
<property name="car">
<bean class="com.spring.demo.bean.Car">
<property name="brand" value="BMW"/>
<property name="company" value="BMW"/>
<property name="maxSpeed" value="233"/>
<property name="price" value="233333.33"/>
</bean>
</property>
</bean>
複製代碼
在Spring中能夠經過xml標籤配置集合屬性:測試
配置java.util.LIst和數組類型時,能夠經過標籤來配置,list中的屬性可使用value標籤或者ref標籤。 可使用utility scheme來單獨定義集合,這樣的好處就是方便其餘bean進行引用。ui
set的狀況和list類似,使用set標籤,用法和list標籤同樣。
<!--
集合屬性:
list標籤:數組和list數據使用這個標籤,
set標籤:set數據使用,使用方法同list標籤
-->
<bean id="stu" class="com.spring.demo.bean.Student">
<property name="name" value="蘇大強"/>
<property name="age" value="60"/>
<property name="cars">
<list>
<ref bean="car"/>
<ref bean="car1"/>
<ref bean="car2"/>
<ref bean="car3"/>
</list>
</property>
</bean>
<!--
也能夠單獨定義list,而後直接綁定
-->
<util:list id="carList">
<ref bean="car"/>
<ref bean="car1"/>
<ref bean="car2"/>
<ref bean="car3"/>
</util:list>
<bean id="stu1" class="com.spring.demo.bean.Student">
<property name="name" value="蘇大強"/>
<property name="age" value="60"/>
<property name="cars" ref="carList"/>
</bean>
複製代碼
java.util.Map能夠經過map標籤進行指定,map標籤中有多個entity子標籤,每一個entity定義一對鍵值對。 在entity中能夠經過key屬性或者key標籤來指定鍵,同理可使用value、ref、bean、null屬性或者標籤指定值。
<!--
map:
map的使用可list類似,使用entry標籤來保存每一對鍵值對,
同時可使用key屬性或者key標籤來存儲key,
value值或value屬性綁定普通的字面值,ref綁定其餘bean做爲值
-->
<bean id="teacher" class="com.spring.demo.bean.Teacher">
<property name="name" value="張莉"/>
<property name="age" value="24"/>
<property name="carMap">
<map>
<entry key="car" value-ref="car"/>
<entry key="car1" value-ref="car1"/>
<entry key="car2" value-ref="car2"/>
<entry key="car3" value-ref="car3"/>
</map>
</property>
</bean>
複製代碼
props標籤用來定義java.util.Properties,該標籤有子標籤prop,具體使用方法與map同樣。
Spring2.5以後支持p命名空間,能夠簡化xml配置,屬性與原來的配置同樣:
<!--
spring2.5以後,可使用p命名空間來簡化屬性配置,須要在配置文件中引入p命名空間
xmlns:p="http://www.springframework.org/schema/p"
-->
<bean id="car4" class="com.spring.demo.bean.Car" p:brand="寶馬" p:company="寶馬"
p:price="2000000.00" p:maxSpeed="200"/>
複製代碼
前面的例子都是經過xml文件進行bean的配置,如今來示範一下經過Java配置方式來配置。
@Configuration //該註解表示這個類是一個配置類,其做用至關於配置文件。
public class CarConfigure {
@Bean //該註解表示會向容器中配置一個bean,其value屬性是設置bean的id,
//沒有的話默認使用方法名做爲id,bean的類型就是返回值類型
public Car carByAnnotation() {
return new Car();
}
}
複製代碼
Spring的IOC容器能夠經過自動裝配來配置bean,使用方式就是使用autowire屬性,有兩種模式:byName和byType。 byName會在Spring容器中根據名字取尋找匹配的bean,沒有的話就沒法完成裝配; byType會在Spring容器中按照類型來查找並進行bean的配置,可是當找到多個符合條件的類型的bean時會報異常,不過能夠經過primary屬性或註解來制定bean的優先級,可是當你設置了多個同一類型的bean的primary屬性爲true的時候失效;這個時候,能夠藉助@Qualifier註解來加以限制。
<!-- 自動裝配: 只聲明 bean, 而把 bean 之間的關係交給 IOC 容器來完成 -->
<!--
byType: 根據類型進行自動裝配. 但要求 IOC 容器中只有一個類型對應的 bean, 如有多個則沒法完成自動裝配.
byName: 若屬性名和某一個 bean 的 id 名一致, 便可完成自動裝配. 若沒有 id 一致的, 則沒法完成自動裝配
-->
<!-- 在使用 XML 配置時, 自動轉配用的很少. 但在基於 註解 的配置時, 自動裝配使用的較多. -->
<bean id="car" class="com.spring.demo.bean.Car">
<property name="company" value="大衆"/>
<property name="brand" value="寶來"/>
<property name="price" value="200000.00"/>
<property name="maxSpeed" value="200"/>
</bean>
<bean id="person" class="com.spring.demo.bean.Person" autowire="byName">
<property name="name" value="James"/>
<property name="age" value="23"/>
<property name="car" ref="carSub"/>
</bean>
<!-- <bean id="person1" class="com.spring.demo.bean.Person" autowire="byType">
<property name="name" value="James"/>
<property name="age" value="23"/>
</bean>-->
<!--
自動裝配的缺點:
1.autowire默認會爲bean裝配所有屬性,因此當你只想裝配部分屬性時顯得不夠靈活;
2.byName和byType兩種模式只能選擇一個,不能兼有。
-->
@Test
public void testByName() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Person person = (Person) context.getBean("person");
System.out.println(person);
}
@Test
public void testByType() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Person person = (Person) context.getBean("person1");
System.out.println(person);
}
複製代碼
做爲一個Java程序員,確定會接觸到繼承。Spring中也提供了bean的繼承,思想和麪向對象的繼承的思想類似。區別在於Spring中的bean的繼承是指bean的配置能夠被繼承(複用)。
<!--
bean的繼承:
你能夠定義一個bean並有其餘bean繼承,這樣作的好處是能減小配置
父bean的配置會由子bean繼承,固然部分屬性除外(autowire,abstract)
子bean能夠從新賦值一些屬性,進行bean從新定義
父bean能夠定義爲abstract,定義爲abstract的bean只能被繼承,不能被註冊(實例化)
父bean能夠不定義class屬性,由子類本身定義
子bean須要經過parent屬性指定你繼承的父bean
-->
<bean id="carSup" class="com.spring.demo.bean.Car" p:company="寶馬" p:brand="mini"
p:maxSpeed="270" p:price="300000.00" abstract="true"/>
<bean id="carSub" parent="carSup"/>
@Test
public void testInherit() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Car car = (Car) context.getBean("carSub");
System.out.println(car);
}
複製代碼
在本文的開頭,咱們引入了A類和B類,當你想建立A類時,而A類又必須有B類的變量時,即可以使用依賴來解決這件事:
<!--
bean的依賴:
你能夠聲明當前bean依賴於哪些bean,這樣當前bean初始化以前會去初始化依賴的bean,
若是沒有註冊致使沒法初始化便會報錯
依賴多個bean的時候,可使用逗號或者空格分開
-->
<bean id="person1" parent="person" depends-on="carSub"/>
@Test
public void testDepends() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Person person1 = (Person) context.getBean("person1");
System.out.println(person1);
}
複製代碼
Spring中的bean的做用域有如下幾種: singleton, property, request,session 默認狀況下,spring中每一個bean只有一個實例,可是能夠顯示指定。
<!--
bean的生命週期:
singleton:默認選項,IOC容器默認爲每一個bean只實例化一個實例,該實例在調用具體的bean以前完成。
property:原型。該方式下IOC容器會在每次調用bean的時候實例化一個新的實例,而且只有在調用時進行bean的初始化
request和session同JavaWeb中同樣,在此很少加介紹
-->
<bean id="dog" class="com.spring.demo.bean.Dog" p:name="haki" scope="prototype"/>
package com.spring.demo.bean;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@AllArgsConstructor
@ToString
public class Dog {
private String name;
public Dog() {
super();
System.out.println("dog's constructor");
}
}
@Test
public void testScope() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Dog dog = (Dog) context.getBean("dog");
Dog dog1 = (Dog) context.getBean("dog");
System.out.println(dog == dog1);
}
複製代碼
在上面的demo中,除了能夠查看bean的建立個數,還應該查看bean的建立時機。