Spring - 02 - 自動化裝配Bean


上面一篇文章中,學習了"Spring - 01 - 通過XML裝配Bean",下面學習自動化裝配Bean


自動化裝配Bean


Spring從兩個角度來實現自動化裝配

組件掃描:Spring會自動發現應用上下文中所創建的bean

自動裝配:Spring自動滿足bean之間的依賴


一、創建可被發現的bean

直接上代碼

package com.test.spring.server.test4;

/**
 * Created by CYX on 2018/4/17.
 */
public interface CompactDisc {

    void play();

}
package com.test.spring.server.test4;

import org.springframework.stereotype.Component;

/**
 * @author CYX
 * @create 2018-04-17-20:47
 */
//該註解表明該類會作爲組件類,並告知Spring要爲這個類創建bean。
@Component
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}
applicationContext.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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:component-scan base-package="com.test.spring.server.test4"/>

</beans>

通過XML啓用組件掃描

測試主類

package com.test.spring.server.test4;

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

/**
 * 測試組件掃描
 *
 * @author CYX
 * @create 2018-04-17-21:08
 */
public class Test4App {

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("/spring/test4/applicationContext.xml");

        SgtPeppers sgtPeppers = context.getBean("sgtPeppers",SgtPeppers.class);

        sgtPeppers.play();
    }

}
輸出結果:


和之前 在XML中顯示配置Bean(Spring - 01 - 通過XML裝配Bean) 對比一下,我們並沒有在applicationContext.xml配置文件中配置<bean>元素用來顯示的聲明bean。

只是在需要創建bean對象的類上增加了一個註解@Component。

@Component註解的作用是 把普通pojo對象 實例化到Spring容器中,相當於applicationContext.xml配置文件中的:

<bean id="sgtPeppers" class="com.test.spring.server.test4.SgtPeppers"></bean>
Spring Framework 5.0.5.RELEASE API  的解釋:
Indicates that an annotated class is a "component". 
Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.
表示註釋類是“組件”。
當使用基於註釋的配置和類路徑掃描時,這些類被認爲是自動檢測的候選對象。

當我們的類不屬於各種歸類時,我們就可以使用@Component來標註這個類。被標註的類,可以認爲是"組件"

不過這裏要注意,僅僅使用@Component註解時,我們並沒有指定bean 的id,默認使用類名的首字母小寫。

<context:component-scan>配置的作用是 開啓組件自動掃描

<context:component-scan base-package="com.test.spring.server.test4"/>的含義是,掃描com.test.spring.server.test4包下的所有註解。



二、爲組件掃描的bean命名

Spring應用 上下文 中所有的bean都會給定一個id。

在前面的例子中,我們並沒有明確的爲 SgtPeppers 指定 bean-id;

但Spring會爲其指定一個id,具體的講,這個bean所給定的id,爲 sgtPeppers,也就是將類名的第一個字母變爲小寫。

如果要爲這個bean設置不同的bean-id,需要將 期望id 作爲值,傳遞給@Component註解。

例如:

@Component("sssgtPeppers")
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}
修改下主方法的bean id,其他不用修改,看下運行結果:



還有一種爲bean命名的方式,這種方式不使用@Component註解,而是使用Java依賴注入規範所提供的的@Named註解來設置。

注意:使用@Named註解,需要添加依賴

<dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
</dependency>
@Named("test4-sgtPeppers")
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}

修改下主方法代碼,看下運行結果:


Spring支持將@Named作爲@Component註解的替代方案。

不過還是推薦@Component註解



三、使用Java Config自動裝配Bean

在上面代碼的基礎上修改一下

package com.test.spring.server.test4;

/**
 * Created by CYX on 2018/4/17.
 */
public interface CompactDisc {

    void play();

}
package com.test.spring.server.test4;


import javax.inject.Named;

/**
 * @author CYX
 * @create 2018-04-17-20:47
 */
//該註解表明該類會作爲組件類,並告知Spring要爲這個類創建bean。
//@Component("sgtPeppers")
//@Component("sssgtPeppers")
@Named("test4-sgtPeppers")
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}
package com.test.spring.server.test4;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * 類CDPlayerConfig通過Java代碼定義了Spring的裝配規則。
 * <p>
 * 在後面我們再詳細介紹
 * <p>
 * 這裏,我們只需要觀察一下CDPlayerConfig類並沒有顯示的聲明任何bean。
 * <p>
 * 只不過它使用了@ComponentScan註解,這個註解能夠在Spring中啓用組件掃描。
 * <p>
 * 如果沒有其他配置的話,@ComponentScan默認會掃描與配置類相同的包。
 * <p>
 * Spring將會掃描這個包以及這個包下所有的子包,查找帶有@Component註解的類。
 *
 * 這種方式,我們暫時註釋掉,使用XML來開啓組件掃描。
 *
 * @author CYX
 * @create 2018-04-17-20:57
 */
