问题:
项目启动时需要加载多个配置类,且需要控制某些配置类的加载顺序。使用@Configuration标注配置类,使用@AutoConfigureAfter、@AutoConfigureBefore设置配置类加载顺序。
结果:能够正常加载配置类,但无法控制配置类的加载顺序。
Spring加载配置文件
方式一:构建ApplicationContext时手动传入配置文件类,可控制配置文件加载顺序。
ApplicationContext context = new AnnotationConfigApplicationContext(Config1.class, Config2.class);
方式二:使用@Configuration和@ComponentScan自动扫描配置类,无法控制加载顺序。
SpringBoot下可以通过@Configuration自动扫描配置类和spring.factories来加载配置类,但这两种方式都无法控制加载顺序。
此时,可通过在配置类上增加@AutoConfigureAfter 、 @AutoConfigureBefore和@AutoConfigureOrder来控制配置文件加载的相对顺序。
前两个注解在SpringBoot1.0.0就支持了,而@AutoConfigureOrder注解则是在SpringBoot1.3.0版本新增。
SpringBoot的自动配置是通过spring.factories来指定的,它的优先级最低,加载时间最晚,spring.factories中的配置类顺序不代表实际加载顺序。可结合 @AutoConfigureAfter 和 @AutoConfigureBefore注解控制配置类的相对加载顺序。
通过@Configuration和@ComponentScan扫描加载的配置类,一般是我们自定义的配置类,这部分配置类优先级最高,加载时间最早,在加载spring.factories配置类前加载,但加载顺序不定。
注意:@AutoConfigureAfter 和 @AutoConfigureBefore 只有在自动配置类上才会生效。如果一个配置类是通过@Configuration扫描加载,那么@AutoConfigureAfter 和 @AutoConfigureBefore就会失效。
错误示例
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan("com.example.config") public class BootStrapApplication { public static void main(String[] args) { SpringApplication.run(BootStrapApplication.class); } } 两个config类,在Config2上增加@AutoConfigureBefore(Config1.class),希望配置按config2、config1加载 package com.example.config; import org.springframework.context.annotation.Configuration; @Configuration public class Config1 { Config1() { System.out.println("this is Config1"); } } package com.example.config; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.context.annotation.Configuration; @Configuration @AutoConfigureBefore(Config1.class) public class Config2 { Config2() { System.out.println("this is Config2"); } }
正确示例
在错误示例的基础上,增加以下配置:
定义spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.autoconfig.AutoConfig1,\ com.example.autoconfig.AutoConfig2,\ com.example.autoconfig.AutoConfig3
定义自动配置类,使用@AutoConfigureBefore和@AutoConfigureAfter注解,期望按AutoConfig3、AutoConfig2、AutoConfig1顺序加载。
package com.example.autoconfig; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @AutoConfigureAfter(AutoConfig2.class) public class AutoConfig1 { AutoConfig1() { System.out.println("this is AutoConfig1"); } } package com.example.autoconfig; public class AutoConfig2 { AutoConfig2() { System.out.println("this is AutoConfig2"); } } package com.example.autoconfig; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @AutoConfigureBefore(AutoConfig2.class) public class AutoConfig3 { AutoConfig3() { System.out.println("this is AutoConfig3"); } }
启动顺序:3 --> 2 --> 1
@AutoConfigureBefore和@AutoConfigureAfter生效了。
且Config先于AutoConfig加载,说明通过扫描加载的自定义配置类优先级更高,加载时间最早;通过spring.factories加载的配置类优先级更低,加载时间最晚。
再看一个例子
预期加载顺序:C --> B --> A
@Configuration @AutoConfigureAfter(ConfigurationB.class) public class ConfigurationA { ConfigurationA(){ System.out.println("CofigurationA 已经被初始化!"); } } @Configuration @AutoConfigureAfter(ConfigurationC.class) public class ConfigurationB { ConfigurationB(){ System.out.println("ConfigurationB 已经被初始化!"); } } @Configuration public class ConfigurationC { ConfigurationC(){ System.out.println("CofigurationC 已经被初始化!"); } }
结果:
CofigurationA 已经被初始化! ConfigurationB 已经被初始化! CofigurationC 已经被初始化!
如上使用
@AutoConfigureBefore
、@AutoConfigureAfter
、@AutoConfigureOrder
调整配置顺序,不会生效,结果打印顺序依然是a、b、c如果想通过
@AutoConfigureAfter
@AutoConfigureBefore
@AutoConfigureOrder
保证bean的加载顺序可通过一下方式:
- 将配置类放在Spring扫描路径里外面(scanBasePackages)
- 如果还是想放在扫描路径里面就需要将@Configuration注解去掉
- 指定顺序:添加文件META-INF/spring.factories
在spring.factories中配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.*******.Demo.ConfigurationA,\ com.*******.Demo.ConfigurationB,\ com.*******.Demo.ConfigurationC