Spring之Bean的配置使用

1.Bean的基本概念

IoC管理的應用程序對象叫做Bean, Bean就是由Spring容器初始化、裝配及管理的對象,除此之外,bean就與應用程序中的其他對象沒有什麼區別了。在Spring中由BeanDefinition代表,配置元數據指定如何實例化Bean、如何組裝Bean等。

2.Spring IoC管理Java Bean

Spring IoC容器如何知道哪些是它管理的對象呢?在Spring Ioc容器的代表就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本功能;而org.springframework.context包下的ApplicationContext接口擴展了BeanFactory,還提供了與Spring AOP集成、國際化處理、事件傳播及提供不同層次的context實現 (如針對web應用的WebApplicationContext)。簡單說, BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 則增加了更多支持企業級功能支持。ApplicationContext完全繼承BeanFactory,因而BeanFactory所具有的語義也適用於ApplicationContext。

容器實現一覽:

 XmlBeanFactoryBeanFactory實現,提供基本的IoC容器功能,可以從classpath或文件系統等獲取資源;

(1)File file = new File("fileSystemConfig.xml");

         Resource resource = new FileSystemResource(file);

         BeanFactory beanFactory = new XmlBeanFactory(resource);

(2)Resource resource = new ClassPathResource("classpath.xml");                 

        BeanFactory beanFactory = new XmlBeanFactory(resource);

ClassPathXmlApplicationContextApplicationContext實現,從classpath獲取配置文件;

         BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");

FileSystemXmlApplicationContextApplicationContext實現,從文件系統獲取配置文件。

         BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");

ApplicationContext接口獲取Bean方法簡介:

 Object getBean(String name) 根據名稱返回一個Bean,客戶端需要自己進行類型轉換;

 T getBean(String name, Class<T> requiredType) 根據名稱和指定的類型返回一個Bean,客戶端無需自己進行類型轉換,如果類型轉換失敗,容器拋出異常;

 T getBean(Class<T> requiredType) 根據指定的類型返回一個Bean,客戶端無需自己進行類型轉換,如果沒有或有多於一個Bean存在容器將拋出異常;

 Map<String, T> getBeansOfType(Class<T> type) 根據指定的類型返回一個鍵值爲名字和值爲Bean對象的 Map,如果沒有Bean對象存在則返回空的Map。


Spring IoC容器目的就是管理Bean,這些Bean將根據配置文件中的Bean定義進行創建,而Bean定義在容器內部由BeanDefinition對象表示,該定義主要包含以下信息:

1.全限定類名(FQN):用於定義Bean的實現類;

2.Bean行爲定義:這些定義了Bean在容器中的行爲;包括作用域(單例、原型創建)、是否惰性初始化及生命週期等;

3.Bean創建方式定義:說明是通過構造器還是工廠方法創建Bean;

4.Bean之間關係定義:即對其他bean的引用,也就是依賴關係定義,這些引用bean也可以稱之爲同事bean 或依賴bean,也就是依賴注入;

Bean定義只有“全限定類名”在當使用構造器或靜態工廠方法進行實例化bean時是必須的,其他都是可選的定義。難道Spring只能通過配置方式來創建Bean嗎?回答當然不是,某些SingletonBeanRegistry接口實現類實現也允許將那些非BeanFactory創建的、已有的用戶對象註冊到容器中,這些對象必須是共享的,比如使用DefaultListableBeanFactory 的registerSingleton() 方法。不過建議採用元數據定義。

IoC容器工作方式:

1.準備配置文件:在配置文件中聲明Bean定義也就是爲Bean配置元數據。

2.由IoC容器進行解析元數據: IoC容器的Bean Reader讀取並解析配置文件,根據定義生成BeanDefinition配置元數據對象,IoC容器根據BeanDefinition進行實例化、配置及組裝Bean。

3.實例化IoC容器:由客戶端實例化容器,獲取需要的Bean。

3.Bean的命名

下面來看看實例:
注:這裏用的spring-framework-4.3.7.RELEASE,jar包如下:
spring-beans-4.3.7.RELEASE.jar
spring-core-4.3.7.RELEASE.jar
spring-context-4.3.7.RELEASE.jar
spring-expression-4.3.7.RELEASE.jar
commons-logging-1.2.jar
log4j-1.2.17.jar
junit-4.10.jar(用於單元測試)

