Spring溫故而知新 - bean的裝配

按條件裝配bean

就是當知足特定的條件時Spring容器才建立Bean,Spring中經過@Conditional註解來實現條件化配置beanweb

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接口的實現spring

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對象,具體實現以下:session

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");
    }
}

測試代碼:ide

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的beanspa

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

@Component
public class AnimalInstance {

    @Autowired
    public Animal animal;
       
}

當Spring嘗試注入animal實例時,因爲Dog和Cat都繼承自Animal,因此此處產生了歧義,下面經過使用@Primary指定首選beancode

@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做用域。

相關文章
相關標籤/搜索