SpringIoC
public class Foo {①
  
private String name;
  
private int age;
  
public String toString(){
     
return "The Foo's Name is : " + this.name + " The Foo's Age is : " + this.age;
  }

  
public String getName() {}
  
public void setName(String name) {}
  
public int getAge() {}
  
public void setAge(int age) {}
}


public class Bar {②
  
private String address;
  
public String toString(){
     
return "The Bar's Address is : " + this.address;
  }

  
public String getAddress() {}
  
public void setAddress(String address) {}
}


public class Base {③
  
private Foo foo;
  
private Bar bar;
  
public String toString(){
     
return "Base : [" + this.foo.toString() +" "+ this.bar.toString()+ "]";
  }

  
public Foo getFoo() {}
  
public void setFoo(Foo foo) {}
  
public Bar getBar() {}
  
public void setBar(Bar bar) {}
}



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="">
  
<bean id="foo" class="com.tony.test.Foo">
     
<property name="name" value="Tony"/>
     
<property name="age" value="27"/>
  
</bean >
  
<bean id="bar" class="com.tony.test.Bar">
     
<property name="address" value="China Tianjin"/>
  
</bean>
  
<bean id="base" class="com.tony.test.Base">
     
<property name="foo">
        
<ref local="foo"/>
     
</property>
     
<property name="bar">
        
<ref local="bar"/>
     
</property>
  
</bean>
</beans>

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainClass {④
  
public static void main(String[] args) {    
        String[] locations 
= {"spring-config-beans.xml"};    
        ApplicationContext ctx 
= new ClassPathXmlApplicationContext(locations);    
        Base main 
= (Base) ctx.getBean("base");⑤   
        System.out.println(main);⑥   
   }

}


我們來看看上面代碼的含義,首先在代碼①和②處我們分別定義了兩個名為Foo和Bar的Bean,在③處我們通過set方法將兩個Bean注入進Base類中,并且在Base類中定義了toString方法來打印出Foo和Bar的信息,在④處我們定義了一個MainClass來執行我們的代碼,在⑤處我們通過getBean獲得配置文件中配置的id為base的Bean并在⑥出將其信息打印至控制臺,控制臺輸出信息如下:
Base : [The Foo's Name is : Tony The Foo's Age is : 27 The Bar's Address is : China Tianjin]
看到上面習以為常的配置信息和set get方法我們根本不會有任何想法,可是當我們看到了Spring2.5注釋特性的時候我們發現自己真的錯了,程序竟然還可以寫成這么簡單。

三、使用@Autowired注釋
import org.springframework.beans.factory.annotation.Autowired;
public class Base {
  @Autowired① 使用了一個名為Autowired的注釋
  
private Foo foo;
  @Autowired②
  
private Bar bar;
  
public String toString(){
     
return "Base : [" + this.foo.toString() +" "+ this.bar.toString()+ "]";
  }

}



<!-- 該 BeanPostProcessor 將自動對標注 @Autowired 的 Bean 進行注入 -->③  
  
<bean class="org.springframework.beans.factory.annotation.
                              AutowiredAnnotationBeanPostProcessor"
/>
  
  
<bean id="foo" class="com.tony.test.Foo">
     
<property name="name" value="Tony"/>
     
<property name="age" value="27"/>
  
</bean>
  
<bean id="bar" class="com.tony.test.Bar">
     
<property name="address" value="China Tianjin"/>
  
</bean>
  
<!-- 此時移除了Base的配置信息 --> ④
  
<bean id="base" class="com.tony.test.Base"/>

我們在①和②處使用了@Autowired注釋,它可以對類的成員變量、方法及構造函數進行標注,完成自動裝配的工作,在③處我們為了使@Autowired注釋生效必須在Spring容器中聲明AutowiredAnnotationBeanPostProcessor Bean它通過掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有 @Autowired 注釋時就找到和其相匹配(默認按類型匹配)的 Bean,并將其注入,而此時我們在聲明Base的時候(④處)就不用寫它的配置信息了,更可以將Base類中的set和get方法刪除。@Autowired還可以通過類的構造函數來進行自動裝配。

在默認情況下使用 @Autowired 注釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個。當找不到一個匹配的 Bean 時,Spring 容器將拋出 BeanCreationException 異常,并指出必須至少擁有一個匹配的 Bean


import org.springframework.beans.factory.annotation.Autowired;
public class Base {
  
private Foo foo;
  
private Bar bar;
  @Autowired
  
public Base(Foo foo,Bar bar){ ①
     
this.foo = foo;
     
this.bar = bar;
  }

  
public String toString(){
     
return "Base : [" + this.foo.toString() +" "+ this.bar.toString()+ "]";
  }

}


我們增加了一個構造函數,通過它來對我們的成員變量進行賦值,我們同時也為這個構造函數添加了@Autowired注釋(①處)使其可以自動將Foo和Bar兩個成員變量裝配進來。


當不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false),這等于告訴 Spring:在找不到匹配 Bean 時也不報錯

