Spring系列.依賴注入配置

程式設計師自由之路發表於2020-06-11

依賴注入的配置

Spring的依賴注入分為基於建構函式的依賴注入基於setter方法的依賴注入

基於建構函式的依賴注入

        <!-- 通過構造器引數索引方式依賴注入 -->
	<bean id="byIndex" class="cn.javass.spring.chapter3.HelloImpl3">
		<constructor-arg index="0" value="Hello World!"/>
		<constructor-arg index="1" value="1"/>
	</bean>
	<!-- 通過構造器引數型別方式依賴注入 -->
	<bean id="byType" class="cn.javass.spring.chapter3.HelloImpl3">
		<constructor-arg type="java.lang.String" value="Hello World!"/>
		<constructor-arg type="int" value="2"/>
	</bean>
	<!-- 通過構造器引數名稱方式依賴注入 -->
	<bean id="byName" class="cn.javass.spring.chapter3.HelloImpl3">
		<constructor-arg name="message" value="Hello World!"/>
		<constructor-arg name="index" value="3"/>
	</bean>
	<!-- 通過靜態的工廠方法注入 -->
	<bean id="byName" class="cn.javass.spring.chapter3.HelloImpl3" factory-method="getBean">
		<constructor-arg name="message" value="Hello World!"/>
		<constructor-arg name="index" value="3"/>
	</bean>

基於setter方法的依賴注入

    <bean class="...HelloImpl4">
	    <property name="message" value="Hello"/>
	    <property name="index" value="1"/>  //value中的值全部是字串形式,如果轉換出錯會報異常
	</bean>
	<bean id="Hello2" class="com.csx.personal.web.services.HelloImpl2">
		<property name="msg" ref="message"/>  //msg屬性是一個類物件
	</bean>

迴圈依賴:建立Bean A需要Bean B,建立Bean B需要Bean C,建立Bean C需要Bean A 這樣就形成了迴圈依賴。 Spring的解決方案:Spring建立Bean的時候會維護一個池,在建立A的時候會去池中查詢A是否在池子中,假如發現就丟擲迴圈依賴異常。

避免依賴注入時的迴圈依賴:可以使用setter方式注入,不要使用構造器形式的注入。

依賴配置常見列子

常量值注入配置

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <!-- results in a setDriverClassName(String) call -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="masterkaoli"/>
    </bean>

注入其他Bean

這邊分別給出了一個引用當前容器和引用父容器中Bean的列子。

   <bean id="Hello2" class="com.csx.personal.web.services.HelloImpl2">
	<property name="msg">        //msg屬性是一個類物件
            <ref  bean="message"/>   //引用同一個容器中id="message"的Bean
       </property>
   </bean>
    
    <!-- 引用父容器中的Bean -->
    <!-- in the parent context -->
    <bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as here -->
    </bean>
 
    <!-- in the child (descendant) context -->
    <bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
    </bean>

注入內部Bean

內部bean:這種bean一般只讓某個外部bean使用(和內部類相似),不讓容器中的其他Bean使用。

    <bean id="outer" class="...">
        <property name="target">
             <!-- this is the inner bean -->
            <bean class="com.example.Person"> 
                <property name="name" value="Fiona Apple"/>
                <property name="age" value="25"/>
            </bean>
        </property>
   </bean>

集合的注入

集合類的注入建議使用util名稱空間

    <util:map id="myMap" key-type="java.lang.String" value-type="java.lang.String">
        <entry key="key1" value="chen"/>
        <entry key="key2" value="zhao"/>
    </util:map>

    <util:list id="myList" value-type="java.lang.String">
        <value>chen</value>
        <value>zhao</value>
    </util:list>
    
    <util:set id="mySet" value-type="java.lang.String" scope="singleton">
        <value>chen</value>
        <value>zhao</value>
    </util:set>
    
    <util:properties id="myProp" location="classpath:xx.properties"/>

null值和空字串的注入

   <bean class="...HelloImpl4">
        <property name="message"><null/></property> //null值
        <property name="index" value=""/>  //空字串
  </bean>

使用depends-on屬性

depends-on屬性用來指定bean的初始化順序。這個屬性只對scope是單列的bean生效

    <!--在例項化beanOne之前先例項化manager和accountDao這兩個bean-->
    <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
	<property name="manager" ref="manager" />
    </bean>
    <bean id="manager" class="ManagerBean" />
    <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

懶載入

