自定义注解
有时我们想根据自己的业务需求自定义条件注解,需要使用@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