上一节我们讲到如何使用@Configuration
下面基于@Configuration的例子,讲解SpringBoot的包扫描机制
1、SpringBoot的包扫描 @ComponentScan
正如上篇文章说到,SpringBoot的包扫描可以让定义的bean自动加入到容器中,通过DI实现IOC思想。那么SpringBoot是怎么扫描的呢?
SpringBoot通过@ComponentScan扫描注解
默认情况:扫描入口类的同级或同级以下
入口的@SpringBootApplication里面有@ComponentScan
下面做个测试,如果把Controller放在上级会如何?
结果报错:This application has no explicit mapping for /error, so you are seeing this as a fallback.
如何解决上述的问题?
- 把文件放置入口的同级或同级以下
- @ComponentScan(“com.***”) 指定地址
结果:可以正常访问。
2、策略模式如何让面向对象编程改成面向抽象编程呢?
策略模式应对面向对象的方式
- ByType,缺点:要避免接口同时被多次实现
- ByName,缺点:业务调整时,需要修改源码
- @Qualifier 指定bean
- 在@Components下追加@Primary提高优先级
- 条件注解@Conditional+Condition
2.1、简单的@Conditional–demo
本例子使用@Configuration和@Bean方式引入容器
在方法实现层,因为使用的是ISkill,如果出现多个类实现接口,会报错。
本次通过条件注解来应对面向抽象问题。
2.1.1、创建一个package,命名为condition,并创建一个FoxCondition类和XingCondition类
切记:import org.springframework.context.annotation.ConditionContext
/*FoxCondition源码*/
package com.fcors.fcors.sample.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class FoxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
/*XingCondition源码*/
package com.fcors.fcors.sample.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class XingCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
2.1.2、修改@Configuration的类
@Configuration与@Bean组合使用
@Conditional(value):value是一个类
package com.fcors.fcors.sample;
import com.fcors.fcors.sample.condition.FoxCondition;
import com.fcors.fcors.sample.condition.XingCondition;
import com.fcors.fcors.sample.hero.Fox;
import com.fcors.fcors.sample.hero.Xing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class HeroConfiguration {
@Bean
@Conditional(XingCondition.class)
public ISkill xing(){
Xing aaa = new Xing();
aaa.setName("Fcors");
return aaa;
}
@Bean
@Conditional(FoxCondition.class)
public ISkill fox(){
return new Fox();
}
}
demo成功实现,我们可以通过条件注解实现。
上面demo存在缺点,例如有多个类同时实现接口时,不可能创建多个类。比较好的解决方法就是,把需要调用的类写在配置文件中,然后通过条件注解去实现。
3、条件注解+文件配置方式
3.1、在配置文件中引入hero.condition
hreo.condition=fox
3.2、在foxCondition中增加条件
package com.fcors.fcors.sample.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class FoxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String HeroName = context.getEnvironment().getProperty("hreo.condition");
return "fox".equalsIgnoreCase(HeroName);
}
}
3.3、扩展学习
- @Conditional0nBean当 SpringIOC容器内存在指定 Bean的条件
- @ConditionalOnClass
- @ConditionalOnExpression 基于SpElL表达式作为判断条件
- @CondtionalOnJava 基于JVM版本作为判断条件
- @CondtionalOnJndi 在JNDI存在时查找指定的位置
- @CondtionalOnMissingBean 在SpringIOC容器中不存在Bean的条件
- @CondtionalOnMissingClass 在SpringIOC容器中不存在Class的条件
- @CondtionalOnNotWebApplication 当前项目不是Web项目的条件
- @CondtionalOnProperty 指定的属性是否有指定的某个值
- @CondtionalOnResource 类的路径是否有指定的值
- @CondtionalOnSingleCandidate当指定Bean在SpringIoc容器中内只有一个;或着虽然有多个但指定首选的Bean
- @CondtionalOnWebApplication 当前项目是Web项目的条件
3.3.1、@ConditionOnPropetry
其实SpringBoot已经封装好了一些成品条件组件。例如@ConditionOnPropetry。该组件的功能跟上面例子的文件配置功能效果一样。
package com.fcors.fcors.sample;
import com.fcors.fcors.sample.condition.FoxCondition;
import com.fcors.fcors.sample.condition.XingCondition;
import com.fcors.fcors.sample.hero.Fox;
import com.fcors.fcors.sample.hero.Xing;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class HeroConfiguration {
@Bean
@ConditionalOnProperty(value="hero.condition",havingValue = "xing",matchIfMissing = true)
public ISkill xing(){
Xing aaa = new Xing();
aaa.setName("Fcors");
return aaa;
}
@Bean
@ConditionalOnProperty(value="hero.condition",havingValue = "fox",matchIfMissing = true)
public ISkill fox(){
return new Fox();
}
}
名词解析:matchIfMissing=true,是设默认值的意思;当配置文件中没有时,则默认,并非无法匹配时。
3.3.2、@ConditionalOnBean
@ConditionalOnBean(value=””)
当容器存在指定的bean
使用场景,依赖
package com.fcors.fcors.sample;
import com.fcors.fcors.sample.condition.FoxCondition;
import com.fcors.fcors.sample.condition.XingCondition;
import com.fcors.fcors.sample.hero.Fox;
import com.fcors.fcors.sample.hero.Xing;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class HeroConfiguration {
@Bean
@ConditionalOnBean(name="mysql")
public ISkill xing(){
Xing aaa = new Xing();
aaa.setName("Fcors");
return aaa;
}
@Bean
@ConditionalOnProperty(value="hero.condition",havingValue = "fox",matchIfMissing = true)
public ISkill fox(){
return new Fox();
}
}
3.3.2、@ContionalMissingBean
@ContionalMissingBean
当容器不存在指定的bean
使用场景:重复引用
下一节,我们将讲解Java的全局异常机制