@Configuration
@ComponentScan(basePackages = "com.test.spring.server.test4")
public class CDPlayerConfig {
}
package com.test.spring.server.test4;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 測試組件掃描
 *
 * @author CYX
 * @create 2018-04-17-21:08
 */
public class Test4App {


    public static void main(String[] args) {

//        ApplicationContext context = new ClassPathXmlApplicationContext("/spring/test4/applicationContext.xml");

        ApplicationContext context =new AnnotationConfigApplicationContext(CDPlayerConfig.class);
        SgtPeppers sgtPeppers = context.getBean("test4-sgtPeppers",SgtPeppers.class);

        sgtPeppers.play();

    }

}

注意:這裏我並沒有使用applicationContext.xml,將之前XML配置文件中的

<context:component-scan base-package="com.test.spring.server.test4"/>註釋掉,所以並沒有貼出來。

輸出結果:


創建Java Config類的關鍵在於爲其添加@Configuration註解。
@Configuration註解表明這個類是一個配置類,該類應該包含在Spring應用上下文中如何創建bean的細節。


@ComponentScan註解默認會掃描與配置類相同的包,以及這個包下面所有的子包。

然後查找帶有@Component註解,找到的話,就會爲他們創建bean實例。



四、通過爲bean添加註解實現自動裝配

自動裝配就是讓Spring自動滿足bean依賴的一種方法;

在滿足依賴的過程中,會在Spring應用上下文中尋找某個bean需求的其他bean。

爲了聲明要進行自動裝配,可以藉助Spring 的@Autowired註解

代碼優先

package com.test.spring.server.test05;

/**
 * Created by CYX on 2018/4/17.
 */
public interface CompactDisc {

    void play();

}
package com.test.spring.server.test05;

import org.springframework.stereotype.Component;

/**
 * @author CYX
 * @create 2018-04-30-17:09
 */
@Component
public class SayHello {

    public SayHello() {
        System.out.println("SayHello 默認構造器");
    }

    public void sayHelloWithName() {
        System.out.println("SayHello :hello cyx");
    }

    public void sayHelloWithAddress() {
        System.out.println("SayHello :hello 南京");
    }

}
package com.test.spring.server.test05;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author CYX
 * @create 2018-04-17-20:47
 */
@Component("sssgtPeppers")
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

//    @Autowired
    private SayHello sayHello;

    /*@Autowired
    public void setSayHello(SayHello sayHello) {
        this.sayHello = sayHello;
    }*/

    @Autowired
    public SgtPeppers(SayHello sayHello) {
        this.sayHello = sayHello;
    }

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
        sayHello.sayHelloWithAddress();
        sayHello.sayHelloWithName();
    }
}
applicationContext.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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">


    <context:component-scan base-package="com.test.spring.server.test05"/>

</beans>

主方法

package com.test.spring.server.test05;

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

/**
 * 測試組件掃描
 *
 * @author CYX
 * @create 2018-04-17-21:08
 */
public class Test5App {


    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("/spring/test05/applicationContext05.xml");

        SgtPeppers sgtPeppers = context.getBean("sssgtPeppers", SgtPeppers.class);

        sgtPeppers.play();

    }

}

運行結果:


@Autowired 註解不僅可以放在成員變量、setter方法上,還可以放在構造器上。



五、通過Java代碼裝配Bean

在上面,我們已經初步使用過通過Java Config來裝配Bean,下面詳細的瞭解一下。

代碼優先:

package com.test.spring.server.test06;

/**
 * Created by CYX on 2018/4/17.
 */
public interface CompactDisc {

    void play();

}
package com.test.spring.server.test06;


/**
 * @author CYX
 * @create 2018-04-17-20:47
 */
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";


    public SgtPeppers() {
    }

    public SgtPeppers(String title, String artist) {
        this.title = title;
        this.artist = artist;
    }

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}
package com.test.spring.server.test06;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author CYX
 * @create 2018-04-17-20:57
 */
@Configuration
@ComponentScan(basePackages = "com.test.spring.server.test06")
public class CDPlayerConfig {

    @Bean(name = "sssgtPeppers")
    public SgtPeppers initializationSgtPeppers() {
        return new SgtPeppers("888888","99999");
    }

}