BeanApi

package com.chensan.spring.chapter1;

public interface BeanApi {
	public void sayHello();
}
BeanImpl1
package com.chensan.spring.chapter1;

public class BeanImpl1 implements BeanApi {
	//@Override
	public void sayHello() {
		System.out.println("Welcome to Spring World!");
	}
}

1.全限定名方式

不指定id,只配置必須的全限定類名,由IoC容器爲其生成一個標識,客戶端必須通過接口“T getBean(Class<T> requiredType)”獲取Bean;

bean1_1.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:context="http://www.springframework.org/schema/context" 
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.3.xsd">
  
  <!-- 不指定id,只配置必須的全限定類名,由IoC容器爲其生成一個標識,
  	客戶端必須通過接口“T getBean(Class<T> requiredType)”獲取Bean;
   -->
  <bean class="com.chensan.spring.chapter1.BeanImpl1"/>
</beans>
TestBeanApi1_1
package com.chensan.spring.chapter1.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_1 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_1.xml");
		BeanApi beanApi = beanFactory.getBean(BeanApi.class);
		beanApi.sayHello();
	}
}

2.只指定id

id必須在IoC容器中唯一
bean1_2.xml(省略篇幅,xml文件格式不再給出)

<!-- 指定id,必須在IoC容器中唯一 -->
  <bean id="bean1_2" class="com.chensan.spring.chapter1.BeanImpl1"/>
TestBeanApi1_2
package com.chensan.spring.chapter1.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_2 {
	@Test
	public void testHelloSpringWorld(){
		//1. 讀取配置文件實例化一個IoC容器
		ApplicationContext context = new ClassPathXmlApplicationContext("bean1_2.xml");
		//2. 從容器中獲取Bean,注意此處完全“面向接口編程,而不是面向實現”
		BeanApi beanApi = context.getBean("bean1_2", BeanApi.class);
		//3. 執行業務邏輯
		beanApi.sayHello();
	}
}

3.指定一個name,不指定id

name是標識符,必須在Ioc容器中唯一;

bean1_3

<!-- 指定name,這樣name就是“標識符”,必須在Ioc容器中唯一; -->
  <bean name="bean1_3" class="com.chensan.spring.chapter1.BeanImpl1"/>
TestBeanApi1_3
package com.chensan.spring.chapter1.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_3 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_3.xml");
		BeanApi beanApi = beanFactory.getBean("bean1_3", BeanApi.class);
		beanApi.sayHello();
	}
}

4.同時指定id和name,id和name不同名

id是標識符,而name是別名,必須在Ioc容器中唯一;

bean1_4

<!-- 
  	指定id和name,id就是標識符,而name就是別名,必須在Ioc容器中唯一;
   -->
  <bean id="bean1_4_1" name="bean1_4_2" class="com.chensan.spring.chapter1.BeanImpl1"/>
TestBeanApi1_4
package com.chensan.spring.chapter1.test;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_4 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_4.xml");
		BeanApi beanApi1 = beanFactory.getBean("bean1_4_1", BeanApi.class);
		beanApi1.sayHello();
		BeanApi beanApi2 = beanFactory.getBean("bean1_4_2", BeanApi.class);
		beanApi2.sayHello();
		String[] bean4Alias = beanFactory.getAliases("bean1_4_2");
		Assert.assertEquals(1, bean4Alias.length);
	}
}

5.同時指定id和name,id和name同名

bean1_5

<!-- 
  	指定id和name,id就是標識符,而name就是別名,必須在Ioc容器中唯一;
   	 如果id和name一樣,IoC容器能檢測到,並消除衝突;
   -->
  <bean id="bean1_5" name="bean1_5" class="com.chensan.spring.chapter1.BeanImpl1"/>
TestBeanApi1_5
package com.chensan.spring.chapter1.test;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_5 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_5.xml");
		BeanApi beanApi = beanFactory.getBean("bean1_5", BeanApi.class);
		beanApi.sayHello();
		String[] bean5Alias = beanFactory.getAliases("bean1_5");
		Assert.assertEquals(0, bean5Alias.length);//bean1_5別名獲取數組長度爲0,bean1_4別名獲取數組長度爲1;說明:別名不能和id一樣,如果一樣則由IoC容器負責消除衝突;
	}
}

6.指定多個name,不指定id

