Spring高階裝配(一) profile Spring高階裝配(一) profile
Spring高階裝配(一) profile
Spring高階裝配要學習的內容包括:
- Spring profile
- 條件化的bean宣告
- 自動裝配與歧義性
- bean的作用域
- Spring表示式語言
以上屬於高階一點的bean裝配技術,如果你沒有啥特別的需求的話用的還比較少。但是用於解決變態一點的需求還是要學一下留個備份。
環境與Profile
直接上情形吧,一個專案現在有三個階段,不同階段使用的dataSource的來源不一樣,分別是:
- 開發階段:使用嵌入式的Hypersonic資料庫
- QA階段:使用不同DataSource配置,比如Common DBCP連線池
- 生產階段:從JNDI容器中獲取一個DataSource
這三種DataSource bean的生成程式碼分別是:
嵌入式的Hypersonic資料庫:
1 @Bean(destroyMethod="shutdown") 2 public DataSource dataSource() { 3 return new EmbeddedDataSourceBuilder() 4 .addScript("classpath:schema.sql")5 .addScript("classpath:test-data.sql") 6 .build(); 7 }
JNDI:
1 @Bean 2 public DataSource dataSource() { 3 JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); 4 jndiObjectFactoryBean.setJndiName("jdbc/myDS");5 jndiObjectFactoryBean.setResourceRef(true); 6 jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); 7 return (DataSource) jndiObjectFactoryBean.getObject(); 8 }
Common DBCP:
1 @Bean(destroyMethod="close") 2 public DataSource dataSource() { 3 BasicDataSource dataSource = new BasicDataSource(); 4 dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test"); 5 dataSource.setDriverClassName("org.h2.Driver"); 6 dataSource.setUserName("sa"); 7 dataSource.setPassword("password"); 8 dataSource.setInitialSize(20); 9 dataSource.setMaxActive(30); 10 return dataSource; 11 }
也就是說每個階段都是用了完全不同的策略來生成DataSource的bean。現在有一個需求是:如何優雅地切換這三種DataSource?
如果只用到基礎的Spring bean的裝配知識的話,我們必須每次手動的加上要轉入的階段對應的DataSource bean定義程式碼。這樣的話容易引入bug,而且不優雅。這種情況其實可以抽象一下:根據不同的情況,生成不同的bean。
Spring針對這種根據環境來決定建立哪個bean和不建立哪個bean提供了了一種解決方案:profile。profile使用的大致流程:
配置profile bean
Spring利用profile來感覺環境決定建立哪個bean和不建立哪個bean,並不是在構建的時候做出決策,而是在執行時再決定。這樣的話程式碼就可以適用於所有的環境,而不是需要額外重構。
在使用profile的時候(since 3.1),首先要把不同的bean定義整理到一個或者多個profile中,在將應用部署到每個環境時,要確保對應的profile處於啟用(active)狀態。
在Java配置中使用@Profile指定某個bean屬於哪個profile。先來一個直接一點的例子:
1 @Configuration 2 @Profile("dev") 3 public class DevelopmentProfileConfig { 4 5 @Bean(destroyMethod="shutdown") 6 public DataSource dataSource() { 7 return new EmbeddedDataSourceBuilder() 8 .addScript("classpath:schema.sql") 9 .addScript("classpath:test-data.sql") 10 .build(); 11 } 12 }
解釋說明:
- @Profile應用在了類級別上
- 這個配置類中的bean只有在dev profile被啟用的時候才會被建立。
- 如果dev profile沒有被啟用,那麼帶有@Bean註解的方法都會被忽略。
在給出一個適用於生產環境的配置:
1 @Configuration 2 @Profile("prod") 3 public class ProductionProfileConfig { 4 @Bean 5 public DataSource dataSource() { 6 JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); 7 jndiObjectFactoryBean.setJndiName("jdbc/myDS"); 8 jndiObjectFactoryBean.setResourceRef(true); 9 jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); 10 return (DataSource) jndiObjectFactoryBean.getObject(); 11 } 12 }
在Spring 3.1中只能在類級別上使用@Profile註解,3.2開始,也可以在方法級別上使用@Profile註解,與@Bean註解一同使用;這樣的話可以把這兩個bean的宣告放到同一個配置類中:
1 @Configuration 2 public class DataSourceConfig { 3 @Bean(destroyMethod="shutdown") 4 @Profile("dev") 5 public DataSource dataSource() { 6 return new EmbeddedDataSourceBuilder() 7 .addScript("classpath:schema.sql") 8 .addScript("classpath:test-data.sql") 9 .build(); 10 } 11 12 @Bean 13 @Profile("prod") 14 public DataSource dataSource() { 15 JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); 16 jndiObjectFactoryBean.setJndiName("jdbc/myDS"); 17 jndiObjectFactoryBean.setResourceRef(true); 18 jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); 19 return (DataSource) jndiObjectFactoryBean.getObject(); 20 } 21 }
這樣一來每個DataSource的bean都被宣告在配置類中,並且只有當規定的profile啟用時,相應的bean才會被建立;沒有指定profile的bean始終都會被建立,與啟用哪個profile沒有關係。
在XML中配置profile
通過<beans>元素的profile屬性,在XML中配置profile bean:
1 <beans profile="dev"> 2 <jdbc:embedded-database id="dataSource"> 3 <jdbc:script location="classpath:schema.sql" /> 4 <jdbc:script location="classpath:test-data.sql" /> 5 </jdbc:embedded-database> 6 </beans>
同理,可以通過把profile設定為prod,建立適用於生產環境的從JNDI獲取的DataSource bean;也可以建立基於連線池定義的dataSource bean,將其放在另一個XML檔案中,並標註為qa profile。所有的配置檔案都會放在部署單元之中(如WAR檔案),但是隻有profile屬性與當前啟用的profile相匹配的配置檔案才會被用到。
如果覺得定義的配置檔案太多,你可以在根<beans>中巢狀定義<beans>元素,而是不是為每個環境建立一個profile XML檔案,配置程式碼如下:
1 <beans> 2 3 <beans profile="dev"> 4 <jdbc:embedded-database id="dataSource"> 5 <jdbc:script location="classpath:schema.sql" /> 6 <jdbc:script location="classpath:test-data.sql" /> 7 </jdbc:embedded-database> 8 </beans> 9 10 <beans profile="qa"> 11 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" 12 p:url="jdbc:h2:tcp://dbserver/~/test" 13 p:driverClassName="org.h2.Driver" 14 p:username="sa" 15 p:password="password" 16 p:initialSize="20" 17 p:maxActive="30" /> 18 </beans> 19 20 <beans profile="prod"> 21 <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase" 22 resource-ref="true" proxy-interface="javax.sql.DataSource" /> 23 </beans> 24 25 </beans>
啟用profle
把profile配置好了之後,問題是怎麼啟用這些profile?
Spring在確定哪個profile處於啟用狀態時,需要依賴兩個獨立的屬性:
- spring.profiles.active
- spring.profiles.default
如果設定了spring.profiles.active屬性的話,那麼它的值就會用來確定哪個profile是啟用的。
如果沒有設定spring.profiles.active屬性的話,那Spring將會查詢spring.profiles.default的值。
如果active和default都沒有設定,那麼就沒有啟用的profile,因此只會啟用那些沒有定義在profile中的bean。
設定啟用屬性的方法:
- 作為DispatcherServlet的初始化引數
- 作為Web應用的上下文引數
- 作為JNDI條目
- 作為環境變數
- 作為JVM的系統屬性
- 在整合測試類上,使用@ActiveProfiles註解設定
推薦的方式使用時DispatcherServlet的引數將spring.profiles.default設定為開發環境的profile,會在Servlet上下文中進行設定,在web.xml中:
1 <web-app> 2 3 <!-- 為上下文設定預設的profile --> 4 <context-param> 5 <param-name>spring.profiles.default</param-name> 6 <param-value>dev</param-value> 7 </context-param> 8 9 <!-- 為Servlet設定預設的profile --> 10 <servlet> 11 <servlet-name>appServlet</servlet-name> 12 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 13 <init-param> 14 <param-name>spring.profiles.default</param-name> 15 <param-value>dev</param-value> 16 </init-param> 17 <load-on-startup>1</load-on-startup> 18 </web-app>
可以通過列出多個profile名稱並以逗號分隔來同時啟用多個profile。不過同時啟用dev和prod可能沒有太大的意義,但是可以同時設定多個彼此不相關的profile。
整合測試時使用@ActiveProfiles註解來指定測試時要啟用的profile。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes={PersistenceTestConfig.class}) @ActiveProfiles("dev") public class PersistenceTest { }
Spring的profile提供了一種很好的條件化建立bean的方法,這裡的條件是基於哪個profile處於啟用狀態來判斷。Spring 4.0提供了一種更為通用的機制來實現條件化的bean定義。
分類: Spring 標籤: spring, profile, 條件化, bean 好文要頂 關注我 收藏該文 tuhooo關注 - 1
粉絲 - 50 +加關注 0 0 « 上一篇: mysql的騷操作:自增長的欄位同時插入到另一個欄位
» 下一篇: Spring高階裝配(二) 條件化的bean
posted @ 2018-06-29 17:25 tuhooo 閱讀( 29) 評論( 0) 編輯 收藏
Spring高階裝配要學習的內容包括:
- Spring profile
- 條件化的bean宣告
- 自動裝配與歧義性
- bean的作用域
- Spring表示式語言
以上屬於高階一點的bean裝配技術,如果你沒有啥特別的需求的話用的還比較少。但是用於解決變態一點的需求還是要學一下留個備份。
環境與Profile
直接上情形吧,一個專案現在有三個階段,不同階段使用的dataSource的來源不一樣,分別是:
- 開發階段:使用嵌入式的Hypersonic資料庫
- QA階段:使用不同DataSource配置,比如Common DBCP連線池
- 生產階段:從JNDI容器中獲取一個DataSource
這三種DataSource bean的生成程式碼分別是:
嵌入式的Hypersonic資料庫:
1 @Bean(destroyMethod="shutdown") 2 public DataSource dataSource() { 3 return new EmbeddedDataSourceBuilder() 4 .addScript("classpath:schema.sql") 5 .addScript("classpath:test-data.sql") 6 .build(); 7 }
JNDI:
1 @Bean 2 public DataSource dataSource() { 3 JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); 4 jndiObjectFactoryBean.setJndiName("jdbc/myDS"); 5 jndiObjectFactoryBean.setResourceRef(true); 6 jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); 7 return (DataSource) jndiObjectFactoryBean.getObject(); 8 }
Common DBCP:
1 @Bean(destroyMethod="close") 2 public DataSource dataSource() { 3 BasicDataSource dataSource = new BasicDataSource(); 4 dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test"); 5 dataSource.setDriverClassName("org.h2.Driver"); 6 dataSource.setUserName("sa"); 7 dataSource.setPassword("password"); 8 dataSource.setInitialSize(20); 9 dataSource.setMaxActive(30); 10 return dataSource; 11 }
也就是說每個階段都是用了完全不同的策略來生成DataSource的bean。現在有一個需求是:如何優雅地切換這三種DataSource?
如果只用到基礎的Spring bean的裝配知識的話,我們必須每次手動的加上要轉入的階段對應的DataSource bean定義程式碼。這樣的話容易引入bug,而且不優雅。這種情況其實可以抽象一下:根據不同的情況,生成不同的bean。
Spring針對這種根據環境來決定建立哪個bean和不建立哪個bean提供了了一種解決方案:profile。profile使用的大致流程:
配置profile bean
Spring利用profile來感覺環境決定建立哪個bean和不建立哪個bean,並不是在構建的時候做出決策,而是在執行時再決定。這樣的話程式碼就可以適用於所有的環境,而不是需要額外重構。
在使用profile的時候(since 3.1),首先要把不同的bean定義整理到一個或者多個profile中,在將應用部署到每個環境時,要確保對應的profile處於啟用(active)狀態。
在Java配置中使用@Profile指定某個bean屬於哪個profile。先來一個直接一點的例子:
1 @Configuration 2 @Profile("dev") 3 public class DevelopmentProfileConfig { 4 5 @Bean(destroyMethod="shutdown") 6 public DataSource dataSource() { 7 return new EmbeddedDataSourceBuilder() 8 .addScript("classpath:schema.sql") 9 .addScript("classpath:test-data.sql") 10 .build(); 11 } 12 }
解釋說明:
- @Profile應用在了類級別上
- 這個配置類中的bean只有在dev profile被啟用的時候才會被建立。
- 如果dev profile沒有被啟用,那麼帶有@Bean註解的方法都會被忽略。
在給出一個適用於生產環境的配置:
1 @Configuration 2 @Profile("prod") 3 public class ProductionProfileConfig { 4 @Bean 5 public DataSource dataSource() { 6 JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); 7 jndiObjectFactoryBean.setJndiName("jdbc/myDS"); 8 jndiObjectFactoryBean.setResourceRef(true); 9 jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); 10 return (DataSource) jndiObjectFactoryBean.getObject(); 11 } 12 }
在Spring 3.1中只能在類級別上使用@Profile註解,3.2開始,也可以在方法級別上使用@Profile註解,與@Bean註解一同使用;這樣的話可以把這兩個bean的宣告放到同一個配置類中:
1 @Configuration 2 public class DataSourceConfig { 3 @Bean(destroyMethod="shutdown") 4 @Profile("dev") 5 public DataSource dataSource() { 6 return new EmbeddedDataSourceBuilder() 7 .addScript("classpath:schema.sql") 8 .addScript("classpath:test-data.sql") 9 .build(); 10 } 11 12 @Bean 13 @Profile("prod") 14 public DataSource dataSource() { 15 JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); 16 jndiObjectFactoryBean.setJndiName("jdbc/myDS"); 17 jndiObjectFactoryBean.setResourceRef(true); 18 jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); 19 return (DataSource) jndiObjectFactoryBean.getObject(); 20 } 21 }
這樣一來每個DataSource的bean都被宣告在配置類中,並且只有當規定的profile啟用時,相應的bean才會被建立;沒有指定profile的bean始終都會被建立,與啟用哪個profile沒有關係。
在XML中配置profile
通過<beans>元素的profile屬性,在XML中配置profile bean:
1 <beans profile="dev"> 2 <jdbc:embedded-database id="dataSource"> 3 <jdbc:script location="classpath:schema.sql" /> 4 <jdbc:script location="classpath:test-data.sql" /> 5 </jdbc:embedded-database> 6 </beans>
同理,可以通過把profile設定為prod,建立適用於生產環境的從JNDI獲取的DataSource bean;也可以建立基於連線池定義的dataSource bean,將其放在另一個XML檔案中,並標註為qa profile。所有的配置檔案都會放在部署單元之中(如WAR檔案),但是隻有profile屬性與當前啟用的profile相匹配的配置檔案才會被用到。
如果覺得定義的配置檔案太多,你可以在根<beans>中巢狀定義<beans>元素,而是不是為每個環境建立一個profile XML檔案,配置程式碼如下:
1 <beans> 2 3 <beans profile="dev"> 4 <jdbc:embedded-database id="dataSource"> 5 <jdbc:script location="classpath:schema.sql" /> 6 <jdbc:script location="classpath:test-data.sql" /> 7 </jdbc:embedded-database> 8 </beans> 9 10 <beans profile="qa"> 11 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" 12 p:url="jdbc:h2:tcp://dbserver/~/test" 13 p:driverClassName="org.h2.Driver" 14 p:username="sa" 15 p:password="password" 16 p:initialSize="20" 17 p:maxActive="30" /> 18 </beans> 19 20 <beans profile="prod"> 21 <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase" 22 resource-ref="true" proxy-interface="javax.sql.DataSource" /> 23 </beans> 24 25 </beans>
啟用profle
把profile配置好了之後,問題是怎麼啟用這些profile?
Spring在確定哪個profile處於啟用狀態時,需要依賴兩個獨立的屬性:
- spring.profiles.active
- spring.profiles.default
如果設定了spring.profiles.active屬性的話,那麼它的值就會用來確定哪個profile是啟用的。
如果沒有設定spring.profiles.active屬性的話,那Spring將會查詢spring.profiles.default的值。
如果active和default都沒有設定,那麼就沒有啟用的profile,因此只會啟用那些沒有定義在profile中的bean。
設定啟用屬性的方法:
- 作為DispatcherServlet的初始化引數
- 作為Web應用的上下文引數
- 作為JNDI條目
- 作為環境變數
- 作為JVM的系統屬性
- 在整合測試類上,使用@ActiveProfiles註解設定
推薦的方式使用時DispatcherServlet的引數將spring.profiles.default設定為開發環境的profile,會在Servlet上下文中進行設定,在web.xml中:
1 <web-app> 2 3 <!-- 為上下文設定預設的profile --> 4 <context-param> 5 <param-name>spring.profiles.default</param-name> 6 <param-value>dev</param-value> 7 </context-param> 8 9 <!-- 為Servlet設定預設的profile --> 10 <servlet> 11 <servlet-name>appServlet</servlet-name> 12 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 13 <init-param> 14 <param-name>spring.profiles.default</param-name> 15 <param-value>dev</param-value> 16 </init-param> 17 <load-on-startup>1</load-on-startup> 18 </web-app>
可以通過列出多個profile名稱並以逗號分隔來同時啟用多個profile。不過同時啟用dev和prod可能沒有太大的意義,但是可以同時設定多個彼此不相關的profile。
整合測試時使用@ActiveProfiles註解來指定測試時要啟用的profile。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes={PersistenceTestConfig.class}) @ActiveProfiles("dev") public class PersistenceTest { }
Spring的profile提供了一種很好的條件化建立bean的方法,這裡的條件是基於哪個profile處於啟用狀態來判斷。Spring 4.0提供了一種更為通用的機制來實現條件化的bean定義。