返回
快速导航关闭
当前搜索
网站分类
栏目推荐
实用工具
热门标签
子分类:
付费推广技术网 >怎么投放DOU+ » 正文

dou 上热门有用吗,dou加是什么,SpringBoot自动配置原理,以及如何编写自定义的starter

怎么投放DOU+ 更新时间: 发布时间: 怎么投放DOU+归档 最新发布 网站地图

简介

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="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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <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="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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <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="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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <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自动配置的无效;


原文链接:
https://juejin.cn/post/7278238875457355834

转载请注明:文章转载自 http://www.chemrm.com/
本文地址:http://www.chemrm.com/zenmetoufangDOU/23211.html
考高分网交流群

扫一扫加入QQ交流群

和更多志同道合朋友一起交流,分享干货资料!
付费推广技术网客服

扫一扫加客服微信

有疑问请咨询付费推广技术网微信号,在线为您解答!
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 Copyright Your 折1CP备14032708号-6 Rights Reserved. Powered · 创奇学院

ICP备案号:浙ICP备14032708号