指定多個name,第一個name後用“;”,後面的name用“,”隔開第一個被用作標識符,其他的爲別名;

bean1_6.xml

<!-- 指定多個name,第一個name後用“;”,後面的name用“,”隔開第一個被用作標識符,其他的爲別名; -->
  <bean name="bean1_6;alias1_6_1,alias1_6_2,alias1_6_3,alias1_6_4" 
  	class="com.chensan.spring.chapter1.BeanImpl1"/>
TestBeanApi1_6
package com.chensan.spring.chapter1.test;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_6 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_6.xml");
		//根據id獲取bean
		BeanApi beanApi = beanFactory.getBean("bean1_6", BeanApi.class);
		beanApi.sayHello();
		//根據別名獲取bean
		BeanApi alias161 = beanFactory.getBean("alias1_6_1", BeanApi.class);//alias51, alias52, alias53都是別名
		alias161.sayHello();
		String[] aliases = beanFactory.getAliases("bean1_6");
		Assert.assertEquals(4, aliases.length);
		for(String alias : aliases){
			System.out.println(alias);
		}		
	}
}

7.指定多個name,同時指定id

指定多個name,第一個name後用“;”,後面的name用“,”隔開第一個被用作標識符,其他的爲別名;當指定id時,name指定的標識符全部爲別名;
bean1_7.xml

<!-- 指定多個name,第一個name後用“;”,後面的name用“,”隔開第一個被用作標識符,其他的爲別名;
   	 當指定id時,name指定的標識符全部爲別名;
   -->
  <bean id="bean1_7" name="bean1_7_1;alias1_7_1,alias1_7_2,alias1_7_3,alias1_7_4" 
  	class="com.chensan.spring.chapter1.BeanImpl1"/>
TestBeanApi1_7
package com.chensan.spring.chapter1.test;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_7 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_7.xml");
		//根據id獲取bean
		BeanApi beanApi = beanFactory.getBean("bean1_7", BeanApi.class);
		beanApi.sayHello();
		//根據別名獲取bean
		BeanApi alias171 = beanFactory.getBean("bean1_7_1", BeanApi.class);//alias51, alias52, alias53都是別名
		alias171.sayHello();
		String[] aliases = beanFactory.getAliases("bean1_7");
		Assert.assertEquals(5, aliases.length);
		for(String alias : aliases){
			System.out.println(alias);
		}	
	}
}

8.使用<alias>標籤指定別名

別名也必須在IoC容器中唯一

bean1_8.xml

<!-- 使用<alias>標籤指定別名,別名也必須在IoC容器中唯一   -->
  <bean name="bean1_8_1" class="com.chensan.spring.chapter1.BeanImpl1"/>
  <alias alias="alias1_8_1" name="bean1_8_1"/>
  <alias alias="alias1_8_2" name="bean1_8_1"/>
TestBeanApi1_8
package com.chensan.spring.chapter1.test;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBeanApi1_8 {
	@Test
	public void testHelloSpringWorld(){
		BeanFactory beanFactory  = new ClassPathXmlApplicationContext("bean1_8.xml");
		
		//根據別名獲取bean
		BeanApi beanApi2 = beanFactory.getBean("bean1_8_1", BeanApi.class);
		beanApi2.sayHello();
		
		String[] aliases = beanFactory.getAliases("bean1_8_1");
		Assert.assertEquals(2, aliases.length);
		for(String alias : aliases){
			System.out.println(alias);
		}	
	}
}

4.實例化Bean

傳統應用程序可以通過new和反射方式進行實例化Bean。而Spring IoC容器則需要根據Bean定義裏的配置元數據使用反射機制來創建Bean。在Spring IoC容器中根據Bean定義創建Bean主要有以下幾種方式:

1.構造器實例化Bean

構造器實例化Bean是最簡單的方式,Spring IoC容器既能使用默認空構造器也能使用有參數構造器兩種方式創建Bean,如以下方式指定要創建的Bean類型:
使用空構造器進行定義,使用此種方式,class屬性指定的類必須有空構造器。

BeanImpl2

package com.chensan.spring.chapter2;

import com.chensan.spring.chapter1.BeanApi;

public class BeanImpl2 implements BeanApi {
	private String message;
	public BeanImpl2(){
		this.message = "Hello Spring!";
	}

