当前位置: 移动技术网 > IT编程>开发语言>Java > 浅谈springboot自动配置原理

浅谈springboot自动配置原理

2019年09月06日  | 移动技术网IT编程  | 我要评论

mrp下载基地,春光乍现 右耳,马报

从main函数说起

一切的开始要从springbootapplication注解说起。

@springbootapplication
public class mybootapplication {
  public static void main(string[] args) {
    springapplication.run(mybootapplication.class);
  } 
}


@springbootconfiguration
@enableautoconfiguration
@componentscan
public @interface springbootapplication {
  
}

其中最重要的就是enableautoconfiguration注解,开启自动配置。

@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
@inherited
@autoconfigurationpackage
@import({autoconfigurationimportselector.class})
public @interface enableautoconfiguration {
  string enabled_override_property = "spring.boot.enableautoconfiguration";
  class<?>[] exclude() default {};
  string[] excludename() default {};
}

通过import注解导入autoconfigurationimportselector。在这个类中加载/meta-inf/spring.factories文件的信息,然后筛选出以enableautoconfiguration为key的数据,加载到ioc容器中,实现自动配置功能。

@target(elementtype.type)
@retention(retentionpolicy.runtime)
@documented
@inherited
@import(autoconfigurationpackages.registrar.class)
public @interface autoconfigurationpackage {

}

从表面看就是自动配置包,主要使用了import注解,导入了registrar类。这里registrar类的registerbeandefinitions方法导包,也就是导入当前main函数所在路径的包地址,我这里是com.zhangfei。

怎么自动装配其他n个类

import({autoconfigurationimportselector.class})该注解给当前配置类导入另外n个自动配置类。

这里既然导入n个自动配置类,那么都导入哪些类呢?

//autoconfigurationimportselector实现deferredimportselector接口,而deferredimportselector接口又继承了importselector
public interface importselector {
  string[] selectimports(annotationmetadata var1);
}

autoconfigurationimportselector通过实现接口importselector的selectimports方法返回需要导入的组件,selectimports方法返回一个全类名字符串数组。

主角上场

//autoconfigurationimportselector.java
@override
public string[] selectimports(annotationmetadata annotationmetadata) {
  if (!isenabled(annotationmetadata)) {
    return no_imports;
  }
  autoconfigurationmetadata autoconfigurationmetadata = autoconfigurationmetadataloader.loadmetadata(this.beanclassloader);
  autoconfigurationentry autoconfigurationentry = getautoconfigurationentry(autoconfigurationmetadata,annotationmetadata);
  return stringutils.tostringarray(autoconfigurationentry.getconfigurations());
}

protected autoconfigurationentry getautoconfigurationentry(autoconfigurationmetadata autoconfigurationmetadata,annotationmetadata annotationmetadata) {
  if (!isenabled(annotationmetadata)) {
    return empty_entry;
  }
  annotationattributes attributes = getattributes(annotationmetadata);
  list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes);
  configurations = removeduplicates(configurations);
  set<string> exclusions = getexclusions(annotationmetadata, attributes);
  checkexcludedclasses(configurations, exclusions);
  configurations.removeall(exclusions);
  configurations = filter(configurations, autoconfigurationmetadata);
  fireautoconfigurationimportevents(configurations, exclusions);
  return new autoconfigurationentry(configurations, exclusions);
}

protected list<string> getcandidateconfigurations(annotationmetadata metadata, annotationattributes attributes) {
  list<string> configurations = springfactoriesloader.loadfactorynames(getspringfactoriesloaderfactoryclass(),getbeanclassloader());
  return configurations;
}

这里又开始调用springfactoriesloader.loadfactorynames。
springfactoriesloader.loadfactorynames方法中关键的三步:
(1)从当前项目的类路径中获取所有 meta-inf/spring.factories 这个文件下的信息.
(2)将上面获取到的信息封装成一个 map 返回,enableautoconfiguration为key。
(3)从返回的map中通过刚才传入的 enableautoconfiguration.class参数,获取该 key 下的所有值。

public static list<string> loadfactorynames(class<?> factoryclass, @nullable classloader classloader) {
  string factoryclassname = factoryclass.getname();
  return (list)loadspringfactories(classloader).getordefault(factoryclassname, collections.emptylist());
}

private static map<string, list<string>> loadspringfactories(@nullable classloader classloader) {
  multivaluemap<string, string> result = (multivaluemap)cache.get(classloader);
  if (result != null) {
    return result;
  } else {
    try {
      enumeration<url> urls = classloader != null ? classloader.getresources("meta-inf/spring.factories") : classloader.getsystemresources("meta-inf/spring.factories");
      linkedmultivaluemap result = new linkedmultivaluemap();

      while(urls.hasmoreelements()) {
        url url = (url)urls.nextelement();
        urlresource resource = new urlresource(url);
        properties properties = propertiesloaderutils.loadproperties(resource);
        iterator var6 = properties.entryset().iterator();

        while(var6.hasnext()) {
          entry<?, ?> entry = (entry)var6.next();
          string factoryclassname = ((string)entry.getkey()).trim();
          string[] var9 = stringutils.commadelimitedlisttostringarray((string)entry.getvalue());
          int var10 = var9.length;

          for(int var11 = 0; var11 < var10; ++var11) {
            string factoryname = var9[var11];
            result.add(factoryclassname, factoryname.trim());
          }
        }
      }

      cache.put(classloader, result);
      return result;
    } catch (ioexception var13) {
      throw new illegalargumentexception("unable to load factories from location [meta-inf/spring.factories]", var13);
    }
  }
}

自动配置都有哪些内容呢?

# auto configure
org.springframework.boot.autoconfigure.enableautoconfiguration=\
org.springframework.boot.autoconfigure.admin.springapplicationadminjmxautoconfiguration,\
org.springframework.boot.autoconfigure.aop.aopautoconfiguration,\
org.springframework.boot.autoconfigure.amqp.rabbitautoconfiguration,\
org.springframework.boot.autoconfigure.batch.batchautoconfiguration,\
org.springframework.boot.autoconfigure.cache.cacheautoconfiguration,\
...其他省略

xxxautoconfiguration和xxproperties

在spring.factories文件中看到的都是自动配置类,那么自动配置用到的属性值在那里呢?我们拿出redis为例

@configuration
@conditionalonclass(redisoperations.class) //判断当前项目有没有这个类redisoperations.class
@enableconfigurationproperties(redisproperties.class) //启用配置属性,这里看到了熟悉的xxxproperties
@import({ lettuceconnectionconfiguration.class, jedisconnectionconfiguration.class }) //导入这两个类
public class redisautoconfiguration {

  @bean
  @conditionalonmissingbean(name = "redistemplate")
  public redistemplate<object, object> redistemplate(redisconnectionfactory redisconnectionfactory)
      throws unknownhostexception {
    redistemplate<object, object> template = new redistemplate<>();
    template.setconnectionfactory(redisconnectionfactory);
    return template;
  }

  @bean
  @conditionalonmissingbean
  public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory)
      throws unknownhostexception {
    stringredistemplate template = new stringredistemplate();
    template.setconnectionfactory(redisconnectionfactory);
    return template;
  }
}

 
//这里则保存redis初始化时的属性
@configurationproperties(prefix = "spring.redis")
public class redisproperties {

  private int database = 0;

  private string url;

  private string host = "localhost";

  private string password;

  private int port = 6379;

  private boolean ssl;

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网