1. 程式人生 > >Spring溫故而知新 - bean的裝配(續)

Spring溫故而知新 - bean的裝配(續)

metadata test BE 狀態 放心 cati 如果 需要 efi

按條件裝配bean

就是當滿足特定的條件時Spring容器才創建Bean,Spring中通過@Conditional註解來實現條件化配置bean

package com.sl.ioc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AnimalConfig {
    
    @Bean(
"dog") @Conditional(DogCondition.class) public Dog DogInstance() { return new Dog(); } @Bean("cat") @Conditional(CatCondition.class) public Cat CatInstance() { return new Cat(); } }

@Conditional和 :Condition接口的實現

public @interface Conditional {

    
/** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); } public interface Condition { /** * Determine if the condition matches. * @param context the condition context *
@param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class} * or {@link org.springframework.core.type.MethodMetadata method} being checked * @return {@code true} if the condition matches and the component can be registered, * or {@code false} to veto the annotated component‘s registration */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }

Conditional註解通過value傳入一個類,實現Condition接口,通過實現Condition接口中matches方法決定是否需要裝配Bean,如果滿足條件需要創建bean則返回true,否則返回false

自己定義兩個繼承Condition接口的類:通過ConditionContext查找當前環境中是否存在dog或者cat屬性,如果存在,則創建對應的bean對象,具體實現如下:

package com.sl.ioc;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class DogCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {       
        Environment environment = context.getEnvironment();        
        boolean flag= environment.containsProperty("dog");        
        return flag;
    }
}

package com.sl.ioc;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class CatCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
        Environment environment = context.getEnvironment();
        
        boolean flag= environment.containsProperty("cat");
        
        return flag;
        
    }
}
package com.sl.ioc;
import org.springframework.stereotype.Component;
@Component
public interface Animal {
    
    void Say();
    
}

package com.sl.ioc;import org.springframework.stereotype.Component;

@Component
public class Cat implements Animal {
    @Override
    public void Say() {
        System.out.println("I am a cat");
    }
}

package com.sl.ioc;
import org.springframework.stereotype.Component;

@Component
public class Dat implements Animal {
    @Override
    public void Say() {
        System.out.println("I am a dog");
    }
}

測試代碼:

public class TestClass {

    @Test
    public void TestGetDoInstance() {
        
        System.setProperty("dog","");    
    ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
        
        String[] beanNames = context.getBeanDefinitionNames();

        for(String bean : beanNames) {

                System.out.println(bean);

        }
    }
}

運行測試可以看到輸出的beanname中會包含dog的bean:

技術分享圖片

自動裝配的歧義處理

Spring自動裝配時如果存在多個bean能夠匹配的話,那麽這種情況會阻礙Spring通過屬性、構造函數或方法進行裝配。針對這種情況,Spring提供了多種 可選方案來解決這個問題,可以選擇一個bean作為首選的bean,或者使用限定符來確定唯一bean

1:使用首選Bean

Spring提供@Primary註解來設置首選Bean,當初選自動裝配歧義時,會選擇裝配帶有@Primary的bean

沿用上面的示例代碼,嘗試裝載animal

@Component
public class AnimalInstance {

    @Autowired
    public Animal animal;
       
}

當Spring嘗試註入animal實例時,由於Dog和Cat都繼承自Animal,所以此處產生了歧義,下面通過使用@Primary指定首選bean

@Component
@Primary   //指定首選bean
public class Cat implements Animal {
    @Override
    public void Say() {
        System.out.println("I am a cat");
    }
}

同樣也可以使用XML配置來實現:<bean>元素提供了primary屬性來設置首選bean

<bean id="cat"  class="com.sl.ioc.Cat" primary ="true" >

測試代碼:

public class TestClass {
    @Test
    public void TestGetDoInstance() {
        //應用上下文
        
        ApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
        
        AnimalInstance animalInstance = context.getBean(AnimalInstance.class);
        animalInstance.animal.Say();
    }
}

運行結果:

技術分享圖片

首選項只是標識一個優先選擇裝載的bean,如果配置了多個@Primary,那麽將帶來新的歧義,Spring依然無法完成自動裝配,可以通過下面限定符來解決這個問題

2:使用限定符

Spring提供@Qualifier註解來指定想要註入的具體bean。例如上面的示例,如果指定註入dog:

package com.sl.ioc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class AnimalInstance {

    @Autowired
    @Qualifier("dog")
    public Animal animal;
    
}

解釋一下:@Qualifier("dog")表示指定的bean具有”dog”限定符,spring中bean如果沒有指定限定符,會使用默認限定符,即使用beanID作為限定符。所以上面是恰好使用了dog bean的ID作為了限定符。也可以寫成如下方式:

@Component
@Qualifier("specialdog")    //為bean指定限定符
public class Dog implements Animal
{
    @Override
    public void Say() {
        System.out.println("I am a dog");
    }    
}
@Component
public class AnimalInstance {

    @Autowired
    @Qualifier("specialdog")    //使用上面定義的限定符
    public Animal animal; 
}

Bean的作用域

Spring容器在創建bean實例的同時,還允許指定bean實例的作用域,常見作用域有一下幾種:

1:單例作用域(Singleton)

2:原型作用域(Prototype)

3:會話作用域(Session)

4:請求作用域(Request)

5:全局會話作用域(globalSession)

Singleton作用域

在整個應用中,Spring IOC容器為使用singleton模式的bean只創建一個實例,Spring將會緩存Bean實例,任何對該類型beand請求都會返回該實例。單例也是Spring默認的作用域。具體使用如下,通過XML配置

<bean id="beanid"  class="com.sl.ioc.xxx" scope="singleton" ></bean>

<bean>元素提供了scope屬性來設置singleton作用域

對應的註解:

@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)

Prototype原型作用域

每次註入或者從Spring容器中獲取時都創建一個新的bean實例:

<bean id="beanid"  class="com.sl.ioc.xxx" scope="prototype" ></bean>

<bean>元素提供了scope屬性來設置singleton作用域

對應的註解:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Session會話作用域

在web應用中,針對每個會話,Spring容器根據bean定義創建的bean實例,只在當前會話Session中有效,XML配置如下:

<bean id="beanid"  class="com.sl.ioc.xxx" scope="session" ></bean>

針對某個HTTP Session,Spring容器會根據bean定義創建一個新的bean實例,該bean僅在當前HTTP Session內有效。所以可以根據需要放心的更改bean實例的內部狀態,而不影響其他Http Session中bean實例。當HTTP Session最終被廢棄的時候,在該HTTP Session作用域內的bean也會被銷毀掉。

Request 請求作用域

<bean id="beanid"  class="com.sl.ioc.xxx" scope="globalSession" ></bean>

在web應用中,針對每次請求,Spring容器根據bean定義創建新的bean實例,只在當前請求內有效

<bean id="beanid"  class="com.sl.ioc.xxx" scope="request" ></bean>

該bean實例只在當前請求內有效,在請求處理完成之後bean也會被銷毀掉

globalSession全局會話作用域

類似於session作用域,只是其用於portlet環境的web應用。如果在非portlet環境將視為session作用域。

Spring溫故而知新 - bean的裝配(續)