一、引言
大家都知道,SpringBoot自2013诞生以来,广受各大企业青睐,可以快速便捷地搭建出生产级别的企业应用。那么问题来了,相对于Spring, SpringBoot有什么特点,可以使其在众多框架中脱颖而出呢?没错,答案就是它可以实现自动化配置,免去了之前繁琐的手工模板化的配置。
二、分析
SpringBoot的自动化配置对于开发者来说,可能会从不同的角度提现这一点。首先我们搭建一个SpringBoot工程(具体搭建方法,参考文章),接下来第一件事可能就是添加依赖,在添加依赖的时候,我们惊奇的发现不需要添加版本号,这给我们带来很大的方便,做开发的小伙伴可能遇到不同版本的SpringBoot,对其他框架的依赖版本是有要求的,否则会出现意想不到的错误。那么SpringBoot是如何帮助我们自动添加版本的呢?我们知道,在工程的pom.xml文件中一般都会加这个父类依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这个依赖中我们要指明SpringBoot的版本号,接下来我们一起看下starter-parent的依赖关系,打开对应工程的仓库地址:C:\Users\wangzh.m2\repository\org\springframework\boot\spring-boot-starter-parent\2.0.4.RELEASE,打开文件spring-boot-starter-parent-2.0.4.RELEASE.pom
从依赖关系看,starter-parent主要功能归纳为:
1.添加对spring-boot-dependencies的依赖
2.指定工程的编码格式
3.指定工程依赖的JDK版本
4.自动化打包与插件配置
5.资源过滤配置
我们根据指定的路径(…/…/spring-boot-dependencies)查看dependencies的依赖关系:
从上图看到,dependencies的依赖文件列举的属性properties里展示了各种依赖包的版本,正是由于这个版本的存在,我们在开发的时候才不必添加版本,就可以下载到相应的依赖。
SpringBoot自动配置的核心是源于条件注解,就是指在不同的条件下生成不同的bean,或者是在某个bean创建完成后,才去生成其他的bean,诸如此类,在特定的条件下创建bean的行为。比较常见的条件注解有:
@Conditional 依赖的条件
@ConditionalOnBean 在某个Bean存在的条件下
@ConditionalOnMissingBean 在某个Bean不存在的条件下
@ConditionalOnClass 在某个Class存在的条件下
@ConditionalOnMissingClass 在某个Class不存在的条件下
三、实战
以@Conditional为例,说明条件注解的作用。
条件注解在Spring中已经引入,在SpringBoot中得以广泛使用。
新建一个标准的Maven工程,在pom.xml添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
1.定义一个汽车的接口类及声明方法:
public interface Car {
public String getBrand(); //用于获取所属品牌
}
2.创建两个实现类:
public class BuickCar implements Car {
public String getBrand() {
return "别克汽车";
}
}
public class ToyotaCar implements Car{
public String getBrand() {
return "丰田汽车";
}
}
3.创建两个条件类,作为生成不同bean的条件
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class BuickCondition implements Condition {
public boolean matches(ConditionContext con, AnnotatedTypeMetadata meta) {
return con.getEnvironment().getProperty("car").equals("别克汽车");
}
}
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ToyotaCondition implements Condition {
public boolean matches(ConditionContext con, AnnotatedTypeMetadata meta) {
return con.getEnvironment().getProperty("car").equals("丰田汽车");
}
}
4.创建配置类:定义两个bean的名字均为car
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CarConfig {
@Bean("car")
@Conditional(BuickCondition.class)
Car buickCar() {
return new BuickCar();
}
@Bean("car")
@Conditional(ToyotaCondition.class)
Car toyotaCar() {
return new ToyotaCar();
}
}
5测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext actx = new AnnotationConfigApplicationContext();
actx.getEnvironment().getSystemProperties().put("car", "别克汽车");
actx.register(CarConfig.class);
actx.refresh();
Car car = (Car)actx.getBean("car");
System.out.println(car.getBrand());
}
结果:
如果在均不满足属性值的条件下,结果如下
public static void main(String[] args) {
AnnotationConfigApplicationContext actx = new AnnotationConfigApplicationContext();
actx.getEnvironment().getSystemProperties().put("car", "汽车");
actx.register(CarConfig.class);
actx.refresh();
Car car = (Car)actx.getBean("car");
System.out.println(car.getBrand());
}
结果:
四、总结
通过上面的例子看出,条件注解控制了创建bean的行为,尽管我们在定义bean的时候,把两个bean定义为相同的名字(car),但是其创建的过程还是依赖条件属性。当不满足条件,Spring容器不会创建bean。在实际开发中,灵活运用条件注解,才能更大限度发挥的spring容器的功能。