龙空技术网

SpringBoot自动配置原理,以及如何编写自定义的starter

java小悠 631

前言:

目前小伙伴们对“java怎么自动生成代码”大致比较重视,你们都需要学习一些“java怎么自动生成代码”的相关资讯。那么小编在网摘上搜集了一些关于“java怎么自动生成代码””的相关知识,希望看官们能喜欢,朋友们快快来了解一下吧!

简介

Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、RabbitMQ等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。

前置知识

批量注册bean的方式

通过@Bean标注方法的方式,一个个来注册 @CompontentScan的方式:默认的@CompontentScan是无能为力的,默认情况下只会注册@Compontent标注的类 @Import

前两种方式,不适用于频繁变化的场景,且有局限性,所以SpringBoot作为脚手架,自动装配第三方类显然第三种方式更适合;

因此我们首先来了解下@Import注解;

@Import注解

作用:@Import可以用来批量导入需要注册的各种类,如普通的类、配置类,然后完成普通类和配置类中所有bean的注册。

支持如下三种方式:

1、直接导入普通类创建一个普通类

kotlin复制代码package com.doudou.imports; public class DouDouBean { public String print() { return "return doudou bean"; } }
创建一个配置类,导入刚创建的类
less复制代码@Configuration @Import({DouDouBean.class}) public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); DouDouBean douDouBean = context.getBean(DouDouBean.class); System.out.println(douDouBean.print()); } }
控制台输出结果2、配合自定义的 ImportSelector 使用

ImportSelector是一个接口,该接口提供了一个selectImports方法,用来返回全类名数组;可以通过这种方式,动态导入N个bean

创建普通类

kotlin复制代码@Configuration public class DouDouConfig { @Bean public String RedBeanBun() { return "Red Bean Bun"; } @Bean public String OatBag() { return "Oat Bag"; } }
typescript复制代码public class DouDouBean { public String print() { return "return doudou bean"; } }
实现ImportSelector接口
typescript复制代码public class DouDouImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[] { DouDouBean.class.getName(), DouDouConfig.class.getName() }; } }
创建一个配置类,导入实现了ImportSelector接口的类
less复制代码@Configuration @Import({DouDouImportSelector.class}) public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); for (String name : context.getBeanDefinitionNames()) { System.out.println(String.format("%s=%s", name, context.getBean(name))); } } }
控制台输出结果2.1 重点:DeferredImportSelector

装个杯,如果让你实现一个框架,比如希望用户如果实现了自己的配置类,跟SpringBoot自动加载的配置类相同,希望以用户的为准,你会怎么做?

SpringBoot实现上述需要就是使用了DeferredImportSelector;

SpringBoot中的核心功能@EnableAutoConfiguration就是靠DeferredImportSelector来实现的;

DeferredImportSelector是ImportSelector的子接口,所以也能够通过@Import导入,跟ImportSelector不同点在于:

分组 延时导入

创建需要延迟导入的配置类

typescript复制代码@Configuration public class DeferredConfig { @Bean public String dog() { return "dog"; } }
实现DeferredImportSelector接口
typescript复制代码public class DouDeferredImportSelect implements DeferredImportSelector { @Override public Class<? extends Group> getImportGroup() { return DeferredImportSelector.super.getImportGroup(); } @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{ DeferredConfig.class.getName() }; } }
创建一个配置类,导入实现了DeferredImportSelector接口的类
less复制代码@Configuration @Import({ DouDeferredImportSelect.class, DouDouImportSelector.class}) public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); for (String name : context.getBeanDefinitionNames()) { System.out.println(String.format("%s=%s", name, context.getBean(name))); } } }
控制台输出结果,可以发现DeferredConfig最后输出,跟Import导入顺序不一致3、配合 ImportBeanDefinitionRegistrar 使用

ImportBeanDefinitionRegistrar也是一个接口,支持手动的注册bean到容器当中

创建需要被手动注册到bean容器当中的类

