1. 程式人生 > >第3章 高級裝配

第3章 高級裝配

scope () 會話 參數初始化 試圖 rabl 返回值 .get 計算


1、"profile bean"(Spring為那些和運行環境有關系的bean提供的配置。配置後,Spring會根據運行環境選取相應的bean)
(需要將這些bean放到profile bean中,並確保profile處於active狀態)
2、使用java配置profile bean(使用@Profile註解指定bean屬於哪個profile文件中)
例如:
//在類級別上使用profile bean
    @Configuration
    @profile("dev")
    public class 配置類{
        @Bean
        public 需要的Bean 方法名(){
            
new 需要的Bean(); } }
    (同理:可以再創建一個類指定不同環境需要裝配的bean)
//上面這個bean只有在dev profile文件被激活時才創建。如果dev 不是激活狀態則忽略創建。(根據profile是否激活,裝配不同的bean)
3、在方法級別上使用profile bean (Spring 3.2 開始可以再方法級別上使用Profile bean)
    @Configuration
    public class 配置類名{
        @Bean
        @Profile("dev")      //dev profile 裝配的bean
public 開發環境需要的Bean 方法名(){ return new 開發環境需要的Bean(); } @Bean @Profile("prod") //prod profile 裝配的bean public 運行環境需要的Bean 方法名(){ return new 運行環境需要的Bean(); } }
4、在XML中配置Profile
1:每一個xml配置文件的<beans>元素添加 profile = ""

例如:
            <beans xmlns = ""
                   ...
                   profile = "dev">
                   <bean>...<bean>
            </bean>            
    2:使用嵌套<beans>
例如:
            <beans>
                <beans profile = "dev">
                    <bean>...<bean>
                </beans>
                <beans profile = "prod">
                    <bean>...<bean>
                </beans>
            <beans>    
5、激活profile
配置profile的兩個屬性:
1:active(Spring.profiles.active):指定那個profile處於激活狀態
2:default(Spring.profiles.default):如果沒有配置active,會找default配置的值。
//如果都active和default都沒有配置:不裝配profile中的bean。
設置active和default屬性:
1:作為DispatcherServlet的參數初始化
在web的Servlet配置中加:
                <web-app ...>
                    <context-param>
                        <param-name>spring.profiles.default</param-name>
                        <param-value>dev</param-value>
                    </context-parm>
                </web-app>
        2:作為Web應用的上下文
3:作為JNDI條目
4:作為環境變量
5:作為JVM的系統屬性
6:在集成測試類上,使用@activeProfiles註解設置
6、條件化的bean
需求:希望一個bean在含有某個外部的jar是才創建,希望某個特定的bean聲明後才創建,(總之就是有條件的創建bean)
在Spring 4之前很難做到。但是Spring 4 引入了 @Conditional 註解,他可以用在帶有@Bean的註解方法上。
@Conditional:
如果給定的計算結果為true則創建bean,否則bean被忽略。
例如:
//假設有一個名為 MagicBean 的類,我們希望只有設置了環境屬性 magic 時才實例化這個bean。
1:條件話創建bean:
            @bean
            @Conditional(MagicExistsCondition.class)    //設置條件。
            public MagicBean magicBean(){
                return new MagicBean();
            }
            //傳給@Conditional的參數需要是Condition接口的實現。實現Condition接口只需要實現matches()方法,
                //matches()方法返回值就是判斷條件的結果。
        2:判斷條件:
            public class MagicExistsCondition implements Condition{
                public boolean matches(ConditionContext context , AnnotatedTypeMetadata metadata){
                    Environment env = context.getEnvironment();
                    return env.containsProperty("magic");
                }
            }
            //本例中判斷環境中是否有magic。
        註意:
上面的例子只是使用了ConditionContext獲取到了環境屬性。實際開發中獲取會根據更為負責的內容判斷(但來源基本都是matches的參數):
ConditionContext:
1:使用getRegistry()方法返回的BeanDefinitionRegistry檢查bean的定義。
2:使用getBeanFactory()方法返回的ConfigurableListtableBeanFactory()檢查bean是否存在,探測bean的屬性。
3:使用getEnvironment()方法返回的ResourceLoader加載資源。
4:使用getClassLoader()返回的ClassLoader加載並檢測類是否存在。
AnnotatedTypeMetadata:
這個接口可以檢查帶有@Bean的方法上還含有什麽註解。
1:使用isAnnotated()方法可以判斷帶有@Bean的方法是不是還有別的註解。
2:借助別的方法可以檢查方法上的其他註解及其屬性。
7、處理自動裝配的歧義性
自動裝配時:只有一個Bean匹配到所需結果時才能裝配成功,匹配到多個Bean阻礙了自動裝配。
例如:
        //需要自動裝配的bean
        @Autowired
        public void setDessert(Dessert dessert){
            this.dessert = dessert;
        }
        //Dessert 是一個接口。並且有三個類實現了該接口並且都是用@Component指定了該類作為bean:
        @Component
        public class Cake implements Dessert{...}
        @Component
        public class Cookies implements Dessert{...}
        @Component
        public class IceCream implements Dessert{...}
        //當Spring試圖給setDessert進行自動註入時:無法匹配到唯一的bean,拋出異常(NoUniqueBeanDefinitionException)。
雖然出現這種歧義性的概率非常小,當發生這種情況時我們可以通過
1:給其中一個bean設置首選。2:使用限定符將可選的bean縮小的一個。
8、標註首選的bean
在聲明bean的時候可以使用@Primary設置一個設置首選的bean,當出現自動裝配的歧義性時會選擇這個bean。
1:@Component 和 @Primary組合聲明首選:
        @Component
        @primary
        public class IceCream implements Dessert{...}
    2:Bean配置 和 Primary組合聲明首選:
2.1:使用javaConfig配置:
            @Bean
            @Primary
            public Dessert iceCream(){
                return new IceCream();
            }
        2.2:使用xml配置bean(使用XML配置bean同樣可以實現:)
            <bean id = "iceCream" class = "" primary = "true" />
        註意:問題來了:如果同時配置了兩個Primary,依然產生歧義。(無法最終鎖定到一個bean上)
9、通過限定符將bean範圍縮小。
????????????????
10、bean作用域:
Spring 應用上下文中所有的bean都是以單例的形式創建的,但是每次都調用一個實例是很不現實的(對象狀態改變)。
Spring提供了多種作用域:可以基於這些作用域創建bean:
1:單例:在整個項目中只有一個bean實例(默認)
2:原型:每次註入或者通過Spring上下文獲取的時候都會創建新的實例
3:會話:在Web應用中為每一個會話創建一個實例
4:請求:在Web應用中為每一個請求創建一個實例
11、選擇bean的作用域:
1:@Component 和 @ Scope 結合
例如:
            @Component
            @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)     //聲明作用域為 原型
            public class xxx(...)
    2:@Bean 和 @Scope 結合(在javaConfig中配置)
