配置是Spring 框架的重要核心之一,所以Spring 应用能够正常的跑起来肯定是需要配置的,但是使用的Spring Boot 后很多配置没有做,那么AUTO-CONFIGURATION 到底是怎么发生的呢?发生的顺序和步骤是怎样的?怎么实现的呢?
在应用中使用Spring Boot的时候,spring-boot-autoconfigure这个jar包会被引入进来,这个Jar包里面包含了一些配置类。这个包里的所有都在应用的claspath上,都有可能在自动配置执行的过程中被用到,从而简化应用的配置工作。这个jar的内容大致是下面这个样子:
箭头指向的基本在ReadingList应用中都有用到,这些配置类通过注解的方式使用,在运行期通过一系列的条件判断进行Bean的加载和配置。在这些配置完成后,一个可用的应用运行环境就准备好了。实现这些配置类的基础是Spring 4.0 提供的Condition 接口,通过实现Condition 接口的matches 方法来进行动态的判定。先来看看Condition 接口怎么用。
1. Spring 4.0 开始提供的 Condition 接口。
举个例子。需要在应用中建立一个操作数据库的DBService 类,这个类中要用到JdbcTemplate的实例。那么实例化JdbcTemplate前,首先要保证JdbcTemplate 在应用的classpath上,那怎么判断classpath上是不是有JdbcTemplate?用Condition 接口怎么实现这个判断呢?
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; } } }
这个判断实现完成,如何使用这个判断条件呢?
@Conditional(JdbcTemplateCondition.class) public DBService dbService() { @AutoWire JdbcTemplate jdbcTemplate; // TODO }
DBService 在实例化以前首先要判断 JdbcTemplateCondition,返回true才会继续进行实例化,否则当前的类会被忽略,不进行实例化。
2、Spring Boot 实现的Conditional** 形式的注解。
Spring Boot 实现了一系列的Conditional Annotation。这些基本上是可以见名知意的。下面就是一部分常见的条件注解(Conditional Annotation)。
没有不要去看所有的条件注解的具体实现,但是为了能够比较明晰的解释Auto-Configuration 的工作过程,将 DataSourceAutoConfiguration 的具体实现写一下,加深下自动配置的理解,也可以学习使用 @Conditional 注解和 接口Condition 的使用。
@Configuration @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class}) @EnableConfigurationProperties({DataSourceProperties.class}) @Import({Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class}) public class DataSourceAutoConfiguration { ... ... }
1、第一个注解 @Configuration 这个和xml 配置Bean 是同样的效果,这个就不详细解释了。
2、第二个注解 @ConditionalOnClass 的意思:在classpath 在上有相应的类的话 返回True,否则返回false。
3、第三个注解 @EnableConfigurationProperties({DataSourceProperties.class}) 允许使用application.properties 文件配置 DataSource 的参数。
4、第四个注解 @Import 将其参数中的类引入并初始化为一个Bean。
真正要关注的是第二个注解 ConditionalOnClass,这个注解判断 DataSource 和 EmbededDataBaseType 两个类是否在classpath 上。如果此条件判定为真,开始后续的判定。继而判断当前是否有DataSource 实例,是否是嵌入式的数据,是否有嵌入式的Tomcat 等等。。一些列运行时判定最终决定是否生成配置一个可用的数据源。
3、ReadingList 工程中 Auto-Configuration 工作的过程。
(1)因为H2在classpath上,因此一个嵌入式的H2 数据库被实例化,这个Bean 是 javax.sql.DataSource 类型的,所以 一个JPA 的接口需要实例化。
(2)因为 Hibernate Entity Manager 在classpath上,因此启用Hibernate的话,需要auto-configuration 需要实例化 LocalContainerManagerFactory Bean 和 JPAVendorAdapter。
(3)因为 JPA 在 classpath 上,所以repository 的实例也会被自动创建。
(4)因为Spring MVC 在classpath上, Spring 的DispatcherServlet 会被实例化。
(5)因为是web 应用,因此一个资源的handler 会被注册,来处理 resource 目录下的静态资源。
(6)因为Tomcat 在classpath 上,一个内置的Tomcat 容器会被启动承载和提供服务。
4、其实不知道也不影响用Spring Boot 实现业务嘛。
这一篇不写也罢。
举个用Condition 的例子:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; // 如果在applicaton.properties 文件中 见env.test=1 那么对应的类会被加载 @ConfigurationProperties("env.test") public class TestEnableCondition implements Condition { public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { String test = conditionContext.getEnvironment().getProperty("env.test"); int isTest; try { isTest = Integer.valueOf(test); } catch (Exception e) { isTest = 0; } return isTest==1; } }
@Conditional(TestEnableCondition.class) @Controller @RequestMapping(value = "/{param}") public class TestController { ... ... }