来吧,SpringBoot的自动配置原理都在这里了

💗推荐阅读文章💗

  • 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》
  • 🌺MySQL系列🌺👉2️⃣《MySQL系列教程》
  • 🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》
  • 🌻SSM框架系列🌻👉4️⃣《SSM框架系列教程》

🎉本博客知识点收录于🎉👉🚀《SpringBoot系列教程》🚀—>✈️01【SpringBoot快速入门、yml语法、自动配置、整合框架】✈️

文章目录

  • 四、SpringBoot原理
    • 4.1 起步依赖原理
      • 4.1.1 版本锁定
      • 4.1.2 起步依赖
    • 4.2 自动配置原理
      • 4.2.1 @SpringBootApplication源码
      • 4.2.2 @SpringBootConfiguration源码
      • 4.2.3 @EnableAutoConfiguration源码
        • 1)Registrar导入器
        • 2)AutoConfigurationImportSelector导入器
      • 4.2.4 [@ComponentScan ](/ComponentScan )
        • 1)AutoConfigurationExcludeFilter
        • 2)TypeExcludeFilter
        • 3)自定义排除器
    • 4.3 自动配置类
      • 4.3.1 自动配置类说明
      • 4.3.2 @Conditional派生注解
      • 4.3.3 自定义自动配置类
    • 4.4 属性配置类
      • 4.4.1 属性配置类说明
      • 4.4.2 自定义属性配置类
    • 4.5 自定义场景启动器
      • 4.5.1 场景启动器包含内容
      • 4.5.2 搭建自动配置工程
        • 4.5.2.1 依赖
        • 4.5.2.2 自动配置类
        • 4.5.2.3 属性配置类
        • 4.5.2.4 具体配置的类
        • 4.5.2.5 spring.factories
      • 4.5.3 搭建测试工程
        • 4.5.3.1 依赖
        • 4.5.2.2 Controller
        • 4.5.2.3 application.yml
        • 4.5.2.4 引导类

四、SpringBoot原理

4.1 起步依赖原理

4.1.1 版本锁定

SpringBoot项目都是继承于spring-boot-starter-parent工程的,我们点进去看看这个工程中配置了什么?

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.0.1.RELEASE</version><relativePath>../../spring-boot-dependencies</relativePath>
</parent>...
<properties><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><resource.delimiter>@</resource.delimiter><maven.compiler.source>${java.version}</maven.compiler.source><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.target>${java.version}</maven.compiler.target>
</properties><pluginManagement><plugins><plugin><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-maven-plugin</artifactId><version>${kotlin.version}</version></plugin>...</plugins>
</pluginManagement>

我们发现spring-boot-starter-parent工程是继承于spring-boot-dependencies工程,在spring-boot-starter-parent工程中锁定了部分插件使用的版本:

我们继续点进spring-boot-dependencies工程:

<properties><activemq.version>5.15.3</activemq.version><antlr2.version>2.7.7</antlr2.version><appengine-sdk.version>1.9.63</appengine-sdk.version><artemis.version>2.4.0</artemis.version><aspectj.version>1.8.13</aspectj.version><assertj.version>3.9.1</assertj.version><atomikos.version>4.0.6</atomikos.version><bitronix.version>2.1.4</bitronix.version>.....
</properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot</artifactId><version>2.0.1.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId><version>2.0.1.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test-autoconfigure</artifactId><version>2.0.1.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId><version>2.0.1.RELEASE</version></dependency>....</dependencies>
</dependencyManagement>

从上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了(解决了版本冲突问题)。

这些版本的搭配经过了SpringBoot官方团队的测试,我们在使用过程中,采用SpringBoot搭配的版本匹配,就避免了版本冲突、不稳定等因素;

4.1.2 起步依赖

在继承的父工程中我们并没有看到依赖的引入(只是版本的锁定),真正的依赖是在我们引入场景启动器时引入的,我们点击spring-boot-starter-web场景启动器查看:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.0.1.RELEASE</version><scope>compile</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId><version>2.0.1.RELEASE</version><scope>compile</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><version>2.0.1.RELEASE</version><scope>compile</scope></dependency><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>6.0.9.Final</version><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.0.5.RELEASE</version><scope>compile</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.0.5.RELEASE</version><scope>compile</scope></dependency>
</dependencies>

我们发现在spring-boot-starter-web依赖中引入了大量web场景需要的依赖,如Spring-web、Spring-webmvc、json转换、tomcat等,SpringBoot中的场景启动器(starter)对场景的依赖进行"打包",这样以后我们的项目只需要引入对应场景的starter即可;

