Spring之Bean(二)


關於Bean

Spring核心容器是一個超級大工廠,所有的對象(包括數據源、Hibernate SessionFactory等基礎性資源)都會被當成Spring核心容器管理的對象。Spring把容器中一切對象統稱爲Bean。

對於Spring框架而言,一切Java對象都是Bean。只要是個Java類,Spring就可以管理該Java類,並將它當成Bean管理。下面定義Axe類和Person類兩個類,其中的Person類的useAxe()方法需要調用Axe對象的chop()方法,像這種A對象需要調用B對象方法的情形,被稱爲依賴。該示例中Person類依賴於Axe對象。示例代碼如下:

public class Axe{
  public String chop(){
    return "使用斧頭砍柴";
  }
}
public class Person{
  private Axe axe;
  //設置注入所需的setter方法
  public void setAxe(Axe axe){
    this.axe = axe;
  }
  public void useAxe(){
    System.out.println("我打算去砍點柴!");
    //調用axe的chop()方法,
    //表明Person對象依賴於axe對象
    System.out.prntln(axe.chop);
  }
}


使用Spring框架後,Spring核心容器是整個應用中的超級大工廠,所有Java對象都交給Spring容器實現,這些對象被統稱爲Spring容器中的Bean。

Spring beans

Spring beans 是那些形成Spring應用的主幹的java對象。它們被Spring IOC容器初始化,裝配,和管理。這些beans通過容器中配置的元數據創建。比如,以XML文件中< bean/> 的形式定義。

Spring 框架定義的beans都是單件beans。在bean tag中有個屬性”singleton”,如果它被賦爲TRUE,bean 就是單件,否則就是一個 prototype bean。默認是TRUE,所以所有在Spring框架中的beans 缺省都是單件。

Spring的內部bean

當一個bean僅被用作另一個bean的屬性時,它能被聲明爲一個內部bean,爲了定義inner bean,在Spring 的 基於XML的 配置元數據中,可以在 或 元素內使用 元素,內部bean通常是匿名的,它們的Scope一般是prototype。



如何管理Bean

給Spring 容器配置元數據

Spring容器管理Bean可以通過XML配置文件,也可以用註解,或者基於java的配置。

<?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:util="http://www.springframework.org/schema/util"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
  <!-- 配置名爲person的Bean,其實現類是com.afy.spring.beans.Person -->
  <bean id="person" class="com.afy.spring.beans.Person">
    <!--控制調用setAxe()方法,將容器axe Bean作爲傳入參數-->
    <property name="axe" ref="axe"></property>
  </bean>

  <bean id="axe" class="com.afy.spring.beans.Axe"/>

  <bean id="date" class="java.util.Date"/> 

</beans>


該配置文件根源上是< beans…/>,裏面包括多個< bean…/>元素,每個< bean…/>元素定義一個Bean。以上配置文件中一共定義3個Bean,其中前兩個Bean是Axe和Person類,最後一個是直接使用了JDK提供的java.util.Date和javax.swing.JFrame類。由於Spring可以把“一切Java對象”當成容器中的Bean,所以不管Java類是JDK提供的,還是第三方框架提供的,或者是開發者自己實現的…..只要是個Java類,並將其配置到XML文件中,Spring容器就可以管理它。

配置文件中的< bean…/>元素默認以反射方式調用該類的無參構造器,以下面代碼爲例:

<bean id="person" class="com.afy.spring.beans.Person">

Spring框架解析該< bean…/>元素後將可以得到idStr和classStr兩個字符串,idStr的值爲“person”(解析< bean…>元素的id屬性得到的值),classStr的值爲“com.afy.spring.beans.Person”(解析< bean…/>元素的class屬性得到的值)。Spring底層會執行如下格式的代碼:

//解析< bean.../>元素的id屬性得到的值爲“person”
String idStr = ...;
//解析< bean.../>元素的class屬性得到該字符串值爲“com.afy.spring.beans.Person”
String classStr = ...;
Class clazz = Class.forName(classStr);
Object obj = clazz.newInstance();
//container代表Spring迭代器
container.put(idStr,obj);

