1. IOC(Inversion of Control)與DI(Dependency Injection)
“控制反轉(zhuǎn)”和“依賴注入”其實(shí)是同一個(gè)概念:
改變由調(diào)用者創(chuàng)建被調(diào)用者的過程。被調(diào)用者依賴Spring容器注入到調(diào)用者中眉睹。
之所以出現(xiàn)兩個(gè)名詞是因?yàn)?strong>“控制反轉(zhuǎn)”比較難懂许起,聽者不明所以鹿寻,所以有了更形象的“依賴注入”财搁。
2. IOC的發(fā)展歷程
3. Spring容器
簡單說,就是Spring中生成“Bean”的工廠蜓洪,并管理“Bean”的生命周期裳仆。Spring的配置文件說到底就是“Bean”的配置纯丸。
“Bean”是Spring管理的基本單位滑凉,不僅僅指簡單的POJO類,組件也是“Bean”,例如數(shù)據(jù)源、事務(wù)管理器行冰,Hibernate的SessionFactory邻吞。
Spring中內(nèi)置了兩種基礎(chǔ)DI容器:BeanFactory
和ApplicationContext
瘦癌。
ApplicationContext
繼承了BeanFactory
的所有特性。關(guān)系如下圖:
BeanFactory
應(yīng)用與內(nèi)存跷敬、CPU資源受限的場合讯私,一般情況下優(yōu)先使用ApplicationContext
,因?yàn)樗藫碛?code>BeanFactory支持的所有功能西傀,還可以:
- 載入多個(gè)配置文件
- 提供國際化支持
- 事件機(jī)制
-
默認(rèn)初始化所有的singleton Bean(
BeanFactory
則不會)
常用的ApplicationContext
的子類如下圖:
- XmlWebApplicationContext:面向Web應(yīng)用
- AnnotationConfigApplicationContext:基于注解存儲DI元數(shù)據(jù)
- XmlPortlerApplicationContext:面向Portal應(yīng)用
- ClassPathXmlApplicationContext斤寇、FileSystemXmlApplicationContext:適用于各種場景
4. 注入方式
Spring的注入方式有三種:設(shè)值注入、構(gòu)造器注入拥褂、工廠注入娘锁。
設(shè)置注入和構(gòu)造器注入
實(shí)體類:
public class Person{
public String name;
private Axe axe;
public Person(Axe axe, String name) {
System.out.println("Person 的帶參構(gòu)造方法");
this.axe = axe;
this.name = name;
}
/**
* 砍柴
*/
public void choopWood() {
if(axe!=null){
axe.useAxe();
}
}
public void info() {
System.out.println("This person's name is " + name);
}
public void setName(String name) {
this.name = name;
}
public void setAxe(Axe axe) {
this.axe = axe;
}
}
注入類:
public interface Axe {
public void useAxe();
}
public class StoneAxe implements Axe {
public void useAxe() {
System.out.println(test + "石斧砍柴很慢");
}
}
public class IronAxe implements Axe{
public void useAxe() {
System.out.println("鐵斧砍柴很快");
}
}
Spring配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!--spring配置文件的根元素,使用spring-beans-3.0.xsd語義約束(.xsd都是語言約束文件)
xmlns:p配置可使用p標(biāo)簽更方便的配置屬性 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- id 表示你這個(gè)組件的名字饺鹃,class表示組件類莫秆;autowire為是否自動加載; lazy-init為是否懶加載悔详,即applicationContext容器啟動不自動加載 -->
<bean id="person" class="test.wsz.spring.bean.Person" lazy-init="true" autowire="byType" >
<!--自定義set注入,name 為成員變量名稱镊屎,value為自定義的值 -->
<property name="name" value="wsz"></property>
<!--引用set注入,name 為成員變量名稱,ref為注入bean的id -->
<property name="axe" ref="ironaxe"></property>
<!--構(gòu)造注入,index為形參的位置茄螃,從0開始缝驳,ref為注入bean的id -->
<constructor-arg ref="stoneaxe" index="0" />
<constructor-arg value="wsz" index="1" />
</bean>
<!--其余實(shí)體Bean-->
<bean id="ironaxe" class="test.wsz.spring.bean.IronAxe" />
<bean id="stoneaxe" class="test.wsz.spring.bean.StoneAxe" />
</beans>
從配置的注釋可以知道“設(shè)置注入”和“構(gòu)造器注入”的配置方法。
- “設(shè)置注入”會調(diào)用成員變量的
setXXX
方法责蝠。 - “構(gòu)造器注入”會調(diào)用類對應(yīng)的構(gòu)造方法党巾。
測試代碼:
ApplicationContext context=new ClassPathXmlApplicationContext("config/spring-config.xml");
Person person=context.getBean("person",Person.class);
person.info();
person.choopWood();
工廠方式注入
工廠方式注入有好幾種,這里只舉“靜態(tài)工廠注入”和“工廠Bean的例子”
- 靜態(tài)工廠注入
先創(chuàng)建一個(gè)工廠類:
/**
* 靜態(tài)簡單工程類
*/
public class AxeFactory {
/**
* 獲取產(chǎn)品的方法霜医,根據(jù)形參返回不同的產(chǎn)品
*/
public static Axe getAxe(String type) {
if("stone".equals(type))
return new StoneAxe();
else if("iron".equals(type))
return new IronAxe();
return new StoneAxe();
}
}
再修改之前的類當(dāng)成產(chǎn)品類:
public class StoneAxe implements Axe {
private String test;
public StoneAxe() {
System.out.println("調(diào)用StoneAxe的無參構(gòu)造方法");
}
public void useAxe() {
System.out.println(test + "石斧砍柴很慢");
}
public void setTest(String test) {
this.test = test;
}
}
再在xml文件中進(jìn)行對應(yīng)的配置:
<!--靜態(tài)工廠類獲取Bean,class為工廠類齿拂;factory-method為工廠獲取方法-->
<bean id="stone" class="test.wsz.spring.factory.AxeFactory" factory-method="getAxe">
<!--設(shè)置傳入工廠類獲取產(chǎn)品方法形參的值-->
<constructor-arg value="stone"/>
<!--設(shè)置注入產(chǎn)品Bean的屬性-->
<property name="test" value="哈哈,"></property>
</bean>
根據(jù)配置我們可以知道肴敛,現(xiàn)在設(shè)置的工廠類返回StoneAxe
署海,我們進(jìn)行測試:
Axe axe=context.getBean("stone",Axe.class);
axe.useAxe();
輸出:
哈哈,石斧砍柴很慢
-
工廠Bean注入
這種方式配置比較簡單医男。只需要創(chuàng)建一個(gè)實(shí)現(xiàn)了Spring工廠接口的工廠類砸狞,再將這個(gè)工廠類配置在<Beans />
中即可。
工廠類镀梭,需實(shí)現(xiàn)接口的三個(gè)方法:
public class AxeBeanFactory implements FactoryBean<Axe>{
Axe axe=null;
/**
* 返回產(chǎn)品
*/
public Axe getObject() throws Exception {
if(axe==null){
axe=new StoneAxe();
}
return axe;
}
/**
* 返回產(chǎn)品的類型
*/
public Class<?> getObjectType() {
return Axe.class;
}
/**
* 是否單例
*/
public boolean isSingleton() {
return true;
}
}
xml配置:
<!--容器中的工廠Bean-->
<bean id="stonefactory" class="test.wsz.spring.factory.AxeBeanFactory" />
值得注意的是刀森,雖然<Bean />
中配置的好像是工廠類,但實(shí)現(xiàn)了Spring工廠接口的該類报账,最終getBean()
時(shí)返回的是對應(yīng)的產(chǎn)品類研底。
測試代碼:
Axe axe=context.getBean("stonefactory",Axe.class);
axe.useAxe();
輸出:
null石斧砍柴很慢
5. 配置細(xì)節(jié)
Spring的配置的參數(shù)太多埠偿,這里只總結(jié)一些常用的。復(fù)雜的等用到時(shí)再看書就行榜晦。
<bean />
中的常用配置
- scope
Bean的作用域冠蒋,有5種: - singleton:單例模式,即整個(gè)容器里面該Bean實(shí)例只有一個(gè)乾胶,默認(rèn)
- prototype:原型模式抖剿,每次
getBean
獲取的是一個(gè)新創(chuàng)建的對象 - request:每個(gè)HTTP請求,產(chǎn)生一個(gè)實(shí)例
- session:每個(gè)HTTP session识窿,產(chǎn)生一個(gè)實(shí)例
- global session:每個(gè)全局的HTTP session斩郎,產(chǎn)生一個(gè)實(shí)例
- lazy-init:
懶加載,當(dāng)使用使用ApplicationContext
時(shí)腕扶,會自動初始化所有的singleton
的“Bean”(“Bean”默認(rèn)是singleton
)孽拷。設(shè)置懶加載即不自動初始化這個(gè)類吨掌。 - autowire:
自動注入半抱。即你不用在配置中設(shè)定成員變量的注入。設(shè)置為byType
時(shí)膜宋,會自動匹配依賴類型相同的Bean窿侈,若有多個(gè)類型相同的會拋出異常。設(shè)置為byName
時(shí)秋茫,會查找id名與屬性名相等的Bean史简。 - init-method:
指定Bean依賴關(guān)系設(shè)置完畢后執(zhí)行的方法。另外一種實(shí)現(xiàn)方式是讓Bean類實(shí)現(xiàn)InitializingBean
接口 - destory-method
指定Bean銷毀之前執(zhí)行的方法肛著。另外一種實(shí)現(xiàn)方式是讓Bean類實(shí)現(xiàn)DisposableBean
接口 - p名稱空間
可以看作一個(gè)語法糖圆兵,在<beans />
中添加:
xmlns:p="http://www.springframework.org/schema/p"
即可更簡單的設(shè)置注入:
<bean id="person" class="test.wsz.spring.bean.Person"
p:name="name" />
等效于
<bean id="person" class="test.wsz.spring.bean.Person" >
<property name="name" value="wsz"></property>
</bean>
6. 免XML的JAVA類配置
這個(gè)其實(shí)應(yīng)用場景比較少,畢竟單純用Java類完成復(fù)雜的Spring配置還是比較困難的枢贿。下面簡單給個(gè)例子:
@Configuration
public class Config {
@Value("王樹政") String personName; //注入Bean的屬性中的值
@Bean(name="person")
public Person person(){
Person p=new Person();
p.setName(personName);
p.setAxe(stoneAxe());
return p;
}
@Bean(name="stoneAxe")
public Axe stoneAxe() {
Axe axe=new StoneAxe();
return axe;
}
}
除了純粹的Java類配置殉农,還可以在xml中引用Java類配置,或者在Java類配置中引用xml配置局荚。有興趣的朋友可以自行百度超凳。