zoukankan      html  css  js  c++  java
  • spring4.0之五:@Conditional在满足特定条件下,才会实例化对象

    这篇文章介绍Spring 4的@Conditional注解。

    一、在Spring的早期版本你可以通过以下方法来处理条件问题

    • 3.1之前的版本,使用Spring Expression Language(SPEL)。
    • 3.1版本有个新特性叫profile,用来解决条件问题。

    1.1、Spring Expression Language(SPEL)

    SPEL有一个三元运算符(if-then-else)可以在配置文件中当作条件语句,如下:

    <bean id="flag">  
       <constructor-arg value="#{systemProperties['system.propery.flag'] ?: false }" />  
    </bean>  
    <bean id="testBean">  
        <property name="prop" value="#{ flag ? 'yes' : 'no' }"/>  
    </bean>

     testBean的prop动态依赖于flag的值。

    1.2、使用Profile

    <!-- 如果没有设置profile,default.xml将被加载 -->  
    <!-- 必须放置在配置文件的最底下,后面再也没有bean的定义 -->  
    <beans profile="default">  
         <import resource="classpath:default.xml" />  
    </beans>  
    <!-- some other profile -->  
    <beans profile="otherProfile">  
        <import resource="classpath:other-profile.xml" />  
    </beans> 

    二、使用@Conditional

    官方文档定义:“Indicates that a component is only eligible for registration when all specified conditions match”,意思是只有满足一些列条件之后创建一个bean

    除了自己自定义Condition之外,Spring还提供了很多Condition给我们用

    @ConditionalOnClass : classpath中存在该类时起效 
    @ConditionalOnMissingClass : classpath中不存在该类时起效 
    @ConditionalOnBean : DI容器中存在该类型Bean时起效 
    @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效 
    @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效 
    @ConditionalOnExpression : SpEL表达式结果为true时 
    @ConditionalOnProperty : 参数设置或者值一致时起效 
    @ConditionalOnResource : 指定的文件存在时起效 
    @ConditionalOnJndi : 指定的JNDI存在时起效 
    @ConditionalOnJava : 指定的Java版本存在时起效 
    @ConditionalOnWebApplication : Web应用环境下起效 
    @ConditionalOnNotWebApplication : 非Web应用环境下起效

    @Conditional定义

    @Retention(RetentionPolicy.RUNTIME)  
    @Target(ElementType.TYPE, ElementType.METHOD)  
    public @interface Conditional{  
        Class<? extends Condition>[] value();
    }  

    @Conditional注解主要用在以下位置:

    • 类级别可以放在注标识有@Component(包含@Configuration)的类上
    • 作为一个meta-annotation,组成自定义注解
    • 方法级别可以放在标识由@Bean的方法上

    如果一个@Configuration的类标记了@Conditional,所有标识了@Bean的方法和@Import注解导入的相关类将遵从这些条件。

    condition接口定义如下:

    public interface Condition {
    
        /**
         * Determine if the condition matches.
         * @param context the condition context
         * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
         * or {@link org.springframework.core.type.MethodMetadata method} being checked.
         * @return {@code true} if the condition matches and the component can be registered
         * or {@code false} to veto registration.
         */
        boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    
    }

    示例1:下面看一个例子:

    package com.dxz.demo.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    public class LinuxCondition implements Condition {
    
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return context.getEnvironment().getProperty("os.name").contains("Linux");
        }
    }
    
    package com.dxz.demo.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    public class WindowsCondition implements Condition {
    
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return context.getEnvironment().getProperty("os.name").contains("Windows");
        }
    }

    我们有两个类LinuxCondition 和WindowsCondition 。两个类都实现了Condtin接口,重载的方法返回一个基于操作系统类型的布尔值。

    下面我们定义两个bean,一个符合条件另外一个不符合条件:

    package com.dxz.demo.condition;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MyConfiguration {
    
        @Bean(name = "emailerService")
        @Conditional(WindowsCondition.class)
        public EmailService windowsEmailerService() {
            return new WindowsEmailService();
        }
    
        @Bean(name = "emailerService")
        @Conditional(LinuxCondition.class)
        public EmailService linuxEmailerService() {
            return new LinuxEmailService();
        }
    }

     当符合某一个条件的时候,这里的@Bean才会被初始化。 

    测试相关其它类:

    package com.dxz.demo.condition;
    
    public interface EmailService {
    
        public void sendEmail();
    }
    
    package com.dxz.demo.condition;
    
    public class WindowsEmailService implements EmailService {
    
        public void sendEmail() {
            System.out.println("send windows email");
        }
    
    }
    
    package com.dxz.demo.condition;
    
    public class LinuxEmailService implements EmailService {
    
        public void sendEmail() {
            System.out.println("send linux email");
    
        }
    
    }
    
    
    package com.dxz.demo.condition;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;
    
    @Service
    public class ConditionClient {
    
        @Autowired
        private EmailService emailService;
    
        @Scheduled(initialDelay = 3000, fixedDelay = 10000)
        public void test() {
            emailService.sendEmail();
        }
    }

    结果:

     示例2:@ConditionalOnProperty来控制Configuration是否生效

    Spring Boot通过@ConditionalOnProperty来控制Configuration是否生效

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Documented
    @Conditional(OnPropertyCondition.class)
    public @interface ConditionalOnProperty {
    
        String[] value() default {}; //数组,获取对应property名称的值,与name不可同时使用  
      
        String prefix() default "";//property名称的前缀,可有可无  
      
        String[] name() default {};//数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用  
      
        String havingValue() default "";//可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置  
      
        boolean matchIfMissing() default false;//缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错  
      
        boolean relaxedNames() default true;//是否可以松散匹配,至今不知道怎么使用的  
    } 
    }

    通过其两个属性name以及havingValue来实现的,其中name用来从application.properties中读取某个属性值。
    如果该值为空,则返回false;
    如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。
    如果返回值为false,则该configuration不生效;为true则生效。

    @Configuration
    //在application.properties配置"mf.assert",对应的值为true
    @ConditionalOnProperty(prefix="mf",name = "assert", havingValue = "true")
    public class AssertConfig {
        @Autowired
        private HelloServiceProperties helloServiceProperties;
        @Bean
        public HelloService helloService(){
            HelloService helloService = new HelloService();
            helloService.setMsg(helloServiceProperties.getMsg());
            return helloService;
        }
    }

    s

  • 相关阅读:
    PAT 甲级 1126 Eulerian Path (25 分)
    PAT 甲级 1126 Eulerian Path (25 分)
    PAT 甲级 1125 Chain the Ropes (25 分)
    PAT 甲级 1125 Chain the Ropes (25 分)
    PAT 甲级 1124 Raffle for Weibo Followers (20 分)
    PAT 甲级 1124 Raffle for Weibo Followers (20 分)
    PAT 甲级 1131 Subway Map (30 分)
    PAT 甲级 1131 Subway Map (30 分)
    AcWing 906. 区间分组 区间贪心
    AcWing 907. 区间覆盖 区间贪心
  • 原文地址:https://www.cnblogs.com/duanxz/p/7489046.html
Copyright © 2011-2022 走看看