一點一滴學習Spring(三)之bean高級裝配

環境與profile

在3.1版本中,Spring引入了bean profile的功能。要使用profile,你首先要將所有不同的bean整理到一個或多個profile之中,
再將應用部署到每個環境時,要確保對應不同的profile處於激活(active)的狀態。
在java配置中,可以用@Profile註解指定某個bean屬於哪一個profile

實例代碼:

@Configuration
@Profile("dev")
public class ProfileConfig {

    @Bean(name="simpleMovieLister")
    public SimpleMovieLister getSimpleMovieLister(){
        return new SimpleMovieLister();
    }

    @Bean(name="movieFinder")
    public MovieFinder getMovieFinder(){
        return new MovieFinder();
    }

}

這個實例可以看到@Profile應用到了類級別上,他會告訴Spring這個配置類中的bean只有在dev Profile激活時纔會創建。
若dev Profile沒有激活的話,那麼帶有@bean註解的方法都會被忽略
在Spring3.1中,只能在類級別上使用@Profile註解,從Spring3.2開始,也開始在方法級別上使用@Profile,與@Bean一同使用
使用實例:

@Configuration
public class ProfileConfig {

    @Bean(name="simpleMovieLister")
    @Profile("dev")
    public SimpleMovieLister getSimpleMovieLister(){
        return new SimpleMovieLister();
    }

    @Bean(name="movieFinder")
    @Profile("prod")
    public MovieFinder getMovieFinder(){
        return new MovieFinder();
    }
}

注意:
1、儘管SimpleMovieLister和MovieFinder在一個profile中,但只有當規定的Profile激活時,相應的bean纔會被創建。
2、可能會有其他的bean並沒有聲明在一個給定的profile範圍內。
3、沒有指定profile的bean始終都會被創建,與激活哪個profile沒有關係

在XML中配置Profile
我們可以通過元素的profile屬性,在xml中配置profile bean。

實例代碼

<?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:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd" profile="dev">
</beans>

還可以在跟元素<beans>元素中嵌套定義<beans>元素,而不是爲每個環境都創建一個profile xml文件。這能夠將所有的profile 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:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd" profile="dev">
    <beans profile="dev">
        ...
    </beans>
    <beans profile="prod">
        ...
    </beans>
</beans>

如何激活profile

Spring在確定哪個profile處於激活狀態時,需要依賴兩個獨立屬性
spring.profiles.activespring.profiles.default
以spring.profiles.active設置優先,若設置了spring.profiles.active屬性的話,它的值就會用來確定哪個profile是激活的。否則Spring將會查找spring.profiles.default的值,若均沒有設置,那麼就沒有激活的profiles,因此只會創建那些沒有定義在profile的bean

設值spring.profiles.active和spring.profiles.default值的方式
1、作爲DispatcherServlet的初始化參數
2、作爲web應用的上下文參數
3、作爲JNDI條目
4、作爲環境變量
5、作爲JVM的系統屬性
6、在集成測試類上,使用@ActiveProfiles註解設置

可以同時激活多個profile,即列出多個profile的值,中間以逗號分隔

實例代碼:
在web.xml文件中,作爲web應用的上下文參數配置
在web.xml文件中,作爲DispatcherServlet應用的初始化參數配置參數配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0">
    <display-name>canal</display-name>
    <context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>   
            classpath*:/applicationContext-*.xml
        </param-value>   
    </context-param>  

    <!--在web.xml文件中,作爲web應用的上下文參數配置-->
    <context-param>  
        <param-name>spring.profiles.active</param-name>  
        <param-value>dev</param-value>   
    </context-param> 

    <servlet>
        <servlet-name>Dispatcher Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <description>Spring MVC定義Bean文件,該文件爲空配置,所有配置交給上級WebApplicationContext來處理</description>
            <param-name>contextConfigLocation</param-name>
            <param-value>WEB-INF/beans.xml</param-value>
        </init-param>
        <!--在web.xml文件中,作爲DispatcherServlet應用的初始化參數配置參數配置-->
        <init-param>
            <param-name>spring.profiles.active</param-name>
            <param-value>dev</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Dispatcher Servlet</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

使用profile進行單元測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/resource/services_profile.xml"})
@ActiveProfiles("dev")
public class TestSpringBean_Profile {

    @Autowired
    private SimpleMovieLister simpleMovieLister;

    @Test
    public void test() {
        simpleMovieLister.testAutowired();
    }
}

條件化的bean

Spring4引入了一個新的註解@Conditional,他可以應用到帶有@Bean的註解的方法上。
如果給定的條件計算結果爲true,就會創建這個bean,否則的話,這個bean會被忽略

使用實例:

@Configuration
public class ConditionalConfig {

    @Bean(name="simpleMovieLister")
    @Conditional(ConditionMatch.class)
    public SimpleMovieLister getSimpleMovieLister(){
        return new SimpleMovieLister();
    }
    @Bean(name="movieFinder")
    @Conditional(ConditionMatch.class)
    public MovieFinder getMovieFinder(){
        return new MovieFinder();
    }

}