typescript复制代码public class DouDefinitionRegistrarBean { public String pig() { return "pig"; } }
实现ImportBeanDefinitionRegistrar接口
java复制代码public class DouDefinitionRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition definition = new RootBeanDefinition(DouDefinitionRegistrarBean.class); registry.registerBeanDefinition("douDefinitionRegistrarBean", definition); } }
创建配置类,导入实现了ImportBeanDefinitionRegistrar接口的类
less复制代码@Configuration @Import({ DouDeferredImportSelect.class, DouDouImportSelector.class, DouDefinitionRegistrar.class}) public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); for (String name : context.getBeanDefinitionNames()) { System.out.println(String.format("%s=%s", name, context.getBean(name))); } } }
控制台输出结果,可以发现DouDefinitionRegistrarBean类被注入@Condition注解

@Condition注解能够实现在满足特定条件下配置类才生效

创建一个配置类,实现Condition接口,改接口仅提供了一个matches方法,返回值为boolean类型,该方法返回true,代表配置类生效;反之,不生效;

typescript复制代码@Configuration public class DouDouConditionBean implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return true; } }
创建另一个配置类,依赖上述配置类matches方法来判断是否需要生效
less复制代码@Configuration @Conditional({DouDouConditionBean.class}) public class ConditionBeanConfig { }
创建配置类,测试ConditionBeanConfig配置类是否被注入
less复制代码@Configuration @ComponentScan(basePackages = {"com.doudou.condition"}) public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); for (String name : context.getBeanDefinitionNames()) { System.out.println(String.format("%s=%s", name, context.getBean(name))); } } }
控制台结果输出,ConditionBeanConfig类生效,被成功注入到IOC容器

拓展注解

@Conditional扩展注解

(判断是否满足当前指定条件)

@ConditionalOnJava

系统的java版本是否符合要求

@ConditionalOnBean

容器中存在指定Bean

@ConditionalOnMissingBean

容器中不存在指定Bean

@ConditionalOnExpression

满足SpEL表达式指定

@ConditionalOnClass

系统中有指定的类

@ConditionalOnMissingClass

系统中没有指定的类

@ConditionalOnSingleCandidate

容器中只有一个指定的Bean,或者这个Bean是首选Bean

@ConditionalOnProperty

系统中指定的属性是否有指定的值

@ConditionalOnResource

类路径下是否存在指定资源文件

@ConditionalOnWebApplication

当前是web环境

@ConditionalOnNotWebApplication

当前不是web环境

@ConditionalOnJndi

JNDI存在指定项

1、SpringBoot自动配置分析

注意:本篇博文分析,基于SpringBoot 2.5.2版本

自动配置原理流程图

上述识别到需要自动配置的配置类,被生成BeanDefinitionMap,通过BeanFactory生成具体的Bean,该部分内容由Spring IOC完成,非SpringBoot做的事,所以一笔带过,主要让大家别太懵逼;

具体源码分析,先从启动类入手:

@SpringBootApplication注解

该注解标注该类是SpringBoot的主配置类

less复制代码@Target(ElementType.TYPE) // 设置该注解可以标注在什么地方 @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited // 是否会被继承 @SpringBootConfiguration // 表示这是SpringBoot的主配置类 @EnableAutoConfiguration // 开启自动配置 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {

由此可见,最核心的注解还是 @EnableAutoConfiguration,展开分析下这个注解都由什么部分组成

