美文网首页
高级装配

高级装配

作者: TheMrBigHead | 来源:发表于2018-09-05 08:28 被阅读0次

    3.1 环境和配置文件

    实际情况中,不同的环境中所应使用的配置是不相同的。

    3.1.1 配置配置文件Bean

    是在runtime时生效的。

    1. 使用Java配置:

      使用@Profile注解

      @Configuration
      @Profile("dev")
      public class DevelopmentProfileConfig {
      @Bean(destroyMethod="shutdown")
          public DataSource dataSource() {
            return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("classpath:schema.sql")
            .addScript("classpath:test-data.sql")
            .build();
      } }
      
      @Configuration
      @Profile("prod")
      public class ProductionProfileConfig {
        @Bean
        public DataSource dataSource() {
          JndiObjectFactoryBean jndiObjectFactoryBean =
              new JndiObjectFactoryBean();
          jndiObjectFactoryBean.setJndiName("jdbc/myDS");
          jndiObjectFactoryBean.setResourceRef(true);
          jndiObjectFactoryBean.setProxyInterface(
              javax.sql.DataSource.class);
          return (DataSource) jndiObjectFactoryBean.getObject();
      } }
      

      从Spring 3.2开始,@Profile注解开始支持方法级别

      @Configuration
      public class DataSourceConfig {
        @Bean(destroyMethod="shutdown")
        @Profile("dev")
        public DataSource embeddedDataSource() {
            return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("classpath:schema.sql")
            .addScript("classpath:test-data.sql")
            .build();
      }
      
        @Bean
        @Profile("prod")
        public DataSource jndiDataSource() {
          JndiObjectFactoryBean jndiObjectFactoryBean =
              new JndiObjectFactoryBean();
          jndiObjectFactoryBean.setJndiName("jdbc/myDS");
          jndiObjectFactoryBean.setResourceRef(true);     
          jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);     
          return (DataSource) jndiObjectFactoryBean.getObject();
      } }
      
    2. 通过xml配置文件配置

      使用profile属性

      <?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:jdbc="http://www.springframework.org/schema/jdbc"
        xsi:schemaLocation="
          http://www.springframework.org/schema/jdbc
          http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd"
         profile="dev">
        <jdbc:embedded-database id="dataSource">
          <jdbc:script location="classpath:schema.sql" />
          <jdbc:script location="classpath:test-data.sql" />
        </jdbc:embedded-database>
      </beans>
      

      在beans标签中使用profile

      <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>
      
      
      <beans profile="prod">
         <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase"
              resource-ref="true" proxy-interface="javax.sql.DataSource" />
      </beans>
      

    3.1.2 激活配置文件

    两个配置:
    1. spring.profiles.active

    2. spring.profiles.default

       spring.profiles.active的值会决定使用哪个环境的配置文件
      
       如果spring.profiles.active没有设置值,那么就会采用spring.profiles.default的值
      
       如果这两个属性都没有设置值,那么在配置环境中定义的Bean将不会被创建
      
    配置方法:

    (1) 在DispatcherServlet中作为初始化参数
    (2) 作为web应用的context参数
    (3) 作为JNDI的入口参数
    (4) 作为环境变量
    (5) 作为JVM系统属性
    (6) 在集成单元测试中使用@ActiveProfiles注解

    <context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
      </context-param>
    
    <listener>
        <listener-class>
          org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>
          org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
          <param-name>spring.profiles.default</param-name>
          <param-value>dev</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    

    3.2 有条件的Bean

    Spring 4引入了@Conditional注解

    @Bean
    @Conditional(MagicExistsCondition.class)
    public MagicBean magicBean() {
      return new MagicBean();
    }
    
    public interface Condition {
        boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
    }
    

    传入到@Conditional的参数可以是任何实现了Condition接口的实现类。

    public class MagicExistsCondition implements Condition {
      public boolean matches(
              ConditionContext context, AnnotatedTypeMetadata metadata) {
            Environment env = context.getEnvironment();
            return env.containsProperty("magic"); 
        }
    }
    

    深入探究一下Condition接口:

    1. ConditionContext
    public interface ConditionContext {
      BeanDefinitionRegistry getRegistry();
      ConfigurableListableBeanFactory getBeanFactory();
      Environment getEnvironment();
      ResourceLoader getResourceLoader();
      ClassLoader getClassLoader();
    }
    
    1. AnnotatedTypeMetadata
    public interface AnnotatedTypeMetadata {
      boolean isAnnotated(String annotationType);
      Map<String, Object> getAnnotationAttributes(String annotationType);
      Map<String, Object> getAnnotationAttributes(
              String annotationType, boolean classValuesAsString);
      MultiValueMap<String, Object> getAllAnnotationAttributes(
              String annotationType);
      MultiValueMap<String, Object> getAllAnnotationAttributes(
              String annotationType, boolean classValuesAsString);
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Documented
    @Conditional(ProfileCondition.class)
    public @interface Profile {
      String[] value();
    }
    
    class ProfileCondition implements Condition {
        public boolean matches(
                    ConditionContext context, AnnotatedTypeMetadata metadata) {
            if (context.getEnvironment() != null) {
                MultiValueMap<String, Object> attrs =
                      metadata.getAllAnnotationAttributes(Profile.class.getName());
                    if (attrs != null) {
                        for (Object value : attrs.get("value")) {
                          if (context.getEnvironment()
                                 .acceptsProfiles(((String[]) value))) {
                              return true;
                          }
                        }
                        return false;
                    }
                }
             return true;
        }
    }
    

    3.3 在自动装配中解决有歧义的Bean

     @Autowired
     public void setDessert(Dessert dessert) {
        this.dessert = dessert;
     }
    
    @Component
    public class Cake implements Dessert { ... }
            
    @Component
    public class Cookies implements Dessert { ... }
    
    @Component
    public class IceCream implements Dessert { ... }
    

    然后就报错了NoUniqueBeanDefinitionException:

    nested exception is
    org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
    No qualifying bean of type [com.desserteater.Dessert] is defined:
    expected single matching bean but found 3: cake,cookies,iceCream
    

    3.3.1 定义一个Primary Bean

    1. 使用@Primary注解
    @Component
    @Primary
    public class IceCream implements Dessert { ... }  
    

    @Bean
    @Primary
    public Dessert iceCream() {
        return new IceCream();
    }
    
    1. 在xml中使用primary属性
          <bean id="iceCream"
                class="com.desserteater.IceCream"
                primary="true" />
    

    3.3.2 使用@Qualifier注解

    1. 和@Autowired(或@Inject)一起使用来注入Bean
    @Autowired
    @Qualifier("iceCream")
    public void setDessert(Dessert dessert) {
        this.dessert = dessert;
    }
    
    1. 和@Component(或@Bean)使用来声明Bean
    @Component
    @Qualifier("cold")
    public class IceCream implements Dessert { ... }
    
    1. 自定义注解

    JAVA不允许同一种注解在一个地方多次使用

    // 这个是会报错的
    
    @Component
    @Qualifier("cold")
    @Qualifier("creamy")
    public class IceCream implements Dessert { ... }
    
    @Autowired
    @Qualifier("cold")
    @Qualifier("creamy")
    public void setDessert(Dessert dessert) {
        this.dessert = dessert;
    }
    

    于是我们开始搞自定义注解了

    // 第一个注解
    @Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
                     ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface Cold { }
    
    // 第二个注解
    
    @Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
             ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface Creamy { }
    
    @Component
    @Cold
    @Creamy
    public class IceCream implements Dessert { ... }
    
    @Autowired
    @Cold
    @Creamy
    public void setDessert(Dessert dessert) {
        this.dessert = dessert;
    }
    

    相关文章

      网友评论

          本文标题:高级装配

          本文链接:https://www.haomeiwen.com/subject/xlyowftx.html