Spring裝配Bean的三種方式+導入和混合配置

Spring IoC與bean

A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application.java

bean是由Spring IoC容器實例化、裝配和管理的對象,不然,bean只是應用程序中的衆多對象之一。mysql

bean及其之間的依賴關係反映在容器使用的配置元數據中。web

咱們已經瞭解,Spring IoC容器可以幫咱們操做bean,可是前提是咱們須要配置元數據以告知Spring容器,它纔可以經過讀取這些配置,來實例化,裝配和管理bean對象。spring

而配置元數據的方式,就是咱們今天要總結的三種,分別是XML,Java註解以及Java代碼。咱們經過這幾種方式,向Spring容器傳達這些對象之間豐富的相互依賴關係。sql

該圖是Spring如何工做的高級視圖。能夠看到,應用程序類與配置元數據相結合,在建立並初始化ApplicationContext以後,就能夠得到一個徹底配置和可執行的系統或應用程序。數據庫

基於XML的顯式裝配

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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions go here -->
</beans>

id屬性表示bean的惟一標識。安全

class屬性定義bean的類型並使用徹底限定的類名。架構

bean實例的三種建立方式

<!-- 1、使用默認構造函數建立,若是沒有該默認構造函數,則建立失敗。 -->
<bean id="userService" class="com.smday.service.impl.UserServiceImpl"></bean>

<!-- 2、使用普通公章中的方法建立對象(使用某個類中的方法建立對象,並存入spring容器 -->
<bean id="instanceFactory" class="com.smday.factory.InstanceFactory"></bean>
<bean id="userService" factory-bean="instanceFactory" factory-method="getUserService"></bean>
<!-- 3、使用工廠中的靜態方法建立對象 -->
<bean id="userService" class="com.smday.factory.StaticFactory" factory-method="getUserService"></bean>

依賴注入的兩種方式

構造器注入方式

<bean>標籤的內部定義<constructor-arg>標籤。app

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}
<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

value:用於提供基本類型和String類型的數據。框架

ref:用於提供其餘的bean類型數據,在spring的ioc核心容器中出現過的bean對象。

在建立對象時,若是沒有提供構造器中的這些參數,將沒法建立該對象。

setter方法注入方式

<bean>標籤的內部定義<property>標籤。

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}
<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

name:指定注入時調用的set方法的屬性名稱。

value:提供基本類型和String類型的數據。

ref:提供其餘的bean類型數據,在spring的ioc核心容器中出現過的bean對象。

若是某個成員必須有值,但並無提供相應的setter方法,將會出錯。

【集合類型的注入】:分爲list和map兩類結構

<bean id="userService" class="com.smday.service.impl.UserServiceImpl">
        <property name="myStrs">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>BBB</value>
            </array>
        </property>
        <property name="myList">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>BBB</value>
            </list>
        </property>
        <property name="mySet">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>BBB</value>
            </set>
        </property>
        <property name="myMap">
            <map>
                <entry key="testA" value="AAA"></entry>
                <entry key="testB" >
                    <value>BBB</value>
                </entry>
            </map>
        </property>
        <property name="myProp">
            <props>
                <prop key="testC">CCC</prop>
                <prop key="testD">DDD</prop>
            </props>
        </property>
    </bean>

list結構可使用list、array和set標籤。

map結構可使用map和props標籤。

利用命名空間簡化xml

1、p-namespace使用bean元素的屬性來提供屬性值和協做bean,而不是使用嵌套的<property/>元素,下面兩段bean的配置效果相同。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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">

    <!-- 傳統的xml聲明 -->
    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="foo@bar.com"/>
    </bean>
    <!-- p-namespace 聲明 -->
    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="foo@bar.com"/>
</beans>

2、Spring 3.1中新引入的c-namespace容許使用內聯屬性來配置構造函數參數,而不是使用嵌套的<constructor-arg>

<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">

    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- 傳統的xml聲明 -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
        <constructor-arg value="foo@bar.com"/>
    </bean>

    <!-- c-namespace 聲明 -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>

</beans>

基於Java的顯式裝配

@Bean 和 @Configuration

這兩個註解類是Spring's new java-configuration的核心構件。

@Bean註解用於指示方法實例化、配置和初始化要由Spring IoC容器管理的新對象,@Bean註解的做用與<bean/>標籤相同。簡單的理解就是這個註解能夠告知spring,這個方法上面將來但願註冊一個應用上下文的bean對象,所以用@Bean註解的方法須要利用Java代碼,定義返回一個bean實例的邏輯。

@Configuration註解一個類代表這個類的主要目的是做爲bean定義的源,@Configuration類容許經過簡單地調用同一類中的其餘@Bean方法來定義bean之間的依賴關係。簡單的理解就是一個配置類,自此以後,你能夠在該配置類中完成在xml中完成的事,但形式會有所不一樣。

下面這個例子是一個最簡單的配置類的定義:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

它的做用和下面這段xml配置的方式等價:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

Bean的依賴

一個@Bean註釋的方法能夠有任意數量的參數來描述構建該bean所需的依賴關係。例如,若是咱們的TransferService須要一個AccountRepository,咱們能夠經過一個方法參數來實現這個依賴:

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

當spring調用transferService方法建立bean時,會自動裝配accountRepository到配置方法中,再次印證了那句話,帶有@Bean註解的方法能夠編寫任何須要的Java代碼來產生Bean的實例,例如構造器,setter方法,以及任何能夠產生實例的方法。

初始化Spring容器

AnnotationConfigApplicationContext是Spring 3.0中新增的。它不只能夠接受@Configuration配置類做爲輸入,還能夠接受普通的@Component類和使用JSR-330元數據註釋的類。

初始化spring容器,獲取Myservice對象,調用對象的方法。

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

