SprintBoot系列(四):包扫描与自动装配

上一节我们讲到如何使用@Configuration

http://www.fcors.com/%e6%8a%80%e6%9c%af%e4%b8%8e%e6%a1%86%e6%9e%b6/sprintboot%e7%b3%bb%e5%88%97%ef%bc%88%e4%b8%89%ef%bc%89%ef%bc%9a%e6%a8%a1%e5%bc%8f%e6%b3%a8%e8%a7%a3%e7%9a%84configuration/

下面基于@Configuration的例子,讲解SpringBoot的包扫描机制

1、SpringBoot的包扫描 @ComponentScan

正如上篇文章说到,SpringBoot的包扫描可以让定义的bean自动加入到容器中,通过DI实现IOC思想。那么SpringBoot是怎么扫描的呢?

SpringBoot通过@ComponentScan扫描注解

默认情况:扫描入口类的同级或同级以下

入口的@SpringBootApplication里面有@ComponentScan

JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图

下面做个测试,如果把Controller放在上级会如何?

JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图1

结果报错:This application has no explicit mapping for /error, so you are seeing this as a fallback.

如何解决上述的问题?

  • 把文件放置入口的同级或同级以下
  • @ComponentScan(“com.***”) 指定地址
JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图2

结果:可以正常访问。

2、策略模式如何让面向对象编程改成面向抽象编程呢?

策略模式应对面向对象的方式

  • ByType,缺点:要避免接口同时被多次实现
  • ByName,缺点:业务调整时,需要修改源码
  • @Qualifier 指定bean
  • 在@Components下追加@Primary提高优先级
  • 条件注解@Conditional+Condition

2.1、简单的@Conditional–demo

本例子使用@Configuration和@Bean方式引入容器

在方法实现层,因为使用的是ISkill,如果出现多个类实现接口,会报错。

JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图3

本次通过条件注解来应对面向抽象问题。

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();
    }
}
JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图4

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);
    }
}
JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图5

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,是设默认值的意思;当配置文件中没有时,则默认,并非无法匹配时。

JAVA、基础技术、技术与框架SprintBoot系列(四):包扫描与自动装配插图6

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的全局异常机制

http://www.fcors.com/%e6%8a%80%e6%9c%af%e4%b8%8e%e6%a1%86%e6%9e%b6/sprintboot%e7%b3%bb%e5%88%97%ef%bc%88%e4%ba%94%ef%bc%89%ef%bc%9a%e5%bc%82%e5%b8%b8%e5%a4%84%e7%90%86%e6%9c%ba%e5%88%b6/