自动配置是什么?

什么是自动配置?举例来讲,当你通过@Autowired或@Resource注解,自动注入一个类实例之前,被注入进来的这个类实例需要被spring容器纳管,不然肯定会注入失败。往往我们会在xml通过`bean id="dfdf"` 或者在类定义上使用@Component、@Configuration等注解,来实现其被spring容器管理。而对于jar包中的类,则稍微复杂一点,要根据jar包中类的实现进行相应引入。
而spring boot的自动配置功能,会对我们配置的一些类,自动注入到spring容器中。特别是对于依赖的jar包中的一些类,在我们的工程用到这些类实例时,直接@Autowired或@Resource注解注入使用就可以了。

自动配置如何实现的?

使用@EnableAutoConfiguration注解,会启用自动配置

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

该注解导入了EnableAutoConfigurationImportSelector,其selectImports方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   try {
      AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
      AnnotationAttributes attributes = getAttributes(annotationMetadata);
      List<String> configurations = getCandidateConfigurations(annotationMetadata,
            attributes);
      configurations = removeDuplicates(configurations);
      configurations = sort(configurations, autoConfigurationMetadata);
      Set<String> exclusions = getExclusions(annotationMetadata, attributes);
      checkExcludedClasses(configurations, exclusions);
      configurations.removeAll(exclusions);
      configurations = filter(configurations, autoConfigurationMetadata);
      fireAutoConfigurationImportEvents(configurations, exclusions);
      return configurations.toArray(new String[configurations.size()]);
   }
   catch (IOException ex) {
      throw new IllegalStateException(ex);
   }
}

其通过getCandidateConfigurations 方法,获取配置文件列表:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
      AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
         getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
   Assert.notEmpty(configurations,
         "No auto configuration classes found in META-INF/spring.factories. If you "
               + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

loadFactoryNames会加载所有META-INF下有spring.factories文件的jar包,并根据spring.factories文件中的配置,去加载相应的类。

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   try {
      Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      List<String> result = new ArrayList<String>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
         String factoryClassNames = properties.getProperty(factoryClassName);
         result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
      }
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
            "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

随便找一个spring.factories文件看看:


利用自动配置,加载自定义jar包

看的差不多了,我们可以自己实现一个工具类,然后利用spring boot的自动配置。

自定义工具类

创建一个maven工程:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jzh</groupId>
<artifactId>envutils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</project>
创建一个工具类:

package envutils;

public class EnvUtils {
    private String env = "test";

    public String printEnv() {
        System.out.println(env);
        return env;
    }

    public String getEnv() {
        return env;
    }

    public void setEnv(String env) {
        this.env = env;
    }

}

很简单,pringEnv方法,会返回env值,也就是“test"

接下来,就是在META-INF目录下,创建一个spring.factories文件,默认META-INF目录是没有的,我们手动创建一个:


其中写入:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

envutils.EnvUtils

测试类

同样的,我们再创建一个maven工程

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jzh</groupId>
<artifactId>AotuTest</artifactId>
<version>0.0.1-SNAPSHOT</version>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.0.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>com.jzh</groupId>
        <artifactId>envutils</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
</dependencies>
</project>

其中引用了spring的一些基础包,和我们上面创建的工具类

创建一个测试类:

package com.jzh.autotest;

        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
        import org.springframework.stereotype.Controller;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.ResponseBody;

        import envutils.EnvUtils;

@Controller
@EnableAutoConfiguration
public class AutoTest {

    @Autowired
    private EnvUtils envUtils;

    @RequestMapping("/env")
    @ResponseBody
    public String env() {
        String env = envUtils.printEnv();
        return env;
    }

    public  static void main(String args[]) {
        SpringApplication.run(AutoTest.class,args);
    }
}

运行,发现envUtils注入成功。

调用/env,返回test:


一点拓展

上面简单演示了自动配置。想一想,如果我们工程中,需要更改env值,应该怎么办?env.setEnv肯定有点丑陋了。能否读取文件中的配置呢?当然可以。

在工具上,添加@ConfigurationProperties(prefix="env")注解。

在测试类的配置文件中,添加:env.env=online(你想要设置给env的值)。

注意:当@ConfigurationProperties注解类中属性,添加@NotNull注解时,必须要在配置文件中设置,否则启动会报错。


参考:http://blog.sina.com.cn/s/blog_c90ce4e00103296u.html

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