淘先锋技术网

首页 1 2 3 4 5 6 7

自定义注解

有时我们想根据自己的业务需求自定义条件注解,需要使用@Conditional + Condition

自定义条件类:

package com.lin.missyou.sample.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class DianaCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String name = context.getEnvironment().getProperty("hero.condition");
        return "diana".equalsIgnoreCase(name);
    }
}
package com.lin.missyou.sample.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class IreliaCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String name = context.getEnvironment().getProperty("hero.condition");
        return "irelia".equalsIgnoreCase(name);
    }
}

接口:

public interface ISkill {
    void r();
}

实现类:

package com.lin.missyou.sample.hero;

import com.lin.missyou.sample.ISkill;

public class Diana implements ISkill {
    public Diana() {	System.out.println("Hello Diana");	}
    public void r(){    System.out.println("Diana R");	}
}
package com.lin.missyou.sample.hero;

import com.lin.missyou.sample.ISkill;

public class Irelia implements ISkill {
    public Irelia() {    System.out.println("Hello Irelia");	}
    public void r(){    System.out.println("Irelia R");	}
}

配置类:

package com.lin.missyou.sample;

import com.lin.missyou.sample.condition.DianaCondition;
import com.lin.missyou.sample.condition.IreliaCondition;
import com.lin.missyou.sample.hero.Diana;
import com.lin.missyou.sample.hero.Irelia;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HeroConfiguration {

    @Bean
    @Conditional(DianaCondition.class)
    public ISkill diana(){
        return new Diana();
    }
    
    @Bean
    @Conditional(IreliaCondition.class)
    public ISkill irelia(){
        return new Irelia();
    }
}

控制器:

package com.lin.missyou.api.v1;

import com.lin.missyou.sample.IConnected;
import com.lin.missyou.sample.ISkill;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/v1/banner")
public class BannerController {

    @Autowired
    private ISkill iSkill;

    @GetMapping("/test")
    public String test1() {
        iSkill.r();
        return "Hello test1";
    }
}

当配置文件如下,启动应用,控制台打印出‘Hello Diana’,说明Diana这个类被加入到容器。

hero.condition = diana

在这里插入图片描述
访问接口,控制台打印出‘Diana R’
在这里插入图片描述
当配置文件中的值改为‘irelia’,启动应用,控制台打印出‘Hello Irelia’,说明Irelia这个类被加入到容器。

hero.condition = irelia

在这里插入图片描述
访问接口,控制台打印出‘Irelia R’
在这里插入图片描述

内置的成品条件注解

注解条件
@ConditionalOnBean当SpringIoC容器内存在指定Bean的条件
@ConditionalOnMissingBean当SpringIoC容器内不存在指定Bean的条件
@ConditionalOnClass当SpringIoC容器内存在指定Class的条件
@ConditionalOnMissingClass当SpringIoC容器内不存在指定Class的条件
@ConditionalOnProperty指定的属性是否有指定的值
@ConditionalOnResource类路径是否有指定的值
@ConditionalOnWebApplication当前项目是Web项目的条件
@ConditionalOnNotWebApplication当项目不是Web项目的条件
@ConditionalOnExpression基于SpEL表达式作为判断条件
@ConditionalOnJava基于JVM版本作为判断条件
@ConditionalOnJndi在JNDI存在时查找指定的位置
@ConditionalOnSingleCandidate当指定Bean在SpringIoC容器内只有一个,或者虽然有多个但是指定首选的Bean

@ConditionalOnProperty

@ConditionalOnProperty几个主要参数:value指定配置文件中的key;havingValue用于指定这个key的值;matchIfMissing配置文件中没有找到对应的配置项。

将配置类改成如下形式,可以起到和上面一样的效果。@ConditionalOnProperty的参数 value指定配置文件中的key,havingValue用于指定这个key的值。

@Configuration
public class HeroConfiguration {

    @Bean
    @ConditionalOnProperty(value = "hero.condition",havingValue = "diana")
    public ISkill diana(){
        return new Diana();
    }

    @Bean
    @ConditionalOnProperty(value = "hero.condition",havingValue = "irelia")
    public ISkill irelia(){
        return new Irelia();
    }
}

当在@ConditionalOnProperty加一个参数(matchIfMissing = true),配置文件改为

hero.condition = irelia111

此时启动应用却发现报错了
在这里插入图片描述
当把配置文件中的配置项注释掉,就会把Diana注入到容器

#hero.condition = irelia

在这里插入图片描述
matchIfMissing并不是条件不成立,而是配置文件中没有找到对应的配置项,起到默认值的作用。

@ConditionalOnBean

当SpringIoC容器内存在指定Bean条件成立

当把配置类改为:

@Configuration
public class HeroConfiguration {
    @Bean
    @ConditionalOnBean(name="camille")
    public ISkill diana(){
        return new Diana();
    }
}

并且在Camille上标注@Component,此时可以将Diana注入容器

@Component
public class Camille {
}

在这里插入图片描述

@ConditionalOnMissingBean

当SpringIoC容器内不存在指定Bean条件成立
配置类改为

@Configuration
public class HeroConfiguration {

    @Bean
    @ConditionalOnMissingBean(name="camille")
    public ISkill diana(){
        return new Diana();
    }
}

此时Diana就不会被注入容器了

使用场景

@Conditional并不是只能在@Configuration配置类中使用,任何@Component及其衍生注解下面都可以追加条件注解。

以上这些条件注解在我们自己写业务时可能并不常用,通常在写一些第三方库时,实现了同一个接口可能有很多个Bean,为了保证实现一个接口的只有一个Bean就需要使用条件注解了。比如,当相同类型的Bean不存在时才注入当前的Bean,如果已经存在相同类型的Bean就不再注入,这种情况下就可以使用 @ConditionalOnMissingBean ;还有有时Bean与Bean之间存在依赖关系,就像上面的例子中BannerController依赖于ISkill接口,很多时候我们需要判断它所依赖的Bean是否存在,如果存在才把它注入容器,这种情况下就可以使用 @ConditionalOnBean