less复制代码@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {

可以发现,核心的就两个注解 @AutoConfigurationPackage、 @Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage

首先分析下 @AutoConfigurationPackage

less复制代码@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {

看到 @Import(AutoConfigurationPackages.Registrar.class) 这个东西,是不是很熟悉了,就是帮我们指定哪些bean需要被注册到Spring IOC容器中;

通过调试,我们可以看到,他将我们启动类所在的目录注册到Spring IOC容器中去了,换句话说,如果没有 @AutoConfigurationPackage注解,我们自己项目的类不会自动被扫描到SpringIOC容器中的;所以,总结一下,该注解主要起到了将启动类所在目录下的包扫描到SpringIOC容器中;

@Import(AutoConfigurationImportSelector.class)

再分析下 @Import(AutoConfigurationImportSelector.class)

kotlin复制代码public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

该类实现了DeferredImportSelector接口,若getImportGroup()方法返回值不为null,将会调用getImportGroup()方法返回值的process()方法;

具体堆栈如下:

makefile复制代码process:429, AutoConfigurationImportSelector$AutoConfigurationGroup (org.springframework.boot.autoconfigure) getImports:879, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation) processGroupImports:809, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation) process:780, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation) parse:193, ConfigurationClassParser (org.springframework.context.annotation) processConfigBeanDefinitions:331, ConfigurationClassPostProcessor (org.springframework.context.annotation) postProcessBeanDefinitionRegistry:247, ConfigurationClassPostProcessor (org.springframework.context.annotation) invokeBeanDefinitionRegistryPostProcessors:311, PostProcessorRegistrationDelegate (org.springframework.context.support) invokeBeanFactoryPostProcessors:112, PostProcessorRegistrationDelegate (org.springframework.context.support) invokeBeanFactoryPostProcessors:746, AbstractApplicationContext (org.springframework.context.support) refresh:564, AbstractApplicationContext (org.springframework.context.support) refresh:145, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context) refresh:754, SpringApplication (org.springframework.boot) refreshContext:434, SpringApplication (org.springframework.boot) run:338, SpringApplication (org.springframework.boot) run:1343, SpringApplication (org.springframework.boot) run:1332, SpringApplication (org.springframework.boot) main:10, SpringbootApplication (com.doudou)

详细分析下org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process方法

kotlin复制代码public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { @Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // 核心代码:获取需要自动加载的配置类 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } } }

深度分析下org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry方法

scss复制代码protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // 获取所有jar包 META-INF/spring.factories 下的key为EnableAutoConfiguration的配置类 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 配置类去重 configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 过滤出满足条件的配置类,过滤规则:META-INF/spring.factories key为AutoConfigurationImportFilter的value值 configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }

深度分析下核心方法org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations

typescript复制代码protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // getSpringFactoriesLoaderFactoryClass方法比较简单,就是返回EnableAutoConfiguration.class List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); return configurations; }

分析下org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames

less复制代码public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 这里获取到的内容为EnableAutoConfiguration String factoryTypeName = factoryType.getName(); // 过滤出key为EnableAutoConfiguration的配置类 return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }

分析下org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories

typescript复制代码private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = (Map)cache.get(classLoader); if (result != null) { return result; } else { Map<String, List<String>> result = new HashMap(); try { // 可以看到,通过类加载机制,获取到所有jar包下META-INF/spring.factories的配置信息 Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories"); ... }); cache.put(classLoader, result); return result; } catch (IOException var14) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14); } } }

上述多次提到key EnableAutoConfiguration、AutoConfigurationImportFilter,你可能没概念,去到具体文件,截图一下,你就明白了;

至此,需要自动加载到Spring IOC的配置类均找到了,接下来交给Spring IOC即可;

2、如何查看哪些配置类被自动加载至Spring容器中

在项目appilication.yml追加以下一条配置项,这样就能够在控制台输出哪些配置类被自动配置;

lua复制代码debug: true

控制台输出结果(内容较多,我拣选了核心的出来):

sql复制代码positive matches: // 以下代表被自动配置的配置类信息 ----------------- AopAutoConfiguration matched: - @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition) AopAutoConfiguration.ClassProxyingConfiguration matched: - @ConditionalOnMissingClass did not find unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition) - @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition) DispatcherServletAutoConfiguration matched: - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet' (OnClassCondition) - found 'session' scope (OnWebApplicationCondition) DispatcherServletAutoConfiguration.DispatcherServletConfiguration matched: - @ConditionalOnClass found required class 'javax.servlet.ServletRegistration' (OnClassCondition) - Default DispatcherServlet did not find dispatcher servlet beans (DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition) Negative matches: // 以下代表条件缺失,没有被自动配置的配置类 ----------------- ActiveMQAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition) AopAutoConfiguration.AspectJAutoProxyingConfiguration: Did not match: - @ConditionalOnClass did not find required class 'org.aspectj.weaver.Advice' (OnClassCondition) ArtemisAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition) BatchAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class 'org.springframework.batch.core.launch.JobLauncher' (OnClassCondition) Exclusions: // 哪些类被排除 ----------- None
3、单独分析一个满足自动装配条件的类

以HttpEncodingAutoConfiguration类为例

less复制代码@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ServerProperties.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { private final Encoding properties; public HttpEncodingAutoConfiguration(ServerProperties properties) { this.properties = properties.getServlet().getEncoding(); } @Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE)); return filter; } }

