Spring中三種裝配bean的方式:自動、Java代碼及XML裝配

推薦使用裝配的順序:自動裝配》Java代碼》XML裝配

一,自動裝配

1、Spring從兩個角度實現自動化裝配:

  • 組件掃描(component scanning):Spring會自動發現應用上下文中所創建的bean。
  • 自動裝配(auto wiring):Spring自動滿足bean之間的依賴。
    組件掃描和自動裝配組合在一起就可以發揮強大的威力,它們能將你的顯示配置降到最低。以光盤和播放器爲例,具體看以下代碼:
//先定義一個CompactDisc 接口
public interface CompactDisc {
    //具體內容不重要
    public void play();
}
//定義一個CompactDisc實現類
@Component("popularMusic")
public class PopularMusic implements CompactDisc {
    @Override
    public void play() {
        System.out.println("播放流行樂");
    }
}
//
//配置類
@Configuration
@ComponentScan
public class CDPlayerConfig {
    //什麼都不用寫
}

到目前爲止,上述僅僅是一個孤零零的類,什麼也沒做。那麼我們應該如何使用呢?我們有最一般的方法就是new一個對象出來,然後再調用該對象的play()方法即可。但是用new創建對象的方法在實際的業務代碼中增加耦合,難以維護,因此纔有了Spring。接下來看一下如何用自動裝配的方式裝配bean(和普通的Java類,Spring中的特殊稱謂),繼而使用bean的方法。

//單元測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    private CompactDisc cd;
    @Test
    public void test(){
       cd.play();
    }
}

//測試結果:
播放流行樂

看完所有代碼會發現,上述代碼和平時自己寫的代碼沒啥區別,僅僅只多了幾個註解(帶@符號的),但就是這幾個註解起到了作用。你自己並沒手動創建一個CompactDisc 對象,而事實的確存在,而且成功運行,這一切都由Spring容器完成。
2、幾個常用的註解:
@Component: 註解用來說名該類作爲一個組件類,並告知Spring要爲該類創建bean
@Configuration: 該註解用來說名該類是一個配置類
@ComponentScan: 啓用Spring組件掃描(默認不啓用),默認掃描與配置類在同一包帶 有@ComPonent註解的類。有兩個屬性:
(1)basePackages:用來設置要進行組件掃描的基礎包,值爲數組,數組屬性爲字符串
(2)basePackageClasses: 設置的數組中包含了類,這些類所在的包將作爲組件掃描的基礎包
@RunWith(SpringJUnit4ClassRunner.class):在測試之前自動生成Spring的應用上下文
@ContextConfiguration(classes = CDPlayerConfig.class):加載配置類。
所以上述代碼大致的執行原理如下:
首先Spring容器首先加載配置類,根據配置類中的設置再去掃描指定包中的帶有@Component的類;然後Spring容器生成掃描到的所有類的對象(默認是單例的),然後根據需要(哪裏有@Autowired)自動裝配所需要的bean,至於bean的生命週期則由Spring管理。

二、Java代碼裝配

要用Java代碼來裝配bean,那麼配置類肯定也是少不了的,一個很簡單的配置類定義如下:

@Configuration
public class JavaConfig {
}

這裏和上面的配置類不同的地方是這裏刪除了@ComponentScan註解,因爲該註解使用進行設置組件掃描的,而在這裏的bean注入是通過Java代碼配置的,因此不需要。如果刪除之後,再去運行上述的CDPlayerTest的話會報BeanCreationException異常。因爲測試期間需要注入的CompactDisc 還未被創建。接下來看一個完整的配置類:

@Configuration
public class JavaConfig {

    /*
     * 聲明一個簡單的bean
     * */
    @Bean
    public ClassicalMusic classicalMusic(){
        return new ClassicalMusic();
    }
    /*
    * 聲明一個帶有依賴對象的bean
    * 在這裏通過使用構造器的方式來注入,當spring調用該方法時,它會自動裝配一個CompactDisc導配置方法中
    * */
    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc){
        return new CDPlayer(compactDisc);
    }
}

@Bean註解告訴Spring這個方法將會返回一個對象,該對象將會註冊爲Spring應用上下文中的bean。默認情況下bean的ID和方法名一致。
上述代碼中生命了兩個bean,即classicalMusic和cdPlayer,前者是一個簡單的bean,而後者是一個帶有依賴對象的bean,當spring調用該方法時,它會自動裝配一個CompactDisc導配置方法中。
在這裏需要以下三個需要注意的地方:

  • 在聲明帶有依賴對象的bean時,必須首先聲明被依賴的bean,即這裏的classicalMusic(ClassicalMusic 實現了CompactDisc )。
  • 這裏生成的bean是單例的,若你再聲明一個實現了CompactDisc 接口的bean時,則會報錯。
  • 帶有@Bean註解的方法可以採用任何必要的Java功能來產生bean實例(構造器和Setter方法是@Bean方法的簡單樣例)。