四、使用@Qualifier注釋
有時我們會遇到這樣一種情況,我們定義了兩個類型相同數據不同的Bean,我們此時需要用到其中一個Bean來供我們使用,使用@Qualifier注釋就可以滿足我們的要求,當使用@Qualifier注釋時自動注入的策略就從 byType 轉變成 byName 了。
<bean id="bar" class="com.tony.test.Bar">
  
<property name="address" value="China Tianjin"/>
</bean>
<bean id="bar2" class="com.tony.test.Bar">
  
<property name="address" value="China Beijing"/>
</bean>

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Base {
  
private Bar bar;
  @Autowired?、?br />   
public Base(@Qualifier("bar2")Bar bar){ ④
     
this.bar = bar;
  }

  
public String toString(){
     
return "Base : ["+ this.bar.toString()+ "]";
  }

}


①和②處我們分別定義了兩個類型為Bar的Bean,②處Bar的address為China Beijing并且Bean的名稱為bar2,在代碼清單4.2的③處我們同樣使用了@Autowired注釋為Bar的構造函數進行自動裝配,可是在④處我們通過@Qualifier("bar2")來明確指定我們需要將id為bar2的Bean裝配進來。我們還可以為成員變量使用@Qualifier注釋。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Base {
  @Autowired ①
  @Qualifier(
"bar2") ②
  
private Bar bar;
  
public String toString(){
     
return "Base : ["+ this.bar.toString()+ "]";
  }

}



五、使用@Component注釋

使用了@Autowired注釋后我們發現自動注入真的非常簡單,但是我們還是得在配置文件中定義相應的<Bean>,如果我們能在配置文件中完全移除Bean的定義那就更好了,Spring2.5就為我們提供了這一可能。
import org.springframework.stereotype.Component;
@Component ①
public class Bar {
  
private String address = "China Tianjin";
  
public String toString(){
     
return "The Bar's Address is : " + this.address;
  }

}


import org.springframework.stereotype.Component;
@Component(
"base") ②
public class Base {
  @Resource
  
private Bar bar;
  
public String toString(){
     
return "Base : ["+ this.bar.toString()+ "]";
  }

}



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="">
  
<context:component-scan base-package="com.tony.test"/> ③
</beans>

我們使用了一個@Component注釋(①處),使用@Component 注釋就可以將一個類定義成為Spring 容器中的 Bean。在代碼清單5.2的②處我們也同樣使用了@Component注釋,而此時我們使用了它提供的一個可選的入參將Bean的名稱定義為base,最后在③處我們將以前定義Bean的內容全部移除,添加了一行<context:component-scan/>,其中的base-package屬性指定了需要掃描的類包,它會自動遞歸下面的子包。
六、使用@Scope注釋

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope(
"prototype") ①
@Component(
"base")
public class Base {
  @Autowired
  
private Bar bar;
  
public String toString(){
     
return "Base : ["+ this.bar.toString()+ "]";
  }

}


在代碼清單6.1中的①處我們添加了一個@Scope注釋,這樣當我們從 Spring 容器中獲取 base 時,每次返回的都是一個新的實例了。

六、使用@Resource 注釋

@Resource 的作用相當于 @Autowired,只不過 @Autowired 按 byType 自動注入,面 @Resource 默認按 byName 自動注入罷了。@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將 @Resource 注釋的 name 屬性解析為 Bean 的名字,而 type 屬性則解析為 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。

Resource 注釋類位于 Spring 發布包的 lib/j2ee/common-annotations.jar 類包中,因此在使用之前必須將其加入到項目的類庫中。來看一個使用 @Resource 的例子:


package com.baobaotao;   
  
import javax.annotation.Resource;   
  
public class Boss {   
    
// 自動注入類型為 Car 的 Bean   
    @Resource  
    
private Car car;   
  
    
// 自動注入 bean 名稱為 office 的 Bean   
    @Resource(name = "office")   
    
private Office office;   
}

一般情況下,我們無需使用類似于 @Resource(type=Car.class) 的注釋方式,因為 Bean 的類型信息可以通過 Java 反射從代碼中獲取。

要讓 JSR-250 的注釋生效,除了在 Bean 類中標注這些注釋外,還需要在 Spring 容器中注冊一個負責處理這些注釋的 BeanPostProcessor:

<bean        
  
class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>   
CommonAnnotationBeanPostProcessor 實現了 BeanPostProcessor 接口,它負責掃描使用了 JSR-250 注釋的 Bean,并對它們進行相應的操

七、使用@PostConstruct 和 @PreDestroy 注釋

Spring 容器中的 Bean 是有生命周期的,

package com.baobaotao;       
      
import javax.annotation.Resource;       
import javax.annotation.PostConstruct;       
import javax.annotation.PreDestroy;       
      
public class Boss {       
    @Resource      
    
private Car car;       
      
    @Resource(name 
= "office")       
    
private Office office;       
      
    @PostConstruct      
    
public void postConstruct1(){       
        System.out.println(
"postConstruct1");       
    }
       
      
    @PreDestroy      
    
public void preDestroy1(){       
        System.out.println(
"preDestroy1");        
    }
       
    …       
}
     