主方法

package com.test.spring.server.test06;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 測試組件掃描
 *
 * @author CYX
 * @create 2018-04-17-21:08
 */
public class Test6App {


    public static void main(String[] args) {

        ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
        SgtPeppers sgtPeppers = context.getBean("sssgtPeppers", SgtPeppers.class);

        sgtPeppers.play();

    }

}

主方法


注意:

SgtPeppers類中已經沒有 @Component 註解了。

SgtPeppers類 的實例化,在 CDPlayerConfig類中實現。

@Bean註解 會告訴Spring這個方法將會返回一個對象,該對象要註冊爲Spring應用上下文中的bean。

@Bean(name = "sssgtPeppers") 同時,我們還爲bean 設置了id。

在默認情況下,bean 的id 與帶有@Bean註解的方法名是一樣的。


在CDPlayerConfig類中進行bean 實例化的時候,咱們還可以對它進行一些初始化的操作。

這些都是根據業務來看...我這裏模擬的話,只是,傳入兩個參數。



六、藉助Java Config實現注入

代碼優先

package com.test.spring.server.test06;

/**
 * Created by CYX on 2018/4/17.
 */
public interface CompactDisc {

    void play();

}
package com.test.spring.server.test06;

/**
 * @author CYX
 * @create 2018-04-30-17:09
 */
public class SayHello {

    public SayHello() {
        System.out.println("SayHello 默認構造器");
    }

    public void sayHelloWithName() {
        System.out.println("SayHello :hello cyx");
    }

    public void sayHelloWithAddress() {
        System.out.println("SayHello :hello 南京");
    }

}
package com.test.spring.server.test06;


import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author CYX
 * @create 2018-04-17-20:47
 */
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    @Autowired
    private SayHello sayHello;


    public SgtPeppers() {
    }

    public SgtPeppers(SayHello sayHello) {
        this.sayHello = sayHello;
    }

    public SgtPeppers(String title, String artist) {
        this.title = title;
        this.artist = artist;
    }

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
        sayHello.sayHelloWithAddress();
        sayHello.sayHelloWithName();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}
package com.test.spring.server.test06;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author CYX
 * @create 2018-04-17-20:57
 */
@Configuration
@ComponentScan(basePackages = "com.test.spring.server.test06")
public class CDPlayerConfig {

    @Bean(name = "sssgtPeppers")
    public SgtPeppers initializationSgtPeppers() {
        return new SgtPeppers("888888", "99999");
    }

    @Bean
    public SgtPeppers initializationSgtPeppersWithHello() {
        return new SgtPeppers(initializationSayHello());
    }

    @Bean
    public SayHello initializationSayHello() {
        return new SayHello();
    }

}
package com.test.spring.server.test06;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 測試組件掃描
 *
 * @author CYX
 * @create 2018-04-17-21:08
 */
public class Test6App {


    public static void main(String[] args) {

        ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
        SgtPeppers sgtPeppers = context.getBean("sssgtPeppers", SgtPeppers.class);

        sgtPeppers.play();

    }

}
運行結果:

在CDPlayerConfig類中我們增加了兩個bean,一個是用來注入的,另一個是被注入的。

在initializationSgtPeppersWithHello()方法中,並沒有像之前那樣子,傳入參數,而是傳入一個對象(方法)

看起來,SayHello對象,是通過調用initializationSayHello()方法得來的,但情況並非如此。

因爲 initializationSayHello()方法上添加了@Bean註解,Spring將會攔截所有對它的調用,並確保直接返回該方法所創建的bean,而不是每次都對其進行實際的調用。


Java Config類換一種寫法:

package com.test.spring.server.test06;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author CYX
 * @create 2018-04-17-20:57
 */
@Configuration
@ComponentScan(basePackages = "com.test.spring.server.test06")
public class CDPlayerConfig {

    @Bean(name = "sssgtPeppers")
    public SgtPeppers initializationSgtPeppers() {
        return new SgtPeppers("888888", "99999");
    }

    @Bean
    public SgtPeppers initializationSgtPeppersWithHello(SayHello sayHello) {
        return new SgtPeppers(sayHello);
    }

    @Bean
    public SayHello initializationSayHello() {
        return new SayHello();
    }

}

在這裏,initializationSgtPeppersWithHello(SayHello sayHello) 方法直接將對象傳入,這樣子,好理解一點。


參考文章:

https://www.cnblogs.com/Ming8006/p/6323633.html

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Component.html

《Spring 實戰》

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