ConditionMatch類需要實現implements Condition接口

如下:

public class ConditionMatch implements Condition{

    @Override
    public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
        // TODO Auto-generated method stub
        Environment env = arg0.getEnvironment();
        return !env.containsProperty("magic");
    }

}

參數ConditionContext的方法如下:

public abstract BeanDefinitionRegistry getRegistry();
public abstract ConfigurableListableBeanFactory getBeanFactory();
public abstract Environment getEnvironment();
public abstract ResourceLoader getResourceLoader();
public abstract ClassLoader getClassLoader();

通過ConditionContext,我們可以做到如下幾點:
藉助getRegistry()返回 的BeanDefinitionRegistry,檢查bean定義;
藉助getBeanFactory()返回的ConfigurableListableBeanFactory,檢查bean是否存在,升值探查bean的屬性;
藉助getEnvironment()返回的Environment檢查環境變量是否存在,一級它的值是什麼;
讀取並探查getResourceLoader()返回的ResourceLoader所加載的自願;
藉助getClassLoader()返回的ClassLoader加載並檢查類是否存在。

參數AnnotatedTypeMetadata的方法如下:
public abstract boolean isAnnotated(String s);//是否存在註解
public abstract Map getAnnotationAttributes(String s);//註解屬性值
public abstract Map getAnnotationAttributes(String s, boolean flag);
public abstract MultiValueMap getAllAnnotationAttributes(String s);
public abstract MultiValueMap getAllAnnotationAttributes(String s, boolean flag);

標示首選的bean

當自動裝配時,有多個bean可以選擇,可標示首選bean,標示方法如下所示:
@Primary與@Bean一起用 標示首選的bean
@Primary與@Component組合用在組件掃面的bean上
在xml的<bean>元素中配置primary=true表是首選的bean

限定自動裝配的bean

@Qualifier註解是使用限定符的主要方式。他可以與@Autowired和@Inject協同使用,在注入的時候哦指定要注入進去的是那個bean
例如我們要將IceCream注入到setDessert()之中
使用方法

@Autowired
@Qualifier("iceCream")
private Dessert dessert;

Bean組件

@Component
@Qualifier("iceCream")
public class IceCream implements Dessert{

    @Override
    public void test() {
        // TODO Auto-generated method stub
        System.out.println("IceCream.....");
    }

}

@Autowired與@Qualifier(“xxx”)一起時,@Qualifier所設置的參數就是想要注入的bean的ID,
所有使用@Component註解聲明的類都會創建爲bean,並且bean的ID爲首字母變爲小寫的類名。
因此@Qualifier(“xxx”)指向的是組件掃描時所創建的bean,並且這個bean是IceCream的實例
@Autowired與@Qualifier(“xxx”)一起時所引用的bean要具有String類型”xxx”作爲限定符。
創建bean時如果沒有指定其他的限定符的話,所有的bean都會給定一個默認的限定符,這個限定符與bean的ID相同。
我們也可以自定義bean的限定符,而不是依賴於將bean ID作爲限定符。
自定義方法是在bean聲明上添加@Qualifier(“xxx”)註解。
如上述@Qualifier(“xxx”)與@Component一起使用
@Qualifier(“xxx”)也可以與@Bean一起使用
代碼如下:

@Bean
    @Qualifier("cake")
    public Dessert getDessert(){
        return new Cake();
    }

setDessert()方法上所指定的限定符要與注入的bean的ID或bean的限定符緊密耦合。

使用自定義的限定符註解:

當我們想使用兩種限定符修飾一個類時候,我們可以在@Component修飾的bean上添加@Qualifier(“xxx1”),@Qualifier(“xxx2”)
但是這種寫法是錯誤的,Java不允許在同一條目上重複出現類型相同的多個註解,如果你試圖這樣做的話,編譯器會提示錯誤。
注:在Java8中允許出現重複的註解,只要這個註解本身定義的時候帶有@Repeatable註解就可以。不過,Spring的@Qualifier註解並沒有在定義的時候
添加@Repeatable註解
但是我們可以創建自定義的限定符註解,藉助這樣的註解來表達bean所希望限定的特性。這個所需要做的就是創建一個註解,註解的本身要使用@Qualifier註解來標註
這樣的註解就有了@Qualifier註解的特性。他們本身實際上就成爲了限定符註解
代碼如下:

@Target({
    ElementType.CONSTRUCTOR,
    ElementType.FIELD,
    ElementType.METHOD,
    ElementType.TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface cold {

}
//註解使用方式:
//掃描創建bean組件時
@Component
@cold
public class IceCream implements Dessert{

    @Override
    public void test() {
        // TODO Auto-generated method stub
        System.out.println("IceCream.....");
    }

}
//注入bean引用時
@Autowired
@cold
private Dessert dessert;
發佈了45 篇原創文章 · 獲贊 22 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章