@Configuration(proxyBeanMethods = false)

@EnableConfigurationProperties(ServerProperties.class) 该注解表示 该配置类对应的配置文件对应的class类,并且将ServerProperties对应的javaBean加入到Spring IOC容器中

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) web环境下生效

@ConditionalOnClass(CharacterEncodingFilter.class) CharacterEncodingFilter类存在,配置类则生效

@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)

配置中是否存在某个配置项为server.servlet.encoding,如果不存在,则能够匹配;如果matchIfMissing为false,则需要存在才能够匹配;

4、分析下配置项和配置类如何互相绑定

以ServerProperties为例

swift复制代码@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { /** * Server HTTP port. */ private Integer port; /** * Network address to which the server should bind. */ private InetAddress address; }

ServerProperties是通过@ConfigurationProperties将配置文件与该类进行绑定;

因此你在application.properties或application.yml中给服务绑定端口号时,实际上就给ServerProperties属性赋值;

yaml复制代码server: port: 8080
5、自定义starter

简介

SpringBoot最强大的功能就是把我们常用的场景抽取成了一个个starter(场景启动器),我们引入SpringBoot为我们提供的这些场景启动器,我们再进行少量的配置就能完成我们的功能。即使是这样,SpringBoot也不能囊括我们所有的场景,往往我们需要自定义starter,来满足我们需要的功能;

如何编写starter

我们参考spring-boot-starter

发现是一个空的jar包,分析下他的pom.xml文件

xml复制代码<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.5.2</version> <scope>compile</scope> </dependency>

发现它依赖了spring-boot-autoconfigure,而spring-boot-autoconfigure包含了starter的配置及代码

因此,梳理出规则如下:

启动器(starter)是一个空的jar文件,仅仅提供辅助性依赖管理,这些依赖可能用于自动装配或其他类库。需要专门写一个类似spring-boot-autoconfigure的配置模块用的时候只需要引入启动器starter,就可以使用自动配置了

命名规范

官方命名空间

前缀:spring-boot-starter-模式:spring-boot-starter-模块名举例:spring-boot-starter-web、spring-boot-starter-jdbc

自定义命名空间

后缀:-spring-boot-starter模式:模块-spring-boot-starter举例:mybatis-spring-boot-starter开始编写自己的starter

makefile复制代码创建一个父工程,和2个Module: 1、doudou-parent (父工程) 2、doudou-spring-boot-starter (子模块,依赖doudou-spring-boot-starter-autoconfigure) 3、doudou-spring-boot-starter-autoconfigure

项目结构概览:

doudou-parent:

xml复制代码<?xml version="1.0" encoding="UTF-8"?> <project xmlns="; xmlns:xsi="; xsi:schemaLocation=" ;> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.doudou</groupId> <artifactId>doudou-parent</artifactId> <version>0.0.1-SNAPSHOT</version> <name>doudou-parent</name> <description>doudou-parent</description> <packaging>pom</packaging> <properties> <java.version>1.8</java.version> </properties> <dependencies> </dependencies> <modules> <module>doudou-spring-boot-starter</module> <module>doudou-spring-boot-starter-autoconfigure</module> </modules> </project>

doudou-spring-boot-starter:

xml复制代码<?xml version="1.0" encoding="UTF-8"?> <project xmlns="; xmlns:xsi="; xsi:schemaLocation=" ;> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.doudou</groupId> <artifactId>doudou-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>doudou-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> <name>doudou-spring-boot-starter</name> <description>doudou-spring-boot-starter</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.doudou</groupId> <artifactId>doudou-spring-boot-starter-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> </project>

doudou-spring-boot-starter-autoconfigure:

xml复制代码<?xml version="1.0" encoding="UTF-8"?> <project xmlns="; xmlns:xsi="; xsi:schemaLocation=" ;> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.doudou</groupId> <artifactId>doudou-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>doudou-spring-boot-starter-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> <name>doudou-spring-boot-starter-autoconfigure</name> <description>doudou-spring-boot-starter-autoconfigure</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project>

DoudouAutoConfiguration:

less复制代码@Configuration @EnableConfigurationProperties(DouDouProperties.class) @ConditionalOnProperty(value = "doudou.name") public class DoudouAutoConfiguration { }

DouDouProperties:

typescript复制代码@ConfigurationProperties("doudou") public class DouDouProperties { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

DouDouController:

kotlin复制代码@RestController public class DouDouController { @Autowired private DouDouProperties douDouProperties; @RequestMapping("/test/doudou/custom/starter") public String indexController() { return douDouProperties.getName(); } }

spring.factories:

ini复制代码org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.doudou.configuration.DoudouAutoConfiguration
外部程序依赖自定义的starter

额外搭建一个SpringBoot工程,依赖doudou-spring-boot-starter

备注:这个搭建比较简单,我主要展示效果

第一步:增加pom依赖

xml复制代码<dependency> <groupId>com.doudou</groupId> <artifactId>doudou-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>

其次在application.yml增加配置:

yaml复制代码doudou: name: doudou

启动服务,访问starter提供的接口

6、总结

如何在面试中回答SpringBoot自动配置原理?

我会从核心注解入手,分析原理:

1、@SpringBootApplication注解标注该工程是一个SpringBoot工程,该注解包含两个核心注解@SpringBootConfiguration、@EnableAutoConfiguration;

2、@SpringBootConfiguration本质就是@Configuration,表示是一个配置类;

3、@EnableAutoConfiguration注解,包含两个核心注解@AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class);

4、@AutoConfigurationPackage注解核心在于@Import(AutoConfigurationPackages.Register.class),该Register类实现了ImportBeanDefinitionRegister接口,我们知道实现该接口,实际上就是手动注册BeanDefinition至Spring IOC容器中,SpringBoot通过@Import(AutoConfigurationPackages.Register.class)实现,将启动类所在目录注册到Spring IOC容器中;这样,我们项目定义的class就能够被Spring IOC注册成bean;

5、@Import(AutoConfigurationImportSelector.class)注解,其中AutoConfigurationImportSelector类实现了DeferredImportSelector接口,DeferredImportSelector接口是ImportSelector的子接口,我们知道实现了ImportSelector接口,主要目的在于手动导入类将其注册成为bean,而DefeeredImportSelector多了一个延时导入的特性,主要目的在于延时导入各种starter jar包下META-INF/spring.factories里面key为EnableAutoConfiuration的配置类,再按照一定过滤规则,过滤出最终需要自动配置的配置类;为什么要延时导入,因为如果外部自定义的配置类跟SpringBoot自动配置的一样,那么注册外部自定义的配置类至Spring IOC容器中,SpringBoot自动配置的无效;

原文链接:

标签: #java怎么自动生成代码