例如:
            @Bean
            @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
            public 需要的Bean 方法名(){
                return new 需要的Bean();
            }
       使用Xml配置bean的情況:
            <bean id = "" class = "" scope = "prototype" />
12、使用會話和請求作用域
?????????????????
13、在xml中聲明作用域代理
?????????????????
14、運行時值註入:
之前描述bean時使用了:
@Bean
public 需要的Bean 方法名(){return new 需要的Bean();}
<bean id ="" class = ""><property name = "屬性名稱" value = "一個字符串"></bean>
無論是使用什麽方法註入,其實都是將關聯的內容寫死了。這種硬編碼有時候是行不通的。
Spring提供了兩種運行時求值的方法。:
1:屬性占位符
2:Spring表達式語言
15、註入外部的值:
在Spring中處理外部值的最簡單的辦法是聲明屬性源,並且使用Spring的Environment檢索。
例如:
        @Configuration
        @PropertySource("classPath:/com/xxx/yyy/app.properties")        //聲明屬性源
        public class 配置類名{
            @Autowired
            Environment env;
            @Bean
            public 需要的Bean 方法名(){
                return new 需要的Bean(env.getProperty("需要的屬性"));    //檢索指定的屬性
            }
        }
16、Spring的Environment
Environment中的getProperty()方法有四個重載的方法:
1:String getProperty(String key)
2:String gegtProperty(String key , String defaultValue) //添加了默認值
3:T getProperty(String key , Class<T> type) //將獲取到的值轉化為T類型(例如String -> Integer)
4:T getProperty(String key , Class<T> type , T defaultValue)
如果沒有找到key也沒有指定默認值,這種情況需要報告給程序,拋出異常:
5:getRequiredProperty()
例如:
             @Configuration
             @PropertySource("classPath:/com/xxx/yyy/app.properties")        //聲明屬性源
             public class 配置類名{
                 @Autowired
                 Environment env;
                 @Bean
                 public 需要的Bean 方法名(){
                     return new 需要的Bean(env.getRequiredProperty("key"));    //如果找不到,拋出異常(IllegalStateException)
                 }
             }
    如果想要將屬性解析為類:
6:getPropertyAsClass()
例如:
            Class<需要的類> clazz = evn.getPropertyAsClass("key" , 需要的類.class);
    Environment還提供了檢索哪些profile處於active狀態(具體看書)。
17、解析屬性占位符:
1:在xml中配置:
        <bean id = ""
            class = ""
            c:_title = "${key1}"
            c:_title = "${key2}" />     //${key}將從某一個源獲取。
    2:如果使用組件掃描和自動裝配(不配置xml了):
        public BlankDisc (@Value("${key}") String title){
            this.title = title;
        }
    註意:
為了使用占位符,需要配置一個
PropertyPlaceholderConfigurer bean
或者
PropertySourcesPlaceholderConfigurer bean (推薦)
(具體看書)
18、使用Spring表達式語言
????????????????

第3章 高級裝配