4.2 自动配置原理

我们在前面说到过,SpringBoot所有的配置基于约定理念,并不是不需要配置,而是SpringBoot在项目启动时帮我们配置了,所有的配置基于约定的参数已经配置好了;那么SpringBoot是如何做到"自动配置"的呢?

我们点击标注在引导类上的@SpringBootApplication注解:

@SpringBootApplication          // 标注这是一个引导类
public class HelloApplication {public static void main(String[] args) {SpringApplication.run(HelloApplication.class);}
}

4.2.1 @SpringBootApplication源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {...}
  • 1)@Target({ElementType.TYPE}):元注解,标注这个注解只能在类上使用
  • 2)@Retention(RetentionPolicy.RUNTIME):元注解,标注这个注解的生命周期是RUNTIME,可以在运行时解析到这个注解
  • 3)@Documented :元注解,表明这个注解应该被 javadoc工具记录
  • 4)**@Inherited **** **:元注解,注解在类上面时,子类会自动继承此注解

4.2.2 @SpringBootConfiguration源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

上面三个都是元注解

**@Configuration **** **:Spring提供的注解,表示这是一个配置类

也就是说标注了@SpringBootApplication、@SpringBootConfiguration等注解的类都可以当做一个配置类使用

4.2.3 @EnableAutoConfiguration源码

@EnableAutoConfiguration是SpringBoot自动配置的核心;

@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 {};
}

标注了两个注解:**@AutoConfigurationPackage****@Import({AutoConfigurationImportSelector.class})**

1)Registrar导入器
  • 1)@AutoConfigurationPackage:在该注解上标注了@Import注解,导入了一个Registrar导入器,开启包的自动导入配置,自动导入标注了该注解的类的同级(包括子级、孙级)包下的所有的Bean;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

往容器中注册了一个Registrar,查看Registrar:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 配置包注册(注册标注了AutoConfigurationPackage注解的类所在的全路径下面的所有类)register(registry, new PackageImport(metadata).getPackageName());}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImport(metadata));}}

2)AutoConfigurationImportSelector导入器

在@EnableAutoConfiguration还标注了一个@Import(AutoConfigurationImportSelector.class),导入了一个AutoConfigurationImportSelector类;

查看继承体系:

发现AutoConfigurationImportSelector实现接口ImportSelector,在导入时,一定会调用**selectImports**进行Bean配置;

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AnnotationAttributes attributes = getAttributes(annotationMetadata);// 给容器中导入一批组件(xxxAutoConfiguration)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);// 其他全部导入到IOC容器给中return StringUtils.toStringArray(configurations);
}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;
}

configurations值:

  • SpringFactoriesLoader源码
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?// META-INF/spring.factoriesclassLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));result.addAll((String) entry.getKey(), factoryClassNames);}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}
}

4.2.4 @ComponentScan

@SpringBootApplication注解上除了标注了上面两个注解外,还标注了一个@ComponentScan注解,其内容如下:

@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

添加了两个排除过滤器,分别是TypeExcludeFilterAutoConfigurationExcludeFilter;两个排除过滤器都继承与TypeFilter接口,并且是一个函数式接口;IOC容器启动时,会将被扫描的Bean的元数据信息传递到该match,由该方法的返回值来决定是否要排除这个Bean;

@FunctionalInterface
public interface TypeFilter {/*** 根据match方法的返回值来决定当前Bean是否要注册到IOC容器* @param metadataReader: 这个注解标注的目标类的元数据读取器* @param metadataReaderFactory: 获取元数据读取器的工厂*/boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException;}
1)AutoConfigurationExcludeFilter
  • AutoConfigurationExcludeFilter
package org.springframework.boot.autoconfigure;import java.io.IOException;
import java.util.List;import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;/*** A {@link TypeFilter} implementation that matches registered auto-configuration classes.** @author Stephane Nicoll* @since 1.5.0*/
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {private ClassLoader beanClassLoader;private volatile List<String> autoConfigurations;@Overridepublic void setBeanClassLoader(ClassLoader beanClassLoader) {this.beanClassLoader = beanClassLoader;}// 作用: 当前这个类不能是一个配置类 并且 也不能是一个自动配置类(不能写在META-INF/spring.factroies文件中)@Overridepublic boolean match(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory) throws IOException {return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);}// 当前这个类上是否标注了Configuration注解private boolean isConfiguration(MetadataReader metadataReader) {return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());}// 当前这个类是否是一个自动配置类private boolean isAutoConfiguration(MetadataReader metadataReader) {return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());}protected List<String> getAutoConfigurations() {if (this.autoConfigurations == null) {this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);}return this.autoConfigurations;}
}

