SpringBoot之@Import注解正确使用方式
建议首先参考文章:
https://www.jianshu.com/p/6b2f672e2446
了解SpringBoot之@Import注解正确使用方式
SpringBoot 自动配置的实现原理
SpringBoot 的核心就是自动配置,自动配置又是基于条件判断来配置 Bean。关于自动配置的源码在 spring-boot-autoconfigure-2.0.3.RELEASE.jar
image回顾配置属性
在通常需要我们在 property 中配置信息时,通常使用 @ConfigurationProperties(pefix=“前缀”) 注解的方式从配置文件中获取配置,如下:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.context.properties.ConfigurationProperties;
@RestController
@ConfigurationProperties(prefix = "test")
//@Component //如果这里添加了注解那么在自动配置类的时候就不用添加@enableConfigurationProperties(HelloProperties.class)注解.
public class Demo {
private String msg="default";//现在我们在配置文件写hello.msg=world,因为简单就不再展示;如果那么默认为default.
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@RequestMapping("/msg")
public Object index(){
return this.msg;
}
}
application.yml 中配置信息
test:
msg: bamboo
访问 url 获取配置信息返回的值
http://localhost:8080/msg
如果把 application.yml 中的配置信息注释掉则默认使用 default 值,否则使用配置信息中的值,以上便是普通配置方式
解析
SpringBoot 运行原理
先看 @SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
主要关注的几个注解如下
@SpringBootConfiguration:标记当前类为配置类
@EnableAutoConfiguration:开启自动配置
@ComponentScan:扫描主类所在的同级包以及下级包里的 Bean
关键是 @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
最关键的要属 @Import(EnableAutoConfigurationImportSelector.class),借助** EnableAutoConfigurationImportSelector**,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器: 通过 @Import(AutoConfigurationImportSelector.class) 导入的配置功能,
AutoConfigurationImportSelector 中的方法 getCandidateConfigurations,得到待配置的 class 的类名集合, 这个集合就是所有需要进行自动配置的类,而是是否配置的关键在于 META-INF/spring.factories 文件中是否存在该配置信息
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
打开,如下图可以看到所有需要配置的类全路径都在文件中,每行一个配置,多个类名逗号分隔, 而 \ 表示忽略换行
image image整个流程如上图所示
样例讲解
以 SpringApplicationAdminJmxAutoConfiguration 类来看其主要构成部分
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class}) //配置完JmxAutoConfiguration后再配置当前类型
// spring.application.admin为前缀,属性为enabled,有值时为true,没有匹配到则为false:以上条件为true则实例化,否则不是实例化
@ConditionalOnProperty( prefix = "spring.application.admin", value = {"enabled"}, havingValue = "true", matchIfMissing = false)
public class SpringApplicationAdminJmxAutoConfiguration
都能看到各种各样的条件判断注解,满足条件时就加载这个 Bean 并实例化
此类的条件注解是:@ConditionalOnProperty
@ConditionalOnBean:当容器里有指定 Bean 的条件下
@ConditionalOnClass:当类路径下有指定的类的条件下
@ConditionalOnExpression:基于 SpEL 表达式为 true 的时候作为判断条件才去实例化
@ConditionalOnJava:基于 JVM 版本作为判断条件
@ConditionalOnJndi:在 JNDI 存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
@ConditionalOnMissingClass:当容器里没有指定类的情况下
@ConditionalOnWebApplication:当前项目时 Web 项目的条件下
@ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnOnSingleCandidate:当指定 Bean 在容器中只有一个,或者有多个但是指定首选的 Bean
这些注解都组合了 @Conditional 注解,只是使用了不同的条件组合最后为 true 时才会去实例化需要实例化的类,否则忽略
这种 spring4.X 带来的动态组合很容易后期配置,从而避免了硬编码,使配置信息更加灵活多变,同时也避免了不必要的意外异常报错。使用的人只要知道配置的条件即可也不用去阅读源码,方便快捷,这也是 sprignboot 快捷方式带来的好处
参考 HttpEncodingAutoConfiguration 配置信息如下
@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
@Configuration:标明为配置类
@EnableConfigurationProperties(HttpEncodingProperties.class) 声明开启属性注入
@ConditionalOnClass(CharacterEncodingFilter.class) 当 CharacterEncodingFilter 在类路径的条件下
@ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true) 当 spring.http.encoding=enabled 的情况下,如果没有设置则默认为 true,即条件符合
@ConditionalOnMissingBean 当容器中没有这个 Bean 时新建 Bean
案例扩展
/**
* @author wuweifeng wrote on 2017/11/25.
* 根据部署环境动态决定是否启用eureka
线上的环境开启eureka,就在application-prod.yml里配上open.eureka=true,
其他的yml什么也不写就行了。这样本地启动时就相当于没有开启EnableDiscoveryClient
*/
@Component
@ConditionalOnProperty(value = "open.eureka")
@EnableDiscoveryClient
public class JudgeEnableDiscoveryClient
自己实现一个自己的自动配置
项目
xm-common:普通jar项目
- src/main
java
BambooServer.java 需要被实例化的服务类
BambooServerProperties.java 配置信息属性类
BmbooServiceAutoConfiguration.java 自动配置类
resources
META-INF/spring.factories 配置自动配置的属性文件
demo:普通springboot-web项目
需要实例化的服务类
public class BambooServer {
private String name;
public String sayServerName(){
return "I'm " + name + "! ";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
配置信息对应的属性映射类, 需要 pom 中加入 spring-boot-starter 依赖
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "bamboo")
public class BambooServerProperties {
private static final String NAME = "bamboo_server0";
private String name = NAME;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
自动配置文件
/**
* Author: bamboo
* Time: 2018/11/25/025
* Describe: 自动配置类
* 根据条件判断是否要自动配置,创建Bean
*/
@Configuration
@EnableConfigurationProperties(BambooServerProperties.class)
@ConditionalOnClass(BambooServer.class)//判断BambooServer这个类在类路径中是否存在
@ConditionalOnProperty(prefix = "bamboo",value = "enabled",matchIfMissing = true)
public class BmbooServiceAutoConfiguration {
@Autowired
private BambooServerProperties mistraServiceProperties;
@Bean(name = "bambooServer")
@ConditionalOnMissingBean(BambooServer.class)//当容器中没有这个Bean时(BambooServer)就自动配置这个Bean,Bean的参数来自于BambooServerProperties
public BambooServer mistraService(){
BambooServer mistraService = new BambooServer();
mistraService.setName(mistraServiceProperties.getName());
return mistraService;
}
}
在创建如下路径文件 src/main/resources/META-INF/spring.factories
Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bamboo.common.autoconfigure.bamboo.BmbooServiceAutoConfiguration
必须是自动配置类的全路径
mvn install 该项目
创建一个 springboot-mvc 项目 pom 依赖上面的 jar
@SpringBootApplication
@RestController
//@Import(value = {CorsConfig.class, LogFilter.class}) //跨域,接口访问请求日志
public class DemoApplication {
@Autowired
private BambooServer bmbooService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RequestMapping("/")
public Object index(){
return "helll demo"+bmbooService.getName()+DateUtils.getDate();
}
}
http://localhost:8080 / 则返回当前服务的默认值
在 applicaton.yml 中加, 重启刷新则会更新为如下信息
bamboo:
name: 测试服务
总结图
imageSpringBoot 自动化配置关键组件关系图
mybatis-spring-boot-starter、spring-boot-starter-web 等组件的 META-INF 文件下均含有 spring.factories 文件,自动配置模块中,SpringFactoriesLoader 收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的 bean。
@ConditionalOnProperty 的作用和用法
在 spring boot 中有时候需要控制配置类是否生效, 可以使用 @ConditionalOnProperty 注解来控制 @Configuration 是否生效.
配置类代码:
@Configuration
@ConditionalOnProperty(prefix = "filter",name = "loginFilter",havingValue = "true")
public class FilterConfig {
//prefix为配置文件中的前缀,
//name为配置的名字
//havingValue是与配置的值对比值,当两个值相同返回true,配置类生效.
@Bean
public FilterRegistrationBean getFilterRegistration() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean(new LoginFilter());
filterRegistration.addUrlPatterns("/*");
return filterRegistration;
}
}
配置文件中的代码
filter.loginFilter=true
网友评论