這段代碼是基本的反射代碼(不是Spring底層所有代碼),Spring框架通過反射根據< bean…/>元素的屬性指定的類名創建了一個Java對象,並以< bean…/>元素的id屬性的值爲key,將該對象放入Spring容器中,這個Java對象就稱爲Spring容器中的Bean。

可以簡單地理解爲每個< bean…/>元素默認驅動Spring調用該類無參構造器創建實例,並將該實例作爲Spring容器中的Bean。

在Spring配置文件中配置Bean時,class屬性的值必須是Bean實現類的完整類名(必須帶包名),不能是接口或抽象類(除非有特殊配置),否則Spring無法使用反射創建該類的實例。

配置文件中還包含一個< property…/>子元素,< property…/>子元素驅動Spring在底層以反射執行一次setter方法。其中< property…/>的name屬性值決定執行哪個setter方法,而value或ref決定執行setter方法的傳入參數。

[] 如果傳入參數是基本類型及其包裝類、Spring等類型,則使用value屬性指定傳入參數。
[] 如果以容器中其他Bean作爲傳入參數,則使用ref屬性指定傳入參數。

Spring只要看到< property…/>子元素,Spring框架就會在底層以反射方式執行一次setter方法,那什麼時候執行這個setter方法呢?該Bean一旦創建處理,Spring就會立即根據< property…/>子元素類執行setter方法。也就是說,< bean…/>元素驅動Spring調用無參構造器創建對象;< property…/>子元素驅動Spring執行setter方法,這兩步是先後執行的,中間幾乎沒有任何間隔。如:

<bean id="person" class="com.afy.spring.beans.Person">
     <property name="axe" ref="axe"></property>
</bean>


< property…/>子元素的name屬性值爲axe,該元素將驅動Spring以反射方式執行Person Bean的setAxe()方法;ref屬性值爲axe,該屬性值指定以容器中名爲axe的Bean作爲執行setter方法的傳入參數。Spring底層會執行如下格式的代碼:

//基本的反射代碼
String nameStr = ...;//解析< property.../>元素的name屬性得到該字符串值爲“axe”
String refStr = ...;//解析< property.../>元素的ref屬性得到該字符串值爲“axe”
String setterName = "set"+nameStr.substring(0,1).toUpdateCase()+nameStr.substring(1);//生成將要調用的setter方法
//或去Spring容器中名爲refStr的Bean,該Bean會做爲傳入參數
Object paramBean = container.get(refStr);
//此處的clazz是前面一段反射代碼通過< bean.../>元素的class屬性得到的class對象
Method setter = clazz.getMethod(setterName,paraBean.getBean());
//此處的obj參數是前段反射代碼爲< bean.../>元素創建的對象
setter.invoke(obj,paramBean);

可以簡單地理解爲每個< property…/>元素默認驅動Spring調用一次setter方法。

由此可見上面配置3個< bean…/>元素時Spring會一次創建com.afy.spring.beans.Person、com.afy.spring.beans.Axe、java.util.Date這3個類的對象,並把它們當成容器中的Bean。

其中id爲Person的< bean…/>元素還包括一個< property…/>子元素,因此Spring會在創建完person Bean之後,立即以容器中id爲axe的Bean作爲參數調用person Bean的setAxe()方法,這樣會導致容器中id爲axe的Bean被賦值給person對象的axe實例變量。

定義類的作用域

當定義一個< bean> 在Spring裏,我們還能給這個bean聲明一個作用域。它可以通過bean 定義中的scope屬性來定義。如,當Spring要在需要的時候每次生產一個新的bean實例,bean的scope屬性被指定爲prototype。另一方面,一個bean每次使用的時候必須返回同一個實例,這個bean的scope 屬性 必須設爲 singleton。

Spring支持的幾種bean的作用域

Spring框架支持以下五種bean的作用域:

