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。
容器實現一覽:
XmlBeanFactory:BeanFactory實現,提供基本的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);
ClassPathXmlApplicationContext:ApplicationContext實現,從classpath獲取配置文件;
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");
FileSystemXmlApplicationContext:ApplicationContext實現,從文件系統獲取配置文件。
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();
}
BeanImpl1package 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;
<?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_1package 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_2package 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_3package 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_4package 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_5package 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_6package 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_7package 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_8package 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_1package 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_2package 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_3package 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