006-Spring Boot自动配置

Posted 木子旭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了006-Spring Boot自动配置相关的知识,希望对你有一定的参考价值。

一、接口Condition、Conditional(原理)

主要提供一下方法

boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

true:表示装配

false:表示不装配

1.1、Conditional

  在Spring4中引入,其主要作用就是判断条件是否满足,从而决定是否初始化并向容器注册Bean

注解:Conditional() 参数是数组,数组内的都是true才装配

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

    /**
     * All {@link Condition}s that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class<? extends Condition>[] value();

}
View Code

通常配合使用。

示例:

接口:EncodingConvert

package com.lhx.spring.springboot_auto_config;

public interface EncodingConvert {

}
View Code

接口实现一:UTF8EncodingConvert 

package com.lhx.spring.springboot_auto_config;

public class UTF8EncodingConvert implements EncodingConvert {

}
View Code

接口实现二:GBKEncodingConvert

package com.lhx.spring.springboot_auto_config;

public class GBKEncodingConvert implements EncodingConvert {

}
View Code

配置类:

package com.lhx.spring.springboot_auto_config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;

@SpringBootConfiguration
public class EncodingConvertConfiguration {
    @Bean
    public EncodingConvert createGBKEncodingConvert() {
        return new GBKEncodingConvert();
    }

    @Bean
    public EncodingConvert createUTF8EncodingConvert() {
        return new UTF8EncodingConvert();
    }
}
View Code

App:

package com.lhx.spring.springboot_auto_config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.Conditional;

@SpringBootApplication
public class App {
    @Bean
    public Runnable createRunnable() {
        return () -> {
            System.out.println("spring boot is running");
        };
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        context.getBean(Runnable.class).run();
        //可以通过启动参数修改-Dfile.encoding=GBK
        System.out.println(System.getProperty("file.encoding"));
        System.out.println(context.getBeansOfType(EncodingConvert.class));
        context.close();
    }
}
View Code

此时,会发现连个接口实现都会被装配进来。

UTF8Condition实现Condition接口

package com.lhx.spring.springboot_auto_config;

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

public class UTF8Condition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String encoding = System.getProperty("file.encoding");
        if (encoding != null) {
            return "utf-8".equalsIgnoreCase(encoding);
        }
        return false;
    }

}
View Code

GBKCondition实现Condition接口

package com.lhx.spring.springboot_auto_config;

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

public class GBKCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String encoding = System.getProperty("file.encoding");
        if (encoding != null) {
            return "gbk".equalsIgnoreCase(encoding);
        }
        return false;
    }

}
View Code

修改配置类

package com.lhx.spring.springboot_auto_config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;

@SpringBootConfiguration
public class EncodingConvertConfiguration {
    @Bean
    @Conditional(GBKCondition.class)
    public EncodingConvert createGBKEncodingConvert() {
        return new GBKEncodingConvert();
    }

    @Bean
    @Conditional(UTF8Condition.class)
    public EncodingConvert createUTF8EncodingConvert() {
        return new UTF8EncodingConvert();
    }
}
View Code

此时,会发现会根据file.encoding值来装配接口类。

可在启动参数增加

  -Dfile.encoding=GBK  

  

然后调试,发现装配类也变了

注意:@Conditional也可以作用在类上

 二、Spring提供的Conditional自动配置

jar:spring-boot-autoconfigure中,org.springframework.boot.autoconfigure.condition;即spring-boot提供

ConditionalOnBean:当存在某个bean时候装配

ConditionalOnMissingBean:当不存在某个bean时候装配注解的bean

ConditionalOnClass:当classpath有才装配

ConditionalOnExpression:

ConditionalOnJava:JDK版本符合时候才装配

ConditionalOnNotWebApplication:不是web环境才装配

ConditionalOnWebApplication:是web环境才装配

ConditionalOnResource:资源存在才装配

ConditionalOnProperty:配置存在才装配

2.1、ConditionalOnProperty 配置存在匹配时候才配置

增加配置类

package com.lhx.spring.springboot_auto_config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;

@SpringBootConfiguration
public class UserConfiguration {

    @Bean
    @ConditionalOnProperty(name = "runnable.enable", havingValue = "true")
    public Runnable createRunnable() {
        return () -> {
        };
    }

}
View Code

App2编写

package com.lhx.spring.springboot_auto_config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.Conditional;

@SpringBootApplication
public class App2 {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}
View Code

默认是不能装配的

可以再application.properties中添加runnable.enable=true即可装配

或者@ConditionalOnProperty(name = "runnable.enable", havingValue = "true")增加

matchIfMissing=true,表示配置没有的时候也生效

2.2、ConditionalOnClass classpath 有某个类才装配

增加或删除maven,查看效果

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
        </dependency>

使用代码

    @Bean
    @ConditionalOnClass(name="com.google.gson.Gson")
    public Runnable createGsonRunnable() {
        return () -> {
        };
    }

2.3、ConditionalOnBean:根据容器中是否存在某个Bean进行装配

    @Bean
    @ConditionalOnBean(name="user")
    public Runnable createOnBeanRunnable() {
        return () -> {
        };
    }

示例一、根据配置选择不同的实现类注入

需求:根据配置选择不同的实现类注入,有个业务逻辑,有两套模式,大部分功能流程一致,但涉及某些实现不一致,标准的模板模式:004-行为型-02-模板方法模式(Template Method)

 实际使用中会有,默认是一种实现,如果有某个配置,则生效。可以参看springboot自动装配实现,如Cache等

实现:https://github.com/bjlhx15/common.git   spring-framework-core/spring-aop/testconditional

基础业务接口

public interface ITestConditionalService {
    String getBefore();
    String get();
}

 

如果新增业务需要新增配置

public class ConstBean {
    public final static String atest="atest";
    public final static String btest="btest";
}

 

抽象类实现公共方案步骤

public abstract class AbstractTestConditionalService implements ITestConditionalService {

    @Override
    public abstract String getBefore();

    @Override
    public String get() {
        String before = this.getBefore();
        // do something
        return "before:" + before;
    }
}

 

第一种实现方案:也就是默认方案

@Service("aTestConditionalServiceImpl")
@ConditionalOnProperty(name = "set.test",havingValue = ConstBean.atest,matchIfMissing = true)
public class ATestConditionalServiceImpl extends AbstractTestConditionalService {
    @Override
    public String getBefore() {
        return "atest test";
    }
}

 

第二种方案

@Service("bTestConditionalServiceImpl")
@ConditionalOnProperty(name = "set.test",havingValue = ConstBean.btest)
public class BTestConditionalServiceImpl extends AbstractTestConditionalService {
    @Override
    public String getBefore() {
        return "btest test";
    }
}

 

配置 如果是a逻辑可默认不写

# 配置实现 atest、btest
set.test=atest

 

以上是关于006-Spring Boot自动配置的主要内容,如果未能解决你的问题,请参考以下文章

006-spring cache-缓存实现-01-原生实现

Persisting data with object-relational mapping-006Spring-Data的运行规则(@Enabl

006-spring cloud gateway-GatewayAutoConfiguration核心配置-GatewayProperties初始化加载Route初始化加载

006Spring面向切面

VSCode 配置 用户自定义代码片段 自定义自动代码补充

从零开始配置vim(27)——代码片段