说明:因为自动配置类是SpringBoot规定的加载规则,默认规则为加载每个jar包下的**/META-INF/spring.factories**文件中的**org.springframework.boot.autoconfigure.EnableAutoConfiguration**指定的自动配置类;

如果我们自己想要自定义自动配置类让其生效,有两种方法:

  • 1)在类上只标注一个@Configuration注解,没有写在/META-INF/spring.factories文件中,不会被排除,但也被默认的规则扫描到了(Registrar导入器);
  • 2)在类上只标注一个@Configuration注解,写在/META-INF/spring.factories文件中,被排除(但被SpringBoot扫描到了),这是SpringBoot推荐的写法,标准的自动配置类

Tips:自动配置类和普通的配置类的作用都是一模一样的,只不过自动配置类一般用于加载某些场景,而且是写在/META-INF/spring.factories中的;


2)TypeExcludeFilter
  • TypeExcludeFilter

package org.springframework.boot.context;import java.io.IOException;
import java.util.Collection;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}// 作用: 获取IOC容器中,所有有关于TypeExcludeFilter的后代类,然后执行这些后代类的match方法@Overridepublic boolean match(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory) throws IOException {if (this.beanFactory instanceof ListableBeanFactory&& getClass() == TypeExcludeFilter.class) {// 从IOC容器中获取有关于TypeExcludeFilter的所有Bean(包括后代类)Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();for (TypeExcludeFilter delegate : delegates) {// 调用TypeExcludeFilter后代类的所有match方法if (delegate.match(metadataReader, metadataReaderFactory)) {return true;}}}return false;}@Overridepublic boolean equals(Object obj) {throw new IllegalStateException("TypeExcludeFilter " + getClass() + " has not implemented equals");}@Overridepublic int hashCode() {throw new IllegalStateException("TypeExcludeFilter " + getClass() + " has not implemented hashCode");}
}

TypeExcludeFilter过滤器是SpringBoot提供给我们用的排除过滤器,我们可以根据一些条件来决定是否要过滤某些Bean;

3)自定义排除器
  • 自定义排除器:
package com.dfbz;import com.dfbz.controller.HelloController;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;import java.io.IOException;/*** @author lscl* @version 1.0* @intro:*/
//@Component          // 不能用这种方式注册到IOC容器,因为排除器在容器扫描时生效,这个时候连都还没加载
public class MyTypeExcludeFilter extends TypeExcludeFilter {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {// 排除HelloControllerreturn metadataReader.getClassMetadata().getClassName().equals(HelloController.class.getName());}
}

排除器(MyTypeExcludeFilter)需要注册到IOC容器中才会生效,但不能使用**@Component **** 注册,因为排除器是在扫描组件时生效,这个时候可能连排除器上标注的@Componen注解还未扫描; **

