在向应用程序加入Spring Boot时,有个名为spring-boot-autoconfigure的JAR文件,其中包含了很多配置类。每个配置类都在应用程序的Classpath里,都有机会为应用程序的配置添砖加瓦。这些配置类里有用于Thymeleaf的配置,有用于Spring Data JPA的配置,有用于SpringMVC的配置,还有很多其他东西的配置,你可以自己选择是否在Spring应用程序中使用它们。
所有这些配置如此与众不同,原因在于它们利用了Spring的条件化配置,这是Spring4.0引入的新特性。条件化配置允许配置存在于应用程序中,但在满足某些特定条件之前都忽略这个配置。
在Spring里可以很方便地编写你自己的条件,你所要做的就是实现condition接口,覆盖它的matches()方法。举例来说,下面这个简单的条件类只有在Classpath里存在JdbcTemplate时才会生效:
package readinglist;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class JdbcTemplateCondition implements Condition
{
@Override
public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata)
{
try
{
context.getClassLoader().loadClass("org.springframework.jdbc.core.JdbcTemplate");
return true;
}
catch(Exception e)
{
return false;
}
}
}
当你用Java来声明Bean的时候,可以使用这个自定义条件类:
@Conditional(JdbcTemplateCondition.class)
public MyService()
{
....
}
在例子中,只有当JdbcTemplateConditin类的条件成立时才会创建MyService这个Bean。也就是说MyService Bean 创建的条件是Classpath里有JdbcTemplate。否则,这个Bean的声明就会忽略掉。
在Spring Boot中定义了很多更有趣的条件,并把它们运用到了配置类上,这些配置类构成了Spring Boot的自动配置。Spring Boot 运用条件化配置的方法,定义多个特殊的条件化注解,并将他们用到配置类上。下面是Spring Boot自动配置中使用的条件化注解:
条件化注解 | 配置生效条件 |
---|---|
@ConditionalOnBean | 配置了某个特定Bean |
@ConditionalOnMissingBean | 没有配置特定的Bean |
@ConditionalOnClass | Classpath里有指定的类 |
@ConditionalOnMissingClass | Classpath里没有指定的类 |
@ConditionalOnExpression | 给定的Spring Expression Language表达式计算结果为true |
@ConditionalOnJava | Java的版本匹配特定值或者一个范围 |
@ConditionalOnJndi | 参数中给定的JNDI位置必须存在一个 |
@ConditionalOnProperty | 指定的就配置属性要有一个明确的值 |
@ConditionalOnResource | Classpath里有指定的资源 |
@ConditionalOnWebApplication | 这是个Web应用程序 |
@ConditionalOnNotWebApplication | 这不是个Web应用程序 |
一般来说,无需查看Spring Boot自动配置类的源代码,但为了查看其自动配置的内核原理,我们可以看一下DataSourceAutoConfiguration里的这个片段(这是Spring Boot自动配置库的一部分)
@configuration
@conditionalonClass({DataSource.class,EmbeddedDatabaseType.class})
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({Reigstrar.class,DataSourcePoolMetadataProvidersConfiguration.class})
public class DataSourceAutoConfiguration{
....
}
在上述代码中,DataSourceAutoConfiguration添加了@Configuration注解,它从其他配置类中导入了一些额外的配置,还定义了一些Bean。最重要的是,DataSourceAutoConfiguration上添加了@ConditionalOnClass注解,要求Classpath里必须要有DataSource和EmbeddedDatabaseType。如果他们不存在,条件就不成立,DataSourceAutoConfiguration提供的配置都会被忽略。
DataSourceAutoConfiguration里嵌入了一个JdbcTemplateConfiguration类,自动配置了一个JdbcTemplate Bean;
@Configuration
@Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class)
protected static class JdbcTemplateConfiguration
{
@Autowired(required=false)
private DataSource dataSource;
@Bean
@ConditionalOnMissingBean(JdbcOperations.class)
public JdbcTemplate jdbcTempalte()
{
return new JdbcTemplate(this.dataSource);
}
...
}
自动配置会做出以下配置决策,比如:
- 因为Classpath里有H2,所以会创建一个嵌入式的H2数据库Bean,它的类型是javax.sql.DataSource,JPA实现(Hibernate)需要它来访问数据库。
- 因为Classpath里有Hibernate(Spring Data JPA传递引入的)的实体管理器,所以自动配置会配置与Hibernate相关的Bean,包括Spring 的LocalContainerEntityMangerFactoryBean和JpaVendorAdapter。
- 因为Classpath里有Spring Data JPA,所以它会自动配置为根据仓库的接口差U年间仓库实现。
- 因为Classpath里有Thymeleaf,所以Thymeleaf会配置为Spring MVC的视图,包括一个Thymeleaf的模板解析器、模板引擎及视图解析器。试图解析器会解析相对于Classpath根目录/templates目录里的模板。
- 因为Classpath里有Spring MVC,所以会配置Spring的DispatcherServlet并启动SpringMVC。
- 因为这是一个SpringMVCWeb应用程序,所以会注册一个资源处理器,把相对于Classpath根目录/static里的静态内容提供出来
- 因为Classpath里有Tomcat,所以会启动一个嵌入式的Tomcat容器,监听8080端口。