既然上述代碼已經配置好了,接下來接看一下如何使用已經配置好的bean,代碼如下:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
        CompactDisc compactDisc = (ClassicalMusic)configApplicationContext.getBean("classicalMusic");
        compactDisc.play();
    }
}

//結果
播放古典音樂

上述代碼大致的執行原理如下:

通過創建AnnotationConfigApplicationContext 對象來配置後的Spring上下文,然後利用上下文來獲取對應的bean,繼而使用對應的方法。

三、XML裝配

1、藉助構造器注入初始化bean
兩種基本的配置方式:

  • constructor-arg 元素
  • 使用Spring3.0中引入的c-命名空間

兩者的區別在很大程度上就是是否繁瑣以及c-命名空間不能注入集合。
下面來看一下一課程-老師的例子做個簡單說明:

//課程類
public class Course {
    String courseName;
    double mark;

    public Course(String courseName, double mark) {
        this.courseName = courseName;
        this.mark = mark;
    }
}

//老師類
public class Teacher{

    private Course course;
    public Teacher(Course course){
        this.course = course;
    }
    public void teach() {
        System.out.println("我教的課程:"+course.courseName+" 績點爲:"+course.mark);
    }
}


//配置文件
<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.xsd">
    <!--Course bean-->
    <bean id = "course" class="com.tyf.day4.Course">
        <constructor-arg value="數學" />
        <constructor-arg value="4.0" />
    </bean>

    <!--Teacher bean-->
    <bean id = "teacher" class="com.tyf.day4.Teacher">
        <constructor-arg ref="course" />
    </bean>
</beans>


//測試類
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("com\\tyf\\day4\\ config.xml");
        Teacher teacher = (Teacher) context.getBean("teacher");
        teacher.teach();
    }
}


//測試結果
我教的課程:數學 績點爲:4.0

上述代碼僅僅說明了一個使用元素進行構造器注入初始化bean的一個很簡單的方法,接下來再看一下如何使用c-命名空間來初始化bean(僅展示xml配置文件)。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Course bean-->
    <bean id = "course" class="com.tyf.day4.Course">
        <constructor-arg value="數學" />
        <constructor-arg value="4.0" />
    </bean>

    <!--Teacher bean-->
    <bean id = "teacher" class="com.tyf.day4.Teacher" c:_0-ref = "course"/>
</beans>

可以看出使用c:-命名空間使得xml配置很簡潔,易於讀懂。_0說的是構造器中第一個參數,同理_1說的是構造器中第二個參數,一次類推。
那麼如果構造器參數列表中有集合類型,則不能使用c:-命名空間,只能使用元素。假如課程中有一個選課學生名字並且使用構造器來注入該屬性:

//課程類
public class Course {
    String courseName;
    double mark;
    List<String> students;//選課學生姓名
    public Course(String courseName, double mark,List<String> studenets) {
        this.courseName = courseName;
        this.mark = mark;
        this.students = studenets;
    }
}
//教師類
public class Teacher{

    private Course course;
    public Teacher(Course course){
        this.course = course;
    }
    public void teach() {
        System.out.println("我教的課程:"+course.courseName+" 績點爲:"+course.mark);
        System.out.println("選課學生:");
        for(String name:course.students){
            System.out.println(name);
        }
    }
}
//測試類不變


//xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Course bean-->
    <bean id = "course" class="com.tyf.day4.Course">
        <constructor-arg value="數學" />
        <constructor-arg value="4.0" />
        <constructor-arg>
            <list>
                <value>Yafeng</value>
                <value>Xiaoshuang</value>
            </list>
        </constructor-arg>
    </bean>
    <!--Teacher bean-->
    <bean id = "teacher" class="com.tyf.day4.Teacher" c:_0-ref = "course"/>
</beans>


//結果
我教的課程:數學 績點爲:4.0
選課學生:
Yafeng
Xiaoshuang

從上述代碼可以看出,list 是作爲Constructor的一個子元素來進行工作的,list中又有value子元素用來給List中的屬性賦值。

2、通過Setter方法注入屬性

將Teacher類改爲如下(其他不變):

//老師類
public class Teacher implements People {

    private Course course;
    public void setCourse(Course course){
        this.course = course;
    }
    @Override
    public void teach() {
        System.out.println("我教的課程:"+course.courseName+" 績點爲:"+course.mark);
        System.out.println("選課學生:");
        for(String name:course.students){
            System.out.println(name);
        }
    }
}
//XML文件中改變的部分
<!--Teacher bean-->
<bean id = "teacher" class="com.tyf.day4.Teacher">
    <property name="course" ref="course" />
</bean>

這裏注入屬性的時候用的是property,其它基本和constructor基本一樣。而構造器注入有c-命名空間,而Sertter屬性注入方法也有一個p-命名空間,用法和c-命名空間一致。

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