  • 编写初始化器:
package com.dfbz;import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;/*** @author lscl* @version 1.0* @intro:*/
public class MyApplicationContextInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {// 往IOC容器中注册一个单例Bean(在IOC容器初始化后,@ComponentScan组件扫描之前执行)applicationContext.getBeanFactory().registerSingleton("myTypeExcludeFilter",new MyTypeExcludeFilter());}
}
  • 让初始化器生效:

resources目录下编写META-INF/spring.factories,内容如下:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.dfbz.MyApplicationContextInitializer

4.3 自动配置类

4.3.1 自动配置类说明

通过刚刚的自动配置原理我们发现,SpringBoot在启动时,就加载了非常多的一些配置类(xxxAutoConfiguration),这些配置类中配置了非常多的信息,包括根据条件导入一些Bean、配置一些属性、绑定一些配置

我们之所以能够启动SpringBoot环境就配置好了大量的环境(SpringMVC环境/MyBatis环境/JPA环境等)都是因为这些自动配置类在SpringBoot启动时帮我们在IOC容器里注册了大量的Bean;

Tips:配置类的地址在**META-INF/spring.factories**文件中**org.springframework.boot.autoconfigure.EnableAutoConfiguration**key所指定的内容;

DispatcherServletAutoConfiguration配置类举例:

// 配置顺序(数字越小,越优先加载,负数也可以)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)	// 标注这是一个配置类
@Configuration// 如果是web环境才配置此类
@ConditionalOnWebApplication(type = Type.SERVLET)// 如果系统中有DispatcherServlet类才配置此类(导入了这个依赖就配置这个类)
@ConditionalOnClass(DispatcherServlet.class)// 在ServletWebServerFactoryAutoConfiguration类加载之后再加载此类
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
// 开启属性配置
@EnableConfigurationProperties(ServerProperties.class)
public class DispatcherServletAutoConfiguration {public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";// 标注这是一个配置类@Configuration// 满足DefaultDispatcherServletCondition的matches方法时配置该类@Conditional(DefaultDispatcherServletCondition.class)// 容器中有ServletRegistration类时配置该类@ConditionalOnClass(ServletRegistration.class)// 开启配置属性@EnableConfigurationProperties(WebMvcProperties.class)protected static class DispatcherServletConfiguration {private final WebMvcProperties webMvcProperties;public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {this.webMvcProperties = webMvcProperties;}// 往IOC容器中配置一个DispatcherServlet类@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet() {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());return dispatcherServlet;}@Bean@ConditionalOnBean(MultipartResolver.class)@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}}@Configuration@Conditional(DispatcherServletRegistrationCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)@Import(DispatcherServletConfiguration.class)protected static class DispatcherServletRegistrationConfiguration {private final ServerProperties serverProperties;private final WebMvcProperties webMvcProperties;private final MultipartConfigElement multipartConfig;public DispatcherServletRegistrationConfiguration(ServerProperties serverProperties, WebMvcProperties webMvcProperties,ObjectProvider<MultipartConfigElement> multipartConfigProvider) {this.serverProperties = serverProperties;this.webMvcProperties = webMvcProperties;this.multipartConfig = multipartConfigProvider.getIfAvailable();}@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(dispatcherServlet,this.serverProperties.getServlet().getServletMapping());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());if (this.multipartConfig != null) {registration.setMultipartConfig(this.multipartConfig);}return registration;}}
}

4.3.2 @Conditional派生注解

我们已经知道了自动配置类的加载规则,只要配置在META-INF/spring.factories文件的org.springframework.boot.autoconfigure.EnableAutoConfigurationkey中的全类名,该配置类即可被加载,但有些时候我们并不希望这些配置类能够被立即加载,而是需要符合某些条件时这些配置类才会被加载;

SpringBoot内置了非常多的条件判断注解,这些注解可以帮助我们在符合条件的清空下,该配置类/方法才会生效;这些注解我们统一称为派生注解;为@ConditionalXxx

  • @Conditional表格:
@Conditional扩展注解作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean容器中不存在指定Bean;
@ConditionalOnExpression满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass系统中没有指定的类
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty系统中指定的属性是否有指定的值
@ConditionalOnResource类路径下是否存在指定资源文件
@ConditionalOnWebApplication当前是web环境
@ConditionalOnNotWebApplication当前不是web环境
@ConditionalOnJndi JNDI存在指定项

4.3.3 自定义自动配置类

  • 自动配置类的加载说明:
    • 1)必须是一个配置类
    • 2)必须配置在META-INF/spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfigurationkey中;
    • 3)可能还会包含一些派生注解(@ConditionalXxx)
package com.dfbz.config;import com.dfbz.controller.HelloController;
import com.dfbz.entity.City;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
import org.springframework.boot.system.JavaVersion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author lscl* @version 1.0* @intro:*/
@Configuration
@ConditionalOnJava(JavaVersion.EIGHT)           // Java版本必须是1.8
@ConditionalOnBean(HelloController.class)       // IOC容器中必须存在HelloController这个对象
@ConditionalOnResource(resources = "application.properties")        // 类路径下必须存在application.properties
public class MyAutoConfiguration {@Beanpublic City city() {return new City();}
}

4.4 属性配置类

4.4.1 属性配置类说明

自动配置类中通常会绑定(通过EnableConfigurationProperties开启)一些属性配置类(xxxProperties.class),这些属性配置类通过**@ConfigurationProperties**注解标识;

在自动配置类中根据条件配置了大量的Bean,而这些Bean上面大都开启(@EnableConfigurationProperties)了属性配置类(xxxProperties.class),这些属性配置类是SpringBoot"基于约定配置"的保障;

查看ServerProperties:

  • @ConfigurationProperties:与application.yml/properties配置文件进行绑定;
// 与配置文件进行绑定
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {private Integer port;private InetAddress address;@NestedConfigurationPropertyprivate final ErrorProperties error = new ErrorProperties();private Boolean useForwardHeaders;private String serverHeader;}

