springboot 自动配置原理

一、springboot自动配置

1、概述

自动配置的功能是其简化运用的关键技术,思想就是约定大于配置,意思就是一个工程约定必须要有事务功能,要有aop功能,要有mvc功能等,所以springboot在创建工程的时候自动把这些功能所需的类实例化并加入到spring容器了,这个就是约定大于配置,约定了必须要有这些功能。

2、springboot中的SPI机制

java原生的SPI,是一种服务发现机制( Service Provider Interface)。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。 这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI。

2.1、JDK中的SPI机制

public interface Log {
 boolean support(String type); 
 void info(); 
 }

在resources/META-INF/services目录创建文件,文件名称必须跟接口的完整限定名相同。这个接口文件中配置了该接口的所有实现类的完整限定名。然后jdk api 加载配置文件

//jdk api 加载配置文件配置实例
 ServiceLoader<Log> all = ServiceLoader.load(Log.class);

2.2、springboot中的SPI机制
具体流程和上面差不多,在工程的resources下面创建META-INF文件夹,在文件夹下创建spring.factories文件,在文件配置内容如下:

com.ss.yc.spi.Log=\ 
com.ss.yc.spi.Log4j,\
com.ss.yc.spi.Logback,\ 
com.ss.yc.spi.Slf4j

配置的key就是接口完整限定名,value就是接口的各个实现类,用","号隔开。

  • loadFactoryNames方法获取实现了接口的所有类的名称
@Test
public void test() {
List<String> strings = SpringFactoriesLoader
.loadFactoryNames(Log.class, ClassUtils.getDefaultClassLoader());

for (String string : strings) { System.out.println(string); 
 }
}
  • loadFactories方法获取实现了接口的所有类的实例
@Test
public void test1() {
List<String> strings = SpringFactoriesLoader
.loadFactories(Log.class, ClassUtils.getDefaultClassLoader());

for (String string : strings) { System.out.println(string); 
 }
}

2.3、我们以SpringFactoriesLoader.loadFactoryNames(Log.class, ClassUtils.getDefaultClassLoader());方法调用为例分析其源码

可以看到springboot spi是加载了整个工程的jar包和自己工程定义的spring.factories文件的。
其核心代码

Properties properties = PropertiesLoaderUtils.loadProperties(resource);
public static Properties loadProperties(Resource resource) 
throws IOException { 
Properties props = new Properties(); 
//核心代码,把文件包装成properties对象
 fillProperties(props, resource);
  return props;
 }

springboot中的SPI其实就是加载整个工程里面的spring.factories文件,然后把文件里面的内容建立一个key和value的映射关系,只是这个映射关系是一个类型和list的映射关系。

3、@EnableAutoConfiguration

@EnableAutoConfiguration注解是springboot自动配置的核心注解,就是因为有这个注解存在就会把例如事务,缓存,aop,mvc等功能自动导入到springboot工程中,Spring框架提供的各种名字为@Enable开头的Annotation定义,比如@EnableScheduling、@EnableMBeanExport等,@EnableAutoConfiguration的理念和做事方式其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义

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

@Import了一个类,这个AutoConfigurationImportSelector自动配置类

3.1、DeferredImportSelector

DeferredImportSelector该接口是ImportSelector接口的一个子接口,那么它是如何使用的呢?

//
public class DeferredImportSelectorDemo implements DeferredImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata)
{

System.out.println("=====DeferredImportSelectorDemo.selectImports");

return newString[]{DeferredBean.class.getName()};
}


//要返回一个实现了Group接口的类
@Override
public Class<?extendsGroup> getImportGroup(){
return DeferredImportSelectorGroupDemo.class;
}


private static class DeferredImportSelectorGroupDemo implements Group{
List<Entry> list=new ArrayList<>();

//收集需要实例化的类
@Override
public void process(AnnotationMetadata metadata,DeferredImportSelector selector){

System.out.println("=====DeferredImportSelectorGroupDemo.process");
String[] strings=selector.selectImports(metadata);
for(String string:strings){
list.add(newEntry(metadata,string));
}
}

//把收集到的类返回给spring容器
@Override
public Iterable<Entry> selectImports(){
System.out.println("=====DeferredImportSelectorGroupDemo.selectImports");
return list;
}
}
}


//要实例的bean
public class DeferredBean{}

该类必须是@Import导入进来

@Component
//Import虽然是实例化一个类,Import进来的类可以实现一些接口@Import({DeferredImportSelectorDemo.class})
public class ImportBean{}

这样就实现了一个类的实例化。

3.2、EnableAutoConfigurationImportSelector

而AutoConfigurationImportSelector类,这个类就是@EnableAutoConfiguration注解中@Import进来的类,可以看到该类正是实现了DeferredImportSelector接口的。该类其实就是收集spring.factories文件中以@EnableAutoConfiguration类型为key的所有的类,然后把这些类交给spring去实例化,而这些类就是我们说的aop、事务、缓存、mvc等功能的支持类,这就是自动配置的加载原理。

4、@Configuration

就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。

其中@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

5、@ComponentScan

其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描

二、自定义SpringBoot Starter

  1. 引入项目的配置依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
  1. 创建xxxService类,完成相关的操作逻辑
  DemoService.java

@Data
public class DemoService{

    private String str1;

    private String str2;
  1. 定义xxxProperties类,属性配置类,完成属性配置相关的操作,比如设置属性前缀,用于在application.properties中配置
//指定项目在属性文件中配置的前缀为str,即可以在属性文件中通过 str.str1=springboot,就可以改变属性类字段 str1 的值了
@SuppressWarnings("ConfigurationProperties")
@ConfigurationProperties(prefix = "str")
public class DemoProperties {

    public static final String DEFAULT_STR1 = "I know, you need me";

    public static final String DEFAULT_STR2 = "but I also need you";

    private String str1 = DEFAULT_STR1;

    private String str2 = DEFAULT_STR2;
   
   }
 
  1. 定义xxxAutoConfiguration类,自动配置类,用于完成Bean创建等工作
// 定义 java 配置类
@Configuration
//引入DemoService
@ConditionalOnClass({DemoService.class})
// 将 application.properties 的相关的属性字段与该类一一对应,并生成 Bean
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {

    // 注入属性类
    @Autowired
    private DemoProperties demoProperties;

    @Bean
    // 当容器没有这个 Bean 的时候才创建这个 Bean
    @ConditionalOnMissingBean(DemoService.class)
    public DemoService helloworldService() {
        DemoService demoService= new DemoService();
        demoService.setStr1(demoProperties.getStr1());
        demoService.setStr2(demoProperties.getStr2());
        return demoService;
    }

}

  1. 在resources下创建目录META-INF,在 META-INF 目录下创建 spring.factories,在SpringBoot启动时会根据此文件来加载项目的自动化配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.springboot.config.DemoAutoConfiguration 
  1. 其他项目中使用自定义的Starter
<!--引入自定义Starter-->
<dependency>
    <groupId>com.lhf.springboot</groupId>
    <artifactId>spring-boot-starter-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

  1. 编写属性配置文件
#配置自定义的属性信息
str.str1=为什么我的眼里常含泪水
str.str2=那是因为我对你爱的深沉
  1. 写注解使用
@RestController
public class StringController {

      @Autowired
    private DemoService demoService;  //引入自定义Starter中的DemoService 

      @RequestMapping("/")
      public String addString(){
        return demoService.getStr1()+ demoService.getStr2();
    }
}

 
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