	public BeanImpl2(String message){
		this.message = message;
	}
	
	@Override
	public void sayHello() {
		System.out.println(message);
	}

	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
}

bean2_1.xml

<!-- 使用默認構造參數 -->
<bean name="bean2_1_1" class="com.chensan.spring.chapter2.BeanImpl2"/>
<!-- 使用有參構造參數 -->
<bean name="bean2_1_2" class="com.chensan.spring.chapter2.BeanImpl2">
  <!-- 指定構造器參數 -->
  <constructor-arg index="0" value="Spring Constructor BeanFactory"/>
</bean>
TestBean2_1
package com.chensan.spring.chapter2.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBean2_1 {
	//構造器實例化Bean
	@Test
	public void instantiatingBeanByConstructor(){
		BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean2_1.xml");
		BeanApi beanApi1 = beanFactory.getBean("bean2_1_1", BeanApi.class);
		beanApi1.sayHello();
		BeanApi beanApi2 = beanFactory.getBean("bean2_1_2", BeanApi.class);
		beanApi2.sayHello();
	}
}
注意:看起來bean2_1_1與之前Bean的命名是一樣的,區別在於BeanImpl2中的sayHello方法中的參數是從構造器中傳入的,所以需要指定對應參數個數的構造器。

2.靜態工廠方式實例化Bean

使用靜態工廠方式除了指定必須的class屬性,還要指定factory-method屬性來指定實例化Bean的方法,而且使用靜態工廠方法也允許指定方法參數,spring IoC容器將調用此屬性指定的方法來獲取Bean

BeanApiStaticFactory

package com.chensan.spring.chapter2;

import com.chensan.spring.chapter1.BeanApi;

public class BeanApiStaticFactory {
	//靜態工廠方法
	public static BeanApi newInstance(String message){
		return new BeanImpl2(message);
	}
}
bean2_2.xml
<!-- 使用靜態工廠方法 -->
<bean name="bean2_2" class="com.chensan.spring.chapter2.BeanApiStaticFactory" 
    factory-method="newInstance">
  <!-- 指定構造器參數 -->
  <constructor-arg index="0" value="Spring Static BeanFactory"/>
</bean>
TestBean2_2
package com.chensan.spring.chapter2.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBean2_2 {
	//使用靜態工廠方法
	@Test
	public void instantiatingBeanByStaticFactory(){
		BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean2_2.xml");
		BeanApi beanApi1 = beanFactory.getBean("bean2_2", BeanApi.class);
		beanApi1.sayHello();
	}
}

3.實例工廠方法實例化Bean

使用實例化工廠方式不能指定class屬性,此時必須使用factory-bean屬性來指定工廠Bean,factory-method屬性指定實例化Bean的方法,而且使用實例工廠方法允許指定方法參數,方式和使用構造器方式一樣

BeanApiInstanceFactory

package com.chensan.spring.chapter2;

import com.chensan.spring.chapter1.BeanApi;

public class BeanApiInstanceFactory {
	//實例化工廠方法
	public BeanApi newInstance(String message){
		return new BeanImpl2(message);
	}
}
bean2_3.xml
<!-- 定義實例化工廠Bean -->
<bean id="bean2_3_1" class="com.chensan.spring.chapter2.BeanApiInstanceFactory"/>
<!-- 使用實例化工廠Bean實例化Bean -->
<bean id="bean2_3_2" factory-bean="bean2_3_1" factory-method="newInstance">
  <!-- 指定構造器參數 -->
  <constructor-arg index="0" value="Spring Instance BeanFactory"/>
</bean>
TestBean2_3
package com.chensan.spring.chapter2.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.chensan.spring.chapter1.BeanApi;

public class TestBean2_3 {
	//使用實例化工廠方法
	@Test
	public void instantiatingBeanByInstanceFactory(){
		BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean2_3.xml");
		BeanApi beanApi2 = beanFactory.getBean("bean2_3_2", BeanApi.class);
		beanApi2.sayHello();
	}
}

這三種方式只是配置不一樣,從獲取方式看完全一樣,沒有任何不同。這也是Spring IoC的魅力,Spring IoC幫你創建Bean,我們只管使用就可以。

參考:http://jinnianshilongnian.iteye.com/blog/1413857

發佈了102 篇原創文章 · 獲贊 49 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章