定製bean的命名

默認狀況下,配置類將會使用@Bean註解的方法的名稱做爲bean的名稱,這一點能夠經過name屬性修改。

@Configuration
public class AppConfig {

    @Bean(name = "myFoo")
    public Foo foo() {
        return new Foo();
    }
}

如上:若是沒有指定name屬性,該bean的名稱爲foo,若是指定了name屬性,這裏的名稱就是myFoo。

基於註解的自動裝配

Spring從如下兩個角度實現自動裝配:

  • 組件掃描:Spring自動發現應用上下文中所建立的bean。
  • 自動裝配:Spring自動知足bean之間的依賴。

首先仍是來看一段簡單的例子:

//定義一個UserService接口
public interface UserService {
    void add();
}
//定義實現類,注意加上@Component註解,告知spring建立這個bean
@Component
public class NormalUserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("添加用戶");
    }
}
//controller層,注意@Autowired註解,自動按類型注入Userservice
@Component
public class UserController {
    @Autowired
    private UserService userservice;

    public void add(){
        userservice.add();
    }
}
//定義配置類,注意@ComponentScan("com.my.demo")註解開啓組件掃描
@Configuration
@ComponentScan("com.my.demo")
public class Appconfig {

}
//整合junit測試類進行測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Appconfig.class)
public class UserServiceTest {
    @Autowired
    private UserService userservice;
    @Test
    public void testMethod(){
        userservice.add();
    }
}

以上就是一套能夠正常運行的簡單案例,固然其中有很多可能出現的問題或者是其餘能夠實現相同功能的方案,咱們都暫且不提。其中出現了許多自動化裝配bean的註解,咱們一一來看:

自動裝配的經常使用註解

【@Component】

  • 做用:將當前類對象存入spring容器中。
  • 屬性:value,用於指定bean的id,不指定value時,默認值爲當前類名首字母小寫。

值得一提的是,在三層架構中,Spring框架提供了明確的三層註釋,做用與@Component相同,但語義更加清晰明瞭,分別是:

Controller:表現層、Service:業務層、Respository:持久層

【@Autowired】

  • 做用:自動按照類型注入,只要容器中有惟一的一個bean對象類型和要注入的變量類型匹配,就能夠注入成功。
  • 若是ioc容器中沒有任何bean類型和要注入的變量類型匹配,則報錯(解決方法是,設置required屬性的值爲false,若是沒找到對應類型的bean,則會出於未裝配狀態),若是ioc容器中有多個類型匹配時,出現歧義性,也會報錯。
  • 出現位置:既能夠是構造器,也能夠是setter方法,甚至任何其餘的方法,Spring都會嘗試知足方法參數上聲明的依賴。
  • 細節:在使用註解注入時,set方法就不是必須的了。

當出現歧義性時,知足類型要求的bean不是惟一時,能夠考慮使用@Qualifier和@Resource註解,參考:Spring解決自動裝配歧義性的幾種方案

【@Configuration】

  • 做用:指定當前類是一個配置類
  • 細節:當配置類做爲AnnotationConfigApplicationContext對象建立的參數時,該註解能夠不寫。

【@ComponentScan】

  • 做用:開啓組件掃描,用於經過註解指定spring在建立容器時要掃描的包。
  • 屬性:value,和basePackages的做用相同,指定建立容器時要掃描的包。
  • 若是不指定value或者basePackages的值,將會默認掃描與配置類相同的包

設置spring組件掃描的基礎包的幾種方案:

  • @ComponentScan("com.my.demo")

  • @ComponentScan(basePackages = {"com.my.demo.web","com.my.demo.service"})

  • @ComponentScan(basePackageClasses = {UserController.class, UserService.class, UserDao.class}),相較於第二種,較爲安全。

須要注意的是,組件掃描默認是不開啓的,咱們須要經過該註解顯式通知Spring,告訴它去尋找帶有@Component註解的類,去建立該類的bean對象。

開啓組件掃描的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

        <!--配置掃描,至關於@ComponentScan("com.my.demo")-->
        <context:component-scan base-package="com.my.demo"/>
</beans>

既然使用xml方式開啓組件掃描,那麼測試的時候須要謹慎,要讀取該xml文件:@ContextConfiguration("classpath:applicationContext.xml")

導入和混合配置

直接以例子呈現:

#jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=123456
/**
 * @author Summerday
 * <p>
 * 和spring鏈接數據庫相關的配置類
 */
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 建立queryRunner對象
     *
     * @param dataSource
     * @return
     */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }

    /**
     * 建立數據源對象
     *
     * @return
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

【@Value】

  • 做用:用於基本類型和string類型的數據。
  • 屬性:value,指定數據的值,可使用spring中的SpEL,spring的el表達式。
  • SpEL的寫法:${表達式}。
/**
 * 主配置類
 */
@Configuration
@ComponentScan(basePackages = "com.smday")
@Import(JdbcConfig.class)
@PropertySource("classpath:JdbcConfig.properties")
public class SpringConfiguration {
}

【@Import】

  • 做用:用於導入其餘的配置類。
  • 屬性:value,指定其餘配置類的字節碼,使用Import註解後,有該註解的類爲父配置類,導入的都是子配置類。

【@PropertySource】

  • 做用:做用於指定properties文件的位置。
  • 屬性:value,指定文件的名稱和路徑,關鍵字classpath表示類路徑下。

最後的最後,引用Spring in Action中做者的話:自動化配置、基於Java的顯式配置以及基於xml的顯式配置都描述了Spring應用中組件以及這些組件之間的關係。做者建議儘量使用自動化的配置,其次若是須要顯式配置,但願優先選擇基於Java的配置,類型安全且易懂。

相關文章
相關標籤/搜索