4.4.2 自定义属性配置类

  • 自定义属性配置类:
package com.dfbz.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @author lscl* @version 1.0* @intro:*/
@Data                       // 提供get/set/toString...
@AllArgsConstructor         // 有参构造
@NoArgsConstructor          // 无参构造@Component                              // 必须要放入IOC容器才能使用@ConfigurationProperties注解
@ConfigurationProperties(prefix = "com.dfbz.book")          // 绑定的前缀为: com.dfbz.book
public class Book {private String name;private Double price;
}
  • application.yml:
com:dfbz:book:name: 《Java入门到精通》price: 38.8
  • DemoController:
package com.dfbz.controller;import com.dfbz.entity.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;/*** @author lscl* @version 1.0* @intro:*/
@RestController
public class DemoController {@Autowiredprivate Book book;@GetMapping("/getBook")@ResponseBodypublic Book getBook() {return book;}
}

访问:http://localhost:8080/getBook:

也可以使用@EnableConfigurationProperties注解来指定要开启某个类上面的属性配置;这样这个类就不需要被放入IOC容器也依旧能与配置文件进行绑定;

package com.dfbz;import com.dfbz.entity.Book;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;/*** @author lscl* @version 1.0* @intro:*/
@SpringBootApplication
@EnableConfigurationProperties(Book.class)          // 开启Book类的属性配置功能
public class HelloApplication {public static void main(String[] args) {SpringApplication.run(HelloApplication.class);}
}

注释Book类中的@Component注解;

重启服务器,再次访问:http://localhost:8080/getBook发现配置依旧可以;

4.5 自定义场景启动器

4.5.1 场景启动器包含内容

1)自动配置类;

xxxAutoConfiguration

2)开启属性配置,并绑定属性配置类;

xxxProperties

3)加入到META-INF/spring.factories中;

4.5.2 搭建自动配置工程

自动配置场景启动器名称规范:{name}-springboot-starter-autoconfigurer

4.5.2.1 依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.dfbz</groupId><artifactId>02-mystarter-springboot-starter-autoconfigurer</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><!--springboot的启动器,包含所有starter的基本配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies></project>
4.5.2.2 自动配置类
package com.mystarter.autoconfig;import com.mystarter.service.HelloService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author lscl* @version 1.0* @intro: 自动配置类*/
// 标注这是一个配置类
@Configuration
// 如果是web环境才配置此类
@ConditionalOnWebApplication
// 开启配置属性
@EnableConfigurationProperties(MyStarterProperties.class)
public class MyStarterAutoConfiguration {@Beanpublic HelloService helloService(){return new HelloService();}
}
4.5.2.3 属性配置类
package com.mystarter.properties;import org.springframework.boot.context.properties.ConfigurationProperties;/*** @author lscl* @version 1.0* @intro: 属性配置类,绑定配置文件,指定前缀*/
@ConfigurationProperties(prefix = "mystarter")
public class MyStarterProperties {private String text;public String getText() {return text;}public void setText(String text) {this.text = text;}
}
4.5.2.4 具体配置的类
package com.mystarter.service;import org.springframework.beans.factory.annotation.Value;/*** @author lscl* @version 1.0* @intro:*/
public class HelloService {@Autowiredprivate MyStarterProperties properties;public String sayHello() {return properties.getText();}
}
4.5.2.5 spring.factories

/MATA-INF/目录下新建spring.factories文件;

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.mystarter.autoconfig.MyStarterAutoConfiguration

4.5.3 搭建测试工程

4.5.3.1 依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>03-MyStarterTest</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.1.RELEASE</version></parent><dependencies><!--导入我们自定义的starter--><dependency><groupId>com.dfbz</groupId><artifactId>02-mystarter-springboot-starter-autoconfigurer</artifactId><version>1.0-SNAPSHOT</version></dependency><!--web场景的starter--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies></project>
4.5.2.2 Controller
package com.dfbz.controller;import com.mystarter.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** @author lscl* @version 1.0* @intro:*/
@Controller
public class HelloController {@Autowiredprivate HelloService helloService;@GetMapping("/hello")@ResponseBodypublic String hello() {return helloService.sayHello();}
}
4.5.2.3 application.yml
mystarter:text: hello~
4.5.2.4 引导类
package com.dfbz;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @author lscl* @version 1.0* @intro:*/
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/207357.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C运算符与表达式

