代码示例:https://git.oschina.net/null_584_3382/spring-boot-introduction
一、与mybatis集成
在介绍spring boot 自动配置原理之前,先看一个例子,spring boot集成mybatis,
测试用的数据库为mysql,所以引入mysql-connector-java依赖
引入myatis提供的mybatis-spring-boot-starter
最后引入druid连接池依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>RELEASE</version> </dependency> </dependencies>
按照mvc逻辑,首先是controller,很普通的代码
@RestController public class Controller { @Autowired DemoMapper demoMapper; @RequestMapping("/fetchByName") public Object fetchByName(@RequestParam("name") String name){ return demoMapper.fetchByName(name); } }
因为只是一个例子,所以省略server层,直接使用dao层
@Mapper public interface DemoMapper { @Select("select * from userinfo where name=#{name}") UserInfo fetchByName(String name); }
嗯,代码都是很普通的,但是datasource是在哪里配置的呢?
关键就在配置文件
spring: datasource: url: jdbc:mysql://${url}:3306/bootintro username: root password: ${password} driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource
好了,所有关键代码都在这里了(哦,还有Application类和domain类,省略了),运行一下(当然,你还要配置自己的数据库)
二、怎么做到的
没错,前面说过,spring boot为我们提供了自动配置,那自动配置的怎么做到的。
***AutoConfiguration类
spring boot中有很多类的名字为***AutoConfiguration,这些类都是一些spring的配置类(头上有@Configuration),就是这些类提供了一些默认配置,如果你使用spring基于java的配置,其实也是做了同样的事情,只不过springboot帮我们做!!
@EnableAutoConfiguration
恩,你肯定会接着问,那么spring boot怎么知道有哪些类是所谓的"***AutoConfiguration"。@EnableAutoConfiguration,就是这个注解开启了自动配置,这个注解包括:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
其中主要的是EnableAutoConfigurationImportSelector这个类,就是用来选择哪些是需要自动配置的。这个类的核心代码:
@Override public String[] selectImports(AnnotationMetadata metadata) { if (!isEnabled(metadata)) { return NO_IMPORTS; } try { AnnotationAttributes attributes = getAttributes(metadata); List<String> configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } }
其中的最关键的是getCandidateConfigurations()这个方法中的:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
追踪进去看就可以发现,这类就是在所有的classpath下查找META-INF/spring.factories这个文件,从这个文件中提出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,而这些值就是一些自动配置类
例如,mybatis提供的META-INF/spring.factories内容:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
多说2句:
- 基本所有的@EnableXXX 都有一个对应的类来控制自动配置加载
- 只有当你希望@EnableAutoConfiguration自动帮你加载的时候才使用配置META-INF/spring.factories这个文件来自动配置,因此其他的Enable大多要自己去实现ImportSelect,例如开启事务的注解@EnableTransactionManagement的加载自动配置类就比较直接。
@Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } }
全部都加载吗
非也,仔细看某个***AutoConfig,举个例子,例如自动配置http编码的CharacterEncodingFilter的自动配置类HttpEncodingAutoConfiguration:
@Configuration @EnableConfigurationProperties(HttpEncodingProperties.class) @ConditionalOnWebApplication @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { private final HttpEncodingProperties properties; public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean @ConditionalOnMissingBean(CharacterEncodingFilter.class) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; } ... ... }
其中,注解中包括许多@Confitionalxxx的,就是这些注解来控制该自动配置是否生效,根据这些注解名字大概都能知道是什么意思,例如@ConditionalOnWebApplication,这个肯定就是只有在web工程中才满足,@ConditionalOnClass只有存在CharacterEncodingFilter.class这个类的时候才有效。
怎么通过配置修改的
spring boot中的大多数自定义配置都可以通过修改配置来完成,例如,还是上面的那个例子,其中有个注解@EnableConfigurationProperties(HttpEncodingProperties.class),而HttpEncodingProperties类就是读取配置文件信息,因此,通过修改配置信息,自动配置类生效的时候就会按照配置信息来自动配置。例如,DataSourceAutoConfiguration,加载配置文件的类为
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, EnvironmentAware, InitializingBean { ... /** * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. */ private String driverClassName; /** * JDBC url of the database. */ private String url; /** * Login user of the database. */ private String username; /** * Login password of the database. */ private String password; ... }
可以知道,在spring boot的配置文件中查找配置属性,根据配置属性来填充该类的属性值
多说2句
- @ConfigurationProperties(prefix = "spring.datasource") 的意思就是会在配置文件中查找spring.datasource为前缀的,然后把对应属性自动注入该类中,例如 配置文件中的spring.datasource.name 的值就会注入到该类的name属性中,其他同理
- spring boot默认会加载application.yml(或者application.properties),并且会从4个位置去搜索该类
- A /config subdirectory of the current directory.
- The current directory
- A classpath /config package
- The classpath root
代码示例:https://git.oschina.net/null_584_3382/spring-boot-introduction