Spring IOC 一——Spring容器裝配Bean
寫在前面
這篇文章去年寫的,緣起於去年某段時間被領導臨時“抓壯丁”般的叫過去做java開發,然後在網上找了一個 SpringMVC 的 demo,學習一下,然後依葫蘆畫瓢,開始了自己的專案開發,也還順利完成了任務。在使用 SpringMVC 的過程中,我被這個被稱作“最優秀”的 java 框架 —— Spring 深深地吸引了,那些曾經掌握的多種設計模式的思想在其中都有實現,比如工廠模式、單例模式、觀察者模式等等,於是在工作之餘認真地學習了這個框架。
學習新東西總是令人興奮的,然而大多數學過的知識很容易又會忘記,所以便想整理一下,寫點筆記,方便自己以後複習檢視。
部分參考資料:
《Spring實戰(第4版)》
Spring 官方文件
W3CSchool Spring教程
易百教程 Spring教程
一、Spring簡介
Spring是一個開源框架,Spring是於2003年興起的一個輕量級的Java開發框架,由Rod Johnson在其著作Expert One-On-One J2EE Development and Design中闡述的部分理念和原型衍生而來。它是為了解決企業應用開發的複雜性而建立的。框架的主要優勢之一就是其分層架構,分層架構允許使用者選擇使用哪一個元件。Spring提供了約20多個元件,開發者可以根據自己需要選擇元件。Spring的核心是控制反轉(IoC)和麵向切面程式設計(AOP)
- IoC:控制反轉。
控制反轉(Inversion of Control),又叫依賴注入(Dependency Injection)。舉例來說,在之前的操作中,比方說有一個類,我們想要呼叫類裡面的方法(不是靜態方法),就要建立類的物件,使用物件呼叫方法實現。對於Spring來說,Spring建立物件的過程,不是在程式碼裡面實現的,而是交給Spring來進行配置實現的。 - AOP:面向切面程式設計。
面向切面程式設計(Aspect Orient Programming)支援允許將一些通用的任務入安全、事物、日誌、快取等進行集中式處理,從而提供了更好的複用,AOP通常用來處理一些具有橫切性質的系統級服務。
二、Hello Spring 例項
從最簡單的 Hello,Spring 例子來體會一下使用 Spring 框架。首先看看不使用 Spring 框架的程式碼:
HelloSpring.java 類:
package com.sharpcj;
public class HelloSpring {
private String name;
public void sayHello() {
System.out.println("Hello," + name + "!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Test.java 類:
package com.sharpcj;
public class Test {
public static void main(String[] args) {
HelloSpring hs = new HelloSpring();
hs.setName("Spring");
hs.sayHello();
}
}
輸出結果:
Hello,Spring!
下面我們用 Spring 框架來實現這個例子。
Spring 框架官方下載地址: http://repo.springsource.org/libs-release-local/
Spring 框架阿里雲下載地址:http://maven.aliyun.com/nexus/content/groups/public/springframework/
新增從 Spring 框架核心 JAR 檔案:
- commons-logging-1.1.1
- spring-aop-5.0.6.RELEASE
- spring-aspects-5.0.6.RELEASE
- spring-beans-5.0.6.RELEASE
- spring-context-5.0.6.RELEASE
- spring-context-support-5.0.6.RELEASE
- spring-core-5.0.6.RELEASE
- spring-expression-5.0.6.RELEASE
- spring-instrument-5.0.6.RELEASE
- spring-instrument-tomcat-5.0.6.RELEASE
- spring-jdbc-5.0.6.RELEASE
- spring-jms-5.0.6.RELEASE
- spring-messaging-5.0.6.RELEASE
- spring-orm-5.0.6.RELEASE
- spring-oxm-5.0.6.RELEASE
- spring-test-5.0.6.RELEASE
- spring-tx-5.0.6.RELEASE
- spring-web-5.0.6.RELEASE
- spring-webmvc-5.0.6.RELEASE
- spring-webmvc-portlet-5.0.6.RELEASE
- spring-websocket-5.0.6.RELEASE
這裡只是使用Spring的基本功能,所以需要使用到下面的這幾個Jar包:
這裡我使用的 idea,用 gradle 編譯的(用 maven 過程類似)。為了提高下載速度,我使用了阿里雲的 maven 倉庫,然後新增依賴,最新穩定版本是 5.0.6 , build.gradle
檔案部分截圖:
程式碼更改如下:
HelloSpring.java 類
package com.sharpcj;
public class HelloSpring {
private String name;
public void sayHello() {
System.out.println("Hello," + name + "!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Test.java 類
package com.sharpcj;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("src/beans.xml");
HelloSpring hs = context.getBean("helloSpring", HelloSpring.class);
hs.sayHello();
}
}
此時在 src 目錄下新建了一個檔案 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-3.0.xsd">
<bean id="helloSpring" class="com.sharpcj.HelloSpring">
<property name="name" value="Spring"/>
</bean>
</beans>
此時執行 Test.java 的 main 方法,列印結果如下:
Hello,Spring!
這樣我們就用 Spring 框架實現了最簡單的 Hello Spring 程式。
三、認識 spring 容器和 Bean
上面用 Spring 框架實現的程式碼中,我們在 Test.java 類中,並沒有通過 “new HelloSpring()” 這樣的呼叫 Spring 構造方法去建立 HelloSpring 的物件,而是使用 Spring 核心容器建立的。
第一步是我們使用框架 API FileSystemXmlApplicationContext() 來建立應用程式的上下文。這個 API 載入 beans 的配置檔案並最終基於所提供的 API,它處理建立並初始化所有的物件,即在配置檔案中提到的 beans。
第二步是使用已建立的上下文的 getBean() 方法來獲得所需的 bean。這個方法使用 bean 的 ID 返回一個最終可以轉換為實際物件的通用物件。一旦有了物件,你就可以使用這個物件呼叫任何類的方法。能通過這種方式建立的物件,一定是在 beans.xml 檔案中配置的。
Spring 核心容器就好像是一個超級大的工廠,在配置檔案中配置過的物件都會被當成 Spring 容器管理的物件。Spring 把容器中的一切物件統稱為 Bean 。 Spring 中的 Bean 與傳統的 java Bean 不同,對 Spring 而言,任何一個 java 類,都可以當成是 Bean 來處理。
四、Spring容器裝配Bean的三種方式
- 在XML中進行裝配
- 自動裝配 bean
- 在Java中進行裝配
還是用上面 HelloSpring 的例子,該例子實在過於簡單,只有一個 bean, 沒有涉及到兩個 bean 之間的依賴關係,不過還是可以用它來理解Spring容器裝配Bean的三種裝配方式。為了說明依賴注入的場景,舉個其它例子:
人用筆寫字。虛擬碼如下:
Pen 類:
public class Pen {
// property 暫不關心
}
Person 類:
public class Person {
private Pen pen;
public Person(Pen pen) {
this.pen = pen;
}
public Pen getPen() {
return this.pen;
}
public void setPen(Pen pen) {
this.pen = pen;
}
// 這裡我們暫不關心該方法
public void write() {
}
}
下面對於這種依賴關係,將分別用虛擬碼來說明構造注入和設定注入。
在 XML 中進行裝配
基本使用
上面例子即是,不再贅述。
依賴注入
如果存在多個Bean, 之間有依賴關係:
構造注入:
<?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-3.0.xsd">
<bean id="pen" class="com.sharpcj.Pen"> </bean>
<bean id="person" class="com.sharpcj.Person">
<constructor-arg name = "pen", ref = "pen">
</bean>
</beans>
設值注入:
<?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-3.0.xsd">
<bean id="pen" class="com.sharpcj.Pen"> </bean>
<bean id="person" class="com.sharpcj.Person">
<property name = "pen", ref = "pen">
</bean>
</beans>
4.2 自動裝配 bean
基本使用
com.sharpcj.HelloSpring.java
程式碼如下:
package com.sharpcj;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class HelloSpring {
private String name;
public void sayHello() {
System.out.println("Hello," + name + "!");
}
public String getName() {
return name;
}
@Value("Spring")
public void setName(String name) {
this.name = name;
}
}
建立類 com.sharpcj.HelloConfig.java
用來開啟元件掃描:
package com.sharpcj;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class HelloConfig {
}
修改com.sharpcj.Test.java
package com.sharpcj;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(com.sharpcj.HelloConfig.class);
HelloSpring hello = context.getBean("helloSpring", HelloSpring.class);
hello.sayHello();
}
}
@Component
註解的類即為 Bean 。 @Configuration
註解的即為配置檔案,@ComponentScan
註解表示開啟自動掃描,預設掃描該配置類所在的包,如果掃描其它包,多個包,形式如下:
@Configuration
@ComponentScan(basePackageClasses = {com.a.A.class, com.b.B.class})
也可以每個包內配置之後,再配置一個總的配置檔案:
@Configuration
@Import({com.a.AConfig.class, com.b.BConfig.class})
public class TotalConfig {
}
依賴注入
如果存在多個Bean, 之間有依賴關係:
// pen 類:
@Component
public class Pen {
// property 暫不關心
}
構造注入:
@Component
public class Person {
private Pen pen;
@Autowired
public Person(Pen pen) {
this.pen = pen;
}
public Pen getPen() {
return this.pen;
}
public void setPen(Pen pen) {
this.pen = pen;
}
// 這裡我們暫不關心該方法
public void write() {
}
}
設值注入:
@Component
public class Person {
private Pen pen;
public Pen getPen() {
return this.pen;
}
@Autowired
public void setPen(Pen pen) {
this.pen = pen;
}
// 這裡我們暫不關心該方法
public void write() {
}
}
通過 java 程式碼進行裝配
基本使用
此時 java 類無需使用註解。
同樣建立一個類,com.sharpcj.HelloConfig.java
來進行裝配 Bean。
package com.sharpcj;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HelloConfig {
@Bean
public HelloSpring helloSpring(){
return new HelloSpring();
}
}
被 @Bean 標註的方法返回的即為唯一 Bean(預設單例模式),方法名隨便取。
Test:
ApplicationContext context=new AnnotationConfigApplicationContext(com.sharpcj.HelloConfig.class);
Person person = context.getBean(Person.class);
person.write();
依賴注入
如果存在多個Bean, 之間有依賴關係:
構造注入:
@Configuration
public class PConfig {
@Bean
public GPen hehe() {
return new GPen();
}
@Bean
public QPen haha() {
return new QPen();
}
@Bean
public Person xixi() {
Person person = new Person(hehe());
return person;
}
}
設值注入:
@Configuration
public class PConfig {
@Bean
public GPen hehe() {
return new GPen();
}
@Bean
public QPen haha() {
return new QPen();
}
@Bean
public Person xixi() {
Person person = new Person();
person.setPen(hehe());
return person;
}
}
寫在後面
這篇文章記錄了Spring容器和Bean的概念,Spring 的基本使用,以及Spring容器裝配Bean的三種方式。關於Spring 容器的知識點比較多,下篇文章接著寫點 Spring 容器裝配 Bean 的高階知識點。