解决springboot自定义配置Boolean属性不能生效的问题
作者:Happywzy~
springboot自定义配置Boolean属性不能生效
属性名不能是is开头,例如isLog属性,你在配置文件中不管怎么给这个属性设值都不会生效,只需改成log即可。
我使用的版本
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> </parent>
springboot自动配置原理
springboot自动配置
1、概述
自动配置的功能是其简化运用的关键技术,思想就是约定大于配置,意思就是一个工程约定必须要有事务功能,要有aop功能,要有mvc功能等,所以springboot在创建工程的时候自动把这些功能所需的类实例化并加入到spring容器了,这个就是约定大于配置,约定了必须要有这些功能。
2、springboot中的SPI机制
java原生的SPI,是一种服务发现机制( Service Provider Interface)。
它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI。
- 2.1、JDK中的SPI机制
public interface Log { boolean support(String type); void info(); }
在resources/META-INF/services目录创建文件,文件名称必须跟接口的完整限定名相同。
这个接口文件中配置了该接口的所有实现类的完整限定名。
然后jdk api 加载配置文件
//jdk api 加载配置文件配置实例 ServiceLoader<Log> all = ServiceLoader.load(Log.class);
- 2.2、springboot中的SPI机制
具体流程和上面差不多,在工程的resources下面创建META-INF文件夹,在文件夹下创建spring.factories文件,在文件配置内容如下:
com.ss.yc.spi.Log=\ com.ss.yc.spi.Log4j,\ com.ss.yc.spi.Logback,\ com.ss.yc.spi.Slf4j
配置的key就是接口完整限定名,value就是接口的各个实现类,用","号隔开。
loadFactoryNames方法获取实现了接口的所有类的名称
@Test public void test() { List<String> strings = SpringFactoriesLoader .loadFactoryNames(Log.class, ClassUtils.getDefaultClassLoader()); for (String string : strings) { System.out.println(string); } }
loadFactories方法获取实现了接口的所有类的实例
@Test public void test1() { List<String> strings = SpringFactoriesLoader .loadFactories(Log.class, ClassUtils.getDefaultClassLoader()); for (String string : strings) { System.out.println(string); } }
- 2.3、我们以SpringFactoriesLoader.loadFactoryNames(Log.class, ClassUtils.getDefaultClassLoader());方法调用为例分析其源码
可以看到springboot spi是加载了整个工程的jar包和自己工程定义的spring.factories文件的。
其核心代码
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
public static Properties loadProperties(Resource resource) throws IOException { Properties props = new Properties(); //核心代码,把文件包装成properties对象 fillProperties(props, resource); return props; }
springboot中的SPI其实就是加载整个工程里面的spring.factories文件,然后把文件里面的内容建立一个key和value的映射关系,只是这个映射关系是一个类型和list的映射关系。
3、@EnableAutoConfiguration
@EnableAutoConfiguration注解是springboot自动配置的核心注解,就是因为有这个注解存在就会把例如事务,缓存,aop,mvc等功能自动导入到springboot工程中,Spring框架提供的各种名字为@Enable开头的Annotation定义,比如@EnableScheduling、@EnableMBeanExport等,@EnableAutoConfiguration的理念和做事方式其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义。
@SuppressWarnings("deprecation") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... }
@Import了一个类,这个AutoConfigurationImportSelector自动配置类
- 3.1、DeferredImportSelector
DeferredImportSelector该接口是ImportSelector接口的一个子接口,那么它是如何使用的呢?
// public class DeferredImportSelectorDemo implements DeferredImportSelector{ @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { System.out.println("=====DeferredImportSelectorDemo.selectImports"); return newString[]{DeferredBean.class.getName()}; } //要返回一个实现了Group接口的类 @Override public Class<?extendsGroup> getImportGroup(){ return DeferredImportSelectorGroupDemo.class; } private static class DeferredImportSelectorGroupDemo implements Group{ List<Entry> list=new ArrayList<>(); //收集需要实例化的类 @Override public void process(AnnotationMetadata metadata,DeferredImportSelector selector){ System.out.println("=====DeferredImportSelectorGroupDemo.process"); String[] strings=selector.selectImports(metadata); for(String string:strings){ list.add(newEntry(metadata,string)); } } //把收集到的类返回给spring容器 @Override public Iterable<Entry> selectImports(){ System.out.println("=====DeferredImportSelectorGroupDemo.selectImports"); return list; } } } //要实例的bean public class DeferredBean{}
该类必须是@Import导入进来
@Component //Import虽然是实例化一个类,Import进来的类可以实现一些接口@Import({DeferredImportSelectorDemo.class}) public class ImportBean{}
这样就实现了一个类的实例化。
- 3.2、EnableAutoConfigurationImportSelector
而AutoConfigurationImportSelector类,这个类就是@EnableAutoConfiguration注解中@Import进来的类,可以看到该类正是实现了DeferredImportSelector接口的。
该类其实就是收集spring.factories文件中以@EnableAutoConfiguration类型为key的所有的类,然后把这些类交给spring去实例化,而这些类就是我们说的aop、事务、缓存、mvc等功能的支持类,这就是自动配置的加载原理。
4、@Configuration
就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。
其中@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
5、@ComponentScan
其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描
自定义SpringBoot Starter
1.引入项目的配置依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.1.4.RELEASE</version> </dependency>
2.创建xxxService类
完成相关的操作逻辑
DemoService.java @Data public class DemoService{ private String str1; private String str2; }
3.定义xxxProperties类
属性配置类,完成属性配置相关的操作,比如设置属性前缀,用于在application.properties中配置
//指定项目在属性文件中配置的前缀为str,即可以在属性文件中通过 str.str1=springboot,就可以改变属性类字段 str1 的值了 @SuppressWarnings("ConfigurationProperties") @ConfigurationProperties(prefix = "str") public class DemoProperties { public static final String DEFAULT_STR1 = "I know, you need me"; public static final String DEFAULT_STR2 = "but I also need you"; private String str1 = DEFAULT_STR1; private String str2 = DEFAULT_STR2; }
4.定义xxxAutoConfiguration类
自动配置类,用于完成Bean创建等工作
// 定义 java 配置类 @Configuration //引入DemoService @ConditionalOnClass({DemoService.class}) // 将 application.properties 的相关的属性字段与该类一一对应,并生成 Bean @EnableConfigurationProperties(DemoProperties.class) public class DemoAutoConfiguration { // 注入属性类 @Autowired private DemoProperties demoProperties; @Bean // 当容器没有这个 Bean 的时候才创建这个 Bean @ConditionalOnMissingBean(DemoService.class) public DemoService helloworldService() { DemoService demoService= new DemoService(); demoService.setStr1(demoProperties.getStr1()); demoService.setStr2(demoProperties.getStr2()); return demoService; } }
5.在resources下创建目录META-INF
在 META-INF 目录下创建 spring.factories
在SpringBoot启动时会根据此文件来加载项目的自动化配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.springboot.config.DemoAutoConfiguration
6.其他项目中使用自定义的Starter
<!--引入自定义Starter--> <dependency> <groupId>com.lhf.springboot</groupId> <artifactId>spring-boot-starter-demo</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
7.编写属性配置文件
#配置自定义的属性信息 str.str1=为什么我的眼里常含泪水 str.str2=那是因为我对你爱的深沉
8.写注解使用
@RestController public class StringController { @Autowired private DemoService demoService; //引入自定义Starter中的DemoService @RequestMapping("/") public String addString(){ return demoService.getStr1()+ demoService.getStr2(); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。