跟着肯哥&#xff08;不是我&#xff09;学运算符与表达式 运算符 在C语言中&#xff0c;运算符是一种用来执行特定操作的符号或关键字。它们用于对变量、常量和表达进行计算、逻辑判断和位操作等。 定义一般都当耳旁风了 运算符分类 算术运算符 -*/%加减乘除取模&#xff0c;…

计算机组成原理(万字爆肝整理)

第一章 计算机系统概述 “较简单&#xff0c;不做过多赘述&#xff0c;后面会详细学到” 第一节 计算机系统层次结构 1.计算机系统的基本组成&#xff1a;硬件软件 2.计算机硬件的基本组成&#xff1a;运算器存储器控制器输入设备输出设备 3.系统软件和应用软件 系统软件…

【PyGIS】使用阿里AIEarth快速下载指定区域指定年份的土地利用数据

说明 中国逐年土地覆盖数据集(CLCD) 由武汉大学的杨杰和黄昕教授团队基于Landsat影像制作了中国逐年土地覆盖数据集(annual China Land Cover Dataset, CLCD),数据包含1985—2021年中国逐年土地覆盖信息。研究团队基于Landsat长时序卫星观测数据,构建时空特征,结合随机森…

编码的发展历史

编码的发展历史 ASCII&#xff1a; ASCII编码使用7位二进制数表示一个字符&#xff0c;范围从0到127。每个字符都有一个唯一的ASCII码值与之对应。例如&#xff0c;大写字母"A"的ASCII码是65&#xff0c;小写字母"a"的ASCII码是97。 ASCII字符集包括英文…

2023年【起重机械指挥】考试题及起重机械指挥找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机械指挥考试题考前必练&#xff01;安全生产模拟考试一点通每个月更新起重机械指挥找解析题目及答案&#xff01;多做几遍&#xff0c;其实通过起重机械指挥作业考试题库很简单。 1、【多选题】按照事故造成的人…

Java基础(程序控制结构篇)

Java的程序控制结构与C语言一致&#xff0c;分为顺序结构、选择结构&#xff08;分支结构&#xff09;和循环结构三种。 一、顺序结构 如果程序不包含选择结构或是循环结构&#xff0c;那么程序中的语句就是顺序的逐条执行&#xff0c;这就是顺序结构。 import java.util.Sc…

拒绝“信息茧房”,不做“井底之蛙”

前言 在信息爆炸的时代&#xff0c;我们每天都会被各种各样的信息包围着。这些信息来自各种渠道&#xff0c;如社交媒体、新闻媒体、搜索引擎、推荐系统等等。在算法和选择下&#xff0c;社交平台、购物平台帮大家过滤掉不熟悉、不喜欢、不感兴趣的内容&#xff0c;只推荐想看…

langchain 部署组件-LangServe

原文&#xff1a;&#x1f99c;️&#x1f3d3; LangServe | &#x1f99c;️&#x1f517; Langchain LangServe &#x1f6a9; We will be releasing a hosted version of LangServe for one-click deployments of LangChain applications. Sign up here to get on the wa…

uni-app 使用uni.getLocation获取经纬度配合腾讯地图api获取当前地址

前言 最近在开发中需要根据经纬度获取当前位置信息&#xff0c;传递给后端&#xff0c;用来回显显示当前位置 查阅uni-app文档&#xff0c;发现uni.getLocation () 可以获取到经纬度&#xff0c;但是在小程序环境没有地址信息 思考怎么把经纬度换成地址&#xff0c;如果经纬度…

使用gin 代理 web网页

问web项目的代理&#xff0c;业界常用的方案是nginx做代理&#xff0c;这个是网上最多资料的。 因为我需要做自己的流量转发&#xff0c;也就是所有访问都要经过我的一个流量分发微服务&#xff0c;这和nginx作用冲突了。如果再加个nginx来做第一层方向代理和网页的静态资源代…

Axios使用方式

ajax是JQUERY封装的XMLHttprequest用来发送http请求 Axios简单点说它就是一个js库,支持ajax请求,发送axios请求功能更加丰富,丰富在哪不知道 1.npm使用方式 vue项目中 npm install axios 2.cdn方式 <script src"https://unpkg.com/axios/dist/axios.min.js">…

层次分析法--可以帮助你做决策的简单算法

作用 层次分析法是一个多指标的评价算法&#xff0c;主要用来在做决策时&#xff0c;给目标的多个影响因子做权重评分。特别是那些需要主观决策的、或者需要用经验判断的决策方案&#xff0c;例如&#xff1a; 买房子&#xff08;主观决策&#xff09;选择旅游地&#xff08;…