bean的定義中有一個lazy-init這個屬性,用來設定單列bean在容器初始化後是否例項化這個bean。預設情況下容器是會例項化所有單例bean的,我們也建議這麼做,因為這樣能在容器初始化階段就發現bean配置是否正確。如果一個Bean按照下面的設定,lazy-init被設定為true那麼它不會被容器預初始化,只有在被使用的時候才被初始化。但是如果有一個單列類依賴了這個Bean,那麼這個被設定成懶載入的Bean還是會被預初始化。

   <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>

如果想設定全域性的單例Bean都不要預先初始化,那麼可以在xml中做如下設定:

    <beans default-lazy-init="true">
        <!-- no beans will be pre-instantiated... -->
    </beans>

Autowiring相關

當我們要往一個bean的某個屬性裡注入另外一個bean,我們會使用property +ref標籤的形式。但是對於大型專案,假設有一個bean A被多個bean引用注入,如果A的id因為某種原因修改了,那麼所有引用了A的bean的ref標籤內容都得修改,這時候如果使用autowire="byType",那麼引用了A的bean就完全不用修改了。

 <!--autowire的用法如下,對某個Bean配置autowire模式 -->
 <!--和給Bean的屬性新增@AutoWired註解的效果一致-->
 <bean id="auto" class="example.autoBean" autowire="byType"/>

autowire的幾種模式:

  • no模式:也是預設模式,這種模式下不會進行自動屬性注入,需要我們自己通過value或者ref屬性進行配置;
  • byName模式:通過屬性的名稱自動裝配,Spring會在容器中查詢名稱與bean屬性名稱一致的bean,並自動注入到bean屬性中。當然bean的屬性需要有setter方法。例如:bean A有個屬性master,master的setter方法就是setMaster,A設定了autowire="byName",那麼Spring就會在容器中查詢名為master的bean通過setMaster方法注入到A中;
  • byType:通過型別自動裝配(注入)。Spring會在容器中查詢類(Class)與bean屬性類一致的bean,並自動注入到bean屬性中,如果容器中包含多個這個型別的bean,Spring將丟擲異常。如果沒有找到這個型別的bean,那麼注入動作將不會執行;
  • constructor:類似於byType,但是是通過建構函式的引數型別來匹配。假設bean A有建構函式A(B b, C c),那麼Spring會在容器中查詢型別為B和C的bean通過建構函式A(B b, C c)注入到A中。與byType一樣,如果存在多個bean型別為B或者C,則會丟擲異常。但時與byType不同的是,如果在容器中找不到匹配的類的bean,將丟擲異常,因為Spring無法呼叫建構函式例項化這個bean;
  • default : 採用父級標籤(即beans標籤的default-autowire屬性)的配置。

需要注意的是上面這5中方式注入都需要我們提供相應的setter方法,通過@Autowired的方式不需要提供相應的setter方法。

自動裝配的缺點

  • 屬性和建構函式引數設定中的顯式依賴項會覆蓋自動裝配;

將Bean排除自動裝配

如果按照下面的方式配置了Bean,那麼這個Bean將不會作為自動裝配的候選Bean。但是autowire-candidate自會對byType形式的自動注入生效,如果我們是通過byName的形式進行自動注入,那麼還是能注入這個Bean。

    <bean id="auto" class="example.autoBean" autowire="byType" autowire-candidate="false"/>

如果我們只想讓某些Bean作為自動裝配的候選Bean,那麼可以進行全域性設定。如果做了下面的配置,那麼只有id為bean1和bean2的Bean才會成為自動裝配的候選Bean。同時default-autowire-candidates的值支援正規表示式形式。但是強烈建議不要配置這個選項的值,使用預設的配置就行。

    <beans default-autowire-candidates="bean1,bean2">
    </beans>

方法注入(單例依賴原型Bean)

我們在配置bean的時候首先要考慮這個bean是要配置成單例模式還是其他模式。 在我們的應用中,絕大多數類都是單例類。當單例類引用單例類,或者原型類引用原型類、單例類時,我們只要像普通的配置方式就行了。但是當一個單例類引用原型類時,就會出現問題。這種情況可以使用下面的方式進行注入。

   @Service
   @Scope("prototype")
   public class MyService1 {
public void service() {
   System.out.println(this.toString() + ":id");
}
	}
	
	@Service
public abstract class MyService {

    private MyService1 service1;
    public void useService(){
     service1 = createService1();
     service1.service();
    }

    @Lookup("myService1")
    public abstract MyService1 createService1();

}
	
//也可以這樣配置bean
<bean id="serviceC" class="com.csx.demo.springdemo.service.ServiceC">
    <lookup-method bean="serviceD" name="createService"/>
</bean>
<bean id="serviceD" class="com.csx.demo.springdemo.service.ServiceD"     scope="prototype"/>

相關文章