spring boot學習筆記(二):自動配置原理

代碼示例:https://git.oschina.net/null_584_3382/spring-boot-introductionjava

1、與mybatis集成

在介紹spring boot 自動配置原理以前,先看一個例子,spring boot集成mybatis,mysql

測試用的數據庫爲mysql,因此引入mysql-connector-java依賴git

引入myatis提供的mybatis-spring-boot-starterweb

最後引入druid鏈接池依賴spring

<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,很普通的代碼sql

@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是在哪裏配置的呢?springboot

關鍵就在配置文件mybatis

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類,省略了),運行一下(固然,你還要配置本身的數據庫)mvc

2、怎麼作到的

沒錯,前面說過,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句:

  1. 基本全部的@EnableXXX 都有一個對應的類來控制自動配置加載
  2. 只有當你但願@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句

  1. @ConfigurationProperties(prefix = "spring.datasource") 的意思就是會在配置文件中查找spring.datasource爲前綴的,而後把對應屬性自動注入該類中,例如 配置文件中的spring.datasource.name 的值就會注入到該類的name屬性中,其餘同理
  2. 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

相關文章
相關標籤/搜索