JSR-250 為初始化之后/銷毀之前方法的指定定義了兩個注釋類,分別是 @PostConstruct 和 @PreDestroy,這兩個注釋只能應用于方法上。標注了 @PostConstruct 注釋的方法將在類實例化后調用,而標注了 @PreDestroy 的方法將在類銷毀之前調用。

您只需要在方法前標注 @PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化后或銷毀之前被 Spring 容器執行了。
我們知道,不管是通過實現 InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的 init-method/destroy-method 屬性進行配置,都只能為 Bean 指定一個初始化 / 銷毀的方法。但是使用 @PostConstruct 和 @PreDestroy 注釋卻可以指定多個初始化 / 銷毀方法,那些被標注 @PostConstruct 或 @PreDestroy 注釋的方法都會在初始化 / 銷毀時被執行。

七、使用 <context:annotation-config/> 簡化配置

Spring 2.1 添加了一個新的 context 的 Schema 命名空間,該命名空間對注釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道注釋本身是不會做任何事情的,它僅提供元數據信息。要使元數據信息真正起作用,必須讓負責處理這些元數據的處理器工作起來。

而我們前面所介紹的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是處理這些注釋元數據的處理器。但是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 為我們提供了一種方便的注冊這些 BeanPostProcessor 的方式,這就是 <context:annotation-config/>。請看下面的配置:

<?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-2.5.xsd   
 http://www.springframework.org/schema/context    
 http://www.springframework.org/schema/context/spring-context-2.5.xsd"
>  
    
    
<context:annotation-config/>    
  
    
<bean id="boss" class="com.baobaotao.Boss"/>  
    
<bean id="office" class="com.baobaotao.Office">  
        
<property name="officeNo" value="001"/>  
    
</bean>  
    
<bean id="car" class="com.baobaotao.Car" scope="singleton">  
        
<property name="brand" value=" 紅旗 CA72"/>  
        
<property name="price" value="2000"/>  
    
</bean>  
</beans>

<context:annotationconfig/> 將隱式地向 Spring 容器注冊 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 這 4 個 BeanPostProcessor。

在配置文件中使用 context 命名空間之前,必須在 <beans> 元素中聲明 context 命名空間。


Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應用程序采用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋,而用 @Component 對那些比較中立的類進行注釋。

在使用 @Component 注釋后,Spring 容器必須啟用類掃描機制以啟用注釋驅動 Bean 定義和注釋驅動 Bean 自動注入的策略。Spring 2.5 對 context 命名空間進行了擴展,提供了這一功能,請看下面的配置:

<?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-2.5.xsd       
 http://www.springframework.org/schema/context        
 http://www.springframework.org/schema/context/spring-context-2.5.xsd"
>      
    
<context:component-scan base-package="com.baobaotao"/>      
</beans> 


這里,所有通過 <bean> 元素定義 Bean 的配置內容已經被移除,僅需要添加一行 <context:component-scan/> 配置就解決所有問題了——Spring XML 配置文件得到了極致的簡化(當然配置元數據還是需要的,只不過以注釋形式存在罷了)。<context:component-scan/> 的 base-package 屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。

<context:component-scan/> 還允許定義過濾器將基包下的某些類納入或排除。Spring 支持以下 4 種類型的過濾方式,通過下表說明:


過濾器類型 說明
注釋 假如 com.baobaotao.SomeAnnotation 是一個注釋類,我們可以將使用該注釋的類過濾出來。
類名指定 通過全限定類名進行過濾,如您可以指定將 com.baobaotao.Boss 納入掃描,而將 com.baobaotao.Car 排除在外。

正則表達式 通過正則表達式定義過濾的類,如下所示: com\.baobaotao\.Default.*
AspectJ 表達式 通過 AspectJ 表達式定義過濾的類,如下所示: com. baobaotao..*Service+

 

context:component-scan base-package="com.baobaotao">      
    
<context:include-filter type="regex"        
        expression
="com\.baobaotao\.service\..*"/>      
    
<context:exclude-filter type="aspectj"        
        expression
="com.baobaotao.util..*"/>      
</context:component-scan>  


值得注意的是 <context:component-scan/> 配置項不但啟用了對類包進行掃描以實施注釋驅動 Bean 定義的功能,同時還啟用了注釋驅動自動注入的功能(即還隱式地在內部注冊了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此當使用 <context:component-scan/> 后,就可以將 <context:annotation-config/> 移除了。

注釋配置不一定在先天上優于 XML 配置。如果 Bean 的依賴關系是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那么注釋配置優于 XML 配置;反之如果這種依賴關系會在部署時發生調整,XML 配置顯然又優于注釋配置,因為注釋是對 Java 源代碼的調整,您需要重新改寫源代碼并重新編譯才可以實施調整。
如果 Bean 不是自己編寫的類(如 JdbcTemplate、SessionFactoryBean 等),注釋配置將無法實施,此時 XML 配置是唯一可用的方式。
注釋配置往往是類級別的,而 XML 配置則可以表現得更加靈活。比如相比于 @Transaction 事務注釋,使用 aop/tx 命名空間的事務配置更加靈活和簡單。