1. 程式人生 > >三、spring中高級裝配(1)

三、spring中高級裝配(1)

bool 引用 idata apache object 基於 environ 變量 java

  大概看了一下第三章的內容,我從項目中仔細尋找,始終沒有發現哪裏有這種配置,但是看完覺得spring還有這麽牛B的功能啊,spring的厲害之處,這種設計程序的思想,很讓我感慨。。。

一、環境與profile

(1)配置profile bean

面對這樣的需求:想出一種方法來配置DataSource,使其在每種環境下都會選擇最為合適的配置,你會如何做呢?看看spring所提供的解決方案。spring中引入了bean profile的功能。在Java配置中,可以使用@Profile 註解指定某個bean屬於哪一個profile。

1)在Java類中配置profile bean

package
com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
//@Profile註解應用在累計別上。它會告訴spring這個配置類中的bean只有在dev profile 激活時才會創建,沒有激活,則不會創建 @Configuration @Profile("dev") public class DevelopmentProfileConfig{   return new   EmbeddDatabaseBuilder().setType(EmbeddedDatebaseType.H2).   addScript("classpath:schema.sql").addScript("classpath:test-   data.sql").build(); }
package com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jndi.JndiObjectFactoryBean; //@Profile註解應用在累計別上。它會告訴spring這個配置類中的bean只有在prod profile 激活時才會創建,沒有激活,則不會創建 @Configuration @Profile("prod") public class ProductiontProfileConfig{   JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();   jndiObjectFactoryBean.setJndiName("jdbc/myDS");   jndiObjectFactoryBean.setResourceRef(true);   jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);   return (DataSource) jndiObjectFactoryBean.getObject(); } //可以將這兩個bean的聲明放置在同一個配置類中 package com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.jndi.JndiObjectFactoryBean; @Configuration public class DateSourceConfig{ //為dev profile裝配bean public DataSource embeddedDataSource(){ return new   EmbeddDatabaseBuilder().setType(EmbeddedDatebaseType.H2).   addScript("classpath:schema.sql").addScript("classpath:test-   data.sql").build(); } //為prod profile 裝配bean public DataSource jbdiDataSource(){ JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();   jndiObjectFactoryBean.setJndiName("jdbc/myDS");   jndiObjectFactoryBean.setResourceRef(true);   jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);   return (DataSource) jndiObjectFactoryBean.getObject(); } }

2)在XML中配置profile bean

//xml中配置的實際效果和在Java類中配置是一樣的

//dev profile 的bean
<beans profile="dev">
    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:schema.sql" />
        <jdbc:script location="classpath:test-data.sql" />
    </jdbc:embedded-database>
</beans>

//qa profile 的bean
<beans profile="qa">
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destory-method="close" p:url="jdbc:h2:tcp://deserver/~/test" p:driverClassName="org.h2.Driver" p:username="sa" p:password="password" p:initialSize="20" p:maxActive="30">
    </bean>
</beans>

//prod profile 的bean
<beans>
    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDataSource" resource-ref="true" proxy-interfase="javax.sql.DataSource" />
</beans>

2)激活 profile

profile bean 配置OK了,如何來激活配置的bean呢?依賴兩個獨立的屬性:spring.profiles.active 和 spring.profiles.default。設置了spring.profiles.active屬性的話,那麽它的值就會來確定哪個profile是激活的,如果設置了spring.profiles.default屬性,那麽spring 會查找spring.profiles.default的值,如果兩個屬性都設置的話,肯定會優先spring.profiles.active屬性對應的profile bean 裝配

多種方式設置這兩個屬性:

1))作為DispatcherServlet的初始化參數

2))作為web應用的上下文參數

3))作為JNDI條目

4))作為環境變量

5))作為JVM的系統屬性

6))在集成測試上,使用@ActiveProfiles註解設置

web.xml 中設置默認的profile

//為上下文設置默認的profile
<context-param>
    <param-name>spring.profile.default</param-name>
    <param-value>dev</param-value>
</context-param>

//為Servlet設置默認的profile 可以列出多個profile名稱,並以逗號分隔來實現
<servlet>
    <servlet-name>app-servlet</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
    </init-param>
</servlet>

二、條件化的bean

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

//條件化的創建bean
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean(){
  return new MagicBean();              
}

//@Conditional 將會通過Condition 接口進行條件對比
public interface Condition{
  public boolean matches(ConditionContext ctcx,AnnotatedTypeMetadata metadata);  
}

//在Contidion 中檢查是否存在magic屬性,滿足這個條件,matches()方法返回true,則所有的@Conditional 註解上引用MagicExistsCondition的bean都會被創建
public class MagicExistsCondition implements Condition{
  public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata){
    Environment env = context.getEnvironment();
    return env.containsProperty("magic");                       
  }  
}

註:這裏講解了兩個比較特殊的接口,一個是ConditionContext,一個是AnnotatedTypeMetadata,做一個說明:

public interface ConditionContext{
  //檢查bean的定義  
  BeanDefinitonRegistry getRegistry();  
  //檢查bean是否存在,甚至探查bean的屬性
  ConfigurableListableBeanFactory getBeanFactory();
  //檢查環境變量是否存在以及它的值是什麽
  Environment getEnvironment();
  //返回ResourceLoader所加載的資源
  ResourceLoader getResourceLoader();
  //返回ClassLoader加載並檢查類是否存在
  ClassLoader getClassLoader();              
}

//檢查帶有@Bean註解的方法上還有什麽其他的註解
public interface AnnotatedTypeMetadata{
  boolean isAnnotated(String annotationType);
  Map<String,Object> getAnnotationAttributes(String annotationType);      
  Map<String,Object> getAnnotationAttributes(String annotationType,boolean classValueAsString);
  MultiValueMap<String,Object> getAnnotationAttributes(String annotationType);
   MultiValueMap<String,Object> getAnnotationAttributes(String annotationType,boolean classValueAsString);
}

註:spring4 中對@Profile註解進行了重構,使其基於@Conditional 和 Condition實現,@Profile在spring4 中的實現:

/*
@Profile本身也使用了@Conditional註解,並且引用了ProfileCondition作為Condition實現,ProfileCondition在做出決策的過程中,考慮到了ConditionContext和AnnotatedTypeMetadata中多個因素
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Document
@Conditional(ProfileCondition.class)
public @interface Profile{
  String[] value();  
}

三、spring中高級裝配(1)