singleton : bean在每個Spring ioc 容器中只有一個實例。
prototype:一個bean的定義可以有多個實例。
request:每次http請求都會創建一個bean,該作用域僅在基於web的Spring ApplicationContext情形下有效。
session:在一個HTTP Session中,一個bean定義對應一個實例。該作用域僅在基於web的Spring ApplicationContext情形下有效。
global-session:在一個全局的HTTP Session中,一個bean定義對應一個實例。該作用域僅在基於web的Spring ApplicationContext情形下有效。

缺省的Spring bean 的作用域是Singleton。

Spring框架中bean的生命週期

Spring容器 從XML 文件中讀取bean的定義,並實例化bean。
Spring根據bean的定義填充所有的屬性。
如果bean實現了BeanNameAware 接口,Spring 傳遞bean 的ID 到 setBeanName方法。
如果Bean 實現了 BeanFactoryAware 接口, Spring傳遞beanfactory 給setBeanFactory 方法。
如果有任何與bean相關聯的BeanPostProcessors,Spring會在postProcesserBeforeInitialization()方法內調用它們。
如果bean實現IntializingBean了,調用它的afterPropertySet方法,如果bean聲明瞭初始化方法,調用此初始化方法。
如果有BeanPostProcessors 和bean 關聯,這些bean的postProcessAfterInitialization() 方法將被調用。
如果bean實現了 DisposableBean,它將調用destroy()方法。

重要的bean生命週期方法

有兩個重要的bean 生命週期方法,第一個是setup , 它是在容器加載bean的時候被調用。第二個方法是 teardown 它是在容器卸載類的時候被調用。

The bean 標籤有兩個重要的屬性(init-method和destroy-method)。用它們你可以自己定製初始化和註銷方法。它們也有相應的註解(@PostConstruct和@PreDestroy)。

在 Spring中如何注入一個java集合

Spring提供以下幾種集合的配置元素:

< list>類型用於注入一列值,允許有相同的值。
< set> 類型用於注入一組值,不允許有相同的值。
< map> 類型用於注入一組鍵值對,鍵和值都可以爲任意類型。
< props>類型用於注入一組鍵值對,鍵和值都只能爲String類型。



Spring容器訪問Bean

配置完Bean後要通過Spring容器訪問容器中的Bean。

在 Spring IOC 容器讀取 Bean 配置創建 Bean 實例之前, 必須對它進行實例化. 只有在容器實例化後, 纔可以從 IOC 容器裏獲取 Bean 實例並使用。

Spring 提供了兩種類型的 IOC 容器實現:

1. BeanFactory: IOC 容器的基本實現;
2. ApplicationContext: 提供了更多的高級特性. 是 BeanFactory 的子接口。

BeanFactory 是 Spring 框架的基礎設施,面向 Spring 本身;ApplicationContext 面向使用 Spring 框架的開發者,是Spring容器最常用的接口,幾乎所有的應用場合都直接使用 ApplicationContext 而非底層的 BeanFactory。該接口下有如下兩個實現類:

1. ClassPathXmlApplicationContext:從類加載路徑下搜索配置文件,並根據配置文件來創建Spring容器;
2. FileSystemXmlApplicationContext:從文件系統的相對路徑或絕對路徑下去搜索配置文件,並根據配置文件來創建Sprig容器。

Java項目中,類加載路徑是穩定的,因此通常是使用 ClassPathXmlApplicationContext創建Spring容器。

public class BeanTest{

   public static void main(String[] args){      
     //1. 創建 Spring 的 IOC 容器
      ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

     //2. 從 IOC 容器中獲取id爲person的Bean
      Person p = ctx.getBean("person",Person.class)

      //3. 使用 bean
      p.useAxe();
   }
}

Spring容器獲取Bean對象主要有如下兩個方法:

1. Object getBean(String id):根據容器中Bean的id來獲取指定Bean,獲取Bean之後需要進行強制類型轉換;
2. T getBean(String name,Class requireType):根據容器中Bean的id來獲取指定Bean,但該方法帶一個泛型參數,因此獲取Bean之後無需進行類型轉換。

獲取Bean(即Java對象)之後,即可以通過對象來調用方法、訪問實例變量,像普通使用Java對象一樣使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章