• 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

  • 相关阅读:
    linux修改键盘按键
    linux添加一个已经存在用户到一个用户组
    centos-6更新yum源(163)
    Fedora 19安装以后的优化
    centos永久性修改系统时间显示格式
    smb.conf文件详解
    Centos上部署文件共享
    centos上mysql开启远程访问
    centos安装mysql后默认密码修改
    centos上mysql的一种安装方式
  • 原文地址:https://www.cnblogs.com/duanxz/p/7489046.html
Copyright © 2020-2023  润新知