文章目錄
前言
經過上一節,對spring的介紹,相信讀者,對spring,有了大致的瞭解。大量篇幅的文字,看着就有點讓人不耐煩,下面,我們就着手動起來。關於此係列的學習,依賴都是通過maven 去管理的。
Eclipse 安裝spring插件
工慾善其事必先利其器,在eclipse 安裝spring 插件將有利於我們的學習。
安裝的時候,一定要注意eclipse 的版本,另外,spring 官網,一般都是最新的插件,在哪找歷史的。
添加鏈接描述,可以參考這篇博客,安裝的步驟,就不說,網上一大堆。
Helloworld Spring
首先先添加Spring 的依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
定義一個POJO Java 對象類
public class HelloWord {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
定義Beans.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="helloworld" class="test.HelloWord">
<property name="message" value="hello spring"></property>//這裏的name 是你的屬性名,必須和POJO 對象的屬性一致,value 是傳入該屬性的值
</bean>
</beans>
對於Bean 的Id ,我們一定要保證它的唯一性。
創建的Beans.xml 的目錄最好在maven工程下的src/main/java,這就是相當於傳統Java 項目的src(src是目錄的頂層),所以上面class,是從test開始的。還有注意的,Beans.xml 這個命名不是隨意的
在main 函數裏面測試:
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWord h = (HelloWord) context.getBean("helloworld");//這裏的就是Beans.xml的配置的bean 的id,去獲取該POJO 對象的。
System.out.println(h.getMessage());
ClassPathXmlApplicationContext 這個是負責加載配置文件,以及實例化所有的對象。
Spring 容器
容器負責創建對象,配置對象,管理對象的生命週期,從創建到消亡。這些對象,在Spring 中,被稱之爲Bean。配置Bean 的元數據庫,可以是在Xml、Java 註解或者Java 代碼中。(比如上面的例子,就是在Xml)。
下面是容器工作圖:
Spring 容器類型
分兩種:
- Spring BeanFactory Container
最簡單的容器
- Spring ApplicationContext Container
這個容器,比上面的更高級,對於企業級應用,推薦使用這個。它的功能包含上面容器所有的。
Bean 定義
對象在Spring 容器中管理,從稱之爲Bean。Bean 一個對象,但是該對象是被實例化了,組裝屬性的。這些Bean 被創通過你在配置的元數據,去創建。
Bean 的定義,被稱之爲配置元數據,你配置這些數據,去讓容器知道:
- 怎麼去創建Bean
- Bean 的生命週期
- Bean 的依賴
Bean 的元數據配置是通過下面一系列的方法:
property | Description |
---|---|
class | 這個屬性是必須的,指定Bean 的class |
name | 這個屬性是區分Bean 的,也是通過該屬性獲取Bean 對象,所以必須保證唯一性 |
scope | 指明Bean 的作用域 |
constructor-arg | 被用來指定依賴,後面有細講 |
properties | 被用來指定依賴,後面有細講 |
autowiring mode | 被用來指定依賴,後面有細講 |
lazy-initialization mode | 懶加載,告訴容器創建Bean 實例,不是在開始,而是在第一次請求 |
initialization method | 初始化方法,所有的屬性都由容器創建好了,回調的方法 |
destruction method | 銷燬方法,所有的屬性都由容器創建好了,回調的方法 |
Bean 元數據配置方式
- 基於Xml
- 基於註解
- 基於Java 代碼
Bean 作用域
Scope | Description |
---|---|
singleton(單例) | 該Bean作用域是整個容器, 只有一個Bean 實例 |
prototype(原型) | 該Bean作用域是,一個Bean可以有多個實例 |
request | 這將bean定義範圍限定爲HTTP請求。 在Web應用中,爲每個請求創建一個bean實例。 |
session | 這將bean定義範圍限定爲session。 在Web應用中,爲每個會話創建一個bean實例 |
Singleton
將上述Hello world 代碼,main 中的代碼,做如下的修改:
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWord h = (HelloWord) context.getBean("helloworld");
h.setMessage("i am new Spring");
System.out.println(h.getMessage());
HelloWord h1 = (HelloWord) context.getBean("helloworld");
System.out.println(h1.getMessage());
這裏句不需要修改Beans.xml,因爲Bean 默認就是這種作用域。
執行之後,你會發現兩次打印的一致。實際上,採用這種模式的作用域,容器將把第一次實例的對象,放到緩存中,之後所有的請求,都是在緩存中拿,而不是重新去創建新的實例。
prototype
這種作用域,容器會爲每一個Bean 創建新的實例。
基於上面的例子,我們修改Beans.xml:
<bean id="helloworld" class="test.HelloWord" scope="prototype">
同樣運行,這次,就一樣的結果。說明每一個獲取的Bean 實例,是不同的。
Bean 生命週期
當一個Bean 需要使用的時候,它通過容器去創建,當不需要的,從容器上移除。
public class ExampleBean {
public void init() {
System.out.println("bean start init");
}
public void destorty() {
System.out.println("bean is destory");
}
}
<bean id="examplebean" class="test.ExampleBean" init-method="init" destroy-method="destorty"></bean>//這裏填寫對應的init 的destory 方法名,這個你保持和Bean 的方法一致即可。
還可以實現InitializingBean or DisposableBean 接口,實現Bean 的初始化和銷燬,但是不建議,它會直接要你重寫方法民,靈活性不行。
當如果多個Bean 裏面都有這麼一樣的初始化、銷燬的方法,可以可以直接在Beans 標籤中,通過配置default-init-method and default-destroy-method 來減少重複性的設置。
如果上述你是web 應用,那麼關閉服務器,就可以發現執行了destory 方法,init 方法不用說,創建的時候直接執行。也可以通過AbstractApplicationContext 的registerShutdownHook 去模擬容器的關閉。
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
ExampleBean eb = (ExampleBean) context.getBean("examplebean");
context.registerShutdownHook();
BeanPostProcessor
如果我們需要在Spring容器完成Bean的實例化、配置和其他的初始化前後添加一些自己的邏輯處理,我們就可以定義一個或者多個BeanPostProcessor接口的實現,然後註冊到容器中。
實例化Bean 的流程:
public class InitHelloWorld implements BeanPostProcessor {
//Bean 初始化前
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("before init bean "+beanName);
return bean;//這個地方在創建的時候,一定要注意就是返回bean 對象,否則會引起異常。
}
//Bean 初始化後
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("after init bean "+beanName);
return bean;
}
}
<bean class="test.InitHelloWorld"></bean>//這個Bean 是沒有id的,你不需要管他
ApplicationContext 會自動檢測到所有實現該接口的Bean,並自動註冊到容器中,所以,你還是要在Beans.xml中定義。
隨便獲取一個之前配置的Bean,會發現執行上面兩個方法,在bean 初始化前後。
Bean 的繼承
一個子類的Bean可以繼承父類的Bean,可以重新相關的屬性,以及添加自己需要的。
原先的HelloWorld 類不動,在編寫一個子類:
public class SubHelloWorld {
private String message;
private String sonmessage;
//相應的set、get方法省略,自行補充
}
這裏有一點需要注意:儘管是繼承,這裏必須要重新定義父類的屬性,否則儘管下面設置繼承關係,一樣沒辦法設置父類的屬性,並且這個父類屬性必須要和父類設置一樣。
注意這裏雖然是繼承關係,但是不需要像Java 一樣通過extends ,繼承的關係,在下面Bean配置的標籤parent 來體現:
<bean id="helloworld" class="test.HelloWord" scope="prototype">
<property name="message" value="hello spring" ></property>
</bean>
<bean id="subhelloworld" class="test.SubHelloWorld" parent="helloworld">//parent 後面是跟的是父類Bean 的id
<property name="sonmessage" value="i am son hello world"></property>//在這我們也可以設置父類的message 屬性,修改爲子類自己特有的,也可以不設置,就是父類的。
</bean>
注意:一定你指定繼承關係,如果直接獲取子類的Bean,是可以獲取父類的屬性,無需多此一舉,在get父類的Bean。
Bean 模板
在Java se 中,我們都知道可以定義一個抽象類加上繼承,去實現一個公用的模板類,在Spring 也一樣。
在上述代碼,修改helloworld,將其定義成抽象的。
<bean id="helloworld" class="test.HelloWord" abstract="true"></bean>
這樣父類可以定義一些屬性,子類可以直接用,或者重新賦值,子類還是要定義父類的屬性(一模一樣)。將父類定義成抽象的好處,父類就是一個純潔的Bean 定義模板。