来吧,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.rhkb.cn/news/200026.html

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

相关文章

Enterprise Architect安装与使用

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Enterprise Architect概述 官方网站&#xff1a;https://www.sparxsystems.cn/products/ea/&#xff1b;图示如下&#xff1a; Enterprise Architect是一个全功能的、基于…

webpack 中,filename 和 chunkFilename 的区别

filename filename 是一个很常见的配置&#xff0c;就是对应于 entry 里面的输入文件&#xff0c;经过webpack打包后输出文件的文件名。比如说经过下面的配置&#xff0c;生成出来的文件名为 index.min.js。 chunkFilename chunkFilename 指未被列在 entry 中&#xff0c;却…

单图像3D重建AI算法综述【2023】

计算机视觉是人工智能的一个快速发展的领域&#xff0c;特别是在 3D 领域。 本概述将考虑一个应用任务&#xff1a;2D 和 3D 环境之间的转换。 在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编…

C语言入门笔记—static、extern、define、指针、结构体

一、static static修饰局部变量的时候&#xff0c;局部变量出了作用域&#xff0c;不销毁。本质上&#xff0c;static修饰局部变量的时候&#xff0c;改变了变量的存储位置。详见下图&#xff0c;当a不被static修饰和被static修饰的时候。 C/C static关键字详解&#xff…

Spring Cloud学习(九)【Elasticsearch 分布式搜索引擎01】

文章目录 初识 elasticsearch了解 ES倒排索引ES 的一些概念安装es、kibana安装elasticsearch部署kibana 分词器安装IK分词器ik分词器-拓展词库 索引库操作mapping 映射属性索引库的 CRUD 文档操作添加文档查看、删除文档修改文档Dynamic Mapping RestClient 操作索引库什么是Re…

【Spring Boot】使用WebSocket协议完成来单提醒及客户催单功能

1 WebSocket介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信(双向传输)——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c; 并进行双向数据传输。 1.1 HTTP协议和WebSocket协议对比 1、HTTP是短…

redis五大常见数据结构的操作命令(string, hash, list, set和zset)

string redis的string&#xff0c;直接按照二进制&#xff08;不做任何的转换&#xff0c;存的是什么取出来的依旧是什么&#xff09;的方式存储。所以string不仅仅可以存储文本数据&#xff0c;还可以存储整数&#xff0c;JSON&#xff0c;xml甚至音视频。但是string的大小最…

谷歌浏览器任意文件访问漏洞(CVE-2023-4357)复现

1.漏洞级别 高危 2.漏洞描述 该漏洞的存在是由于 Google Chrome中未充分验证 XML 中不受信任的输入。远程攻击者可利用该漏洞通过构建的 HTML 页面绕过文件访问限制&#xff0c;导致chrome任意文件读取。 总结&#xff1a;一个XXE漏洞 3.利用范围 Google Chrome < 116.…

【计算机网络笔记】网络地址转换(NAT)

系列文章目录 系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08…

JAXB:用XmlElement注解复杂类型的Java属性,来产生多层嵌套的xml元素

例如&#xff0c;下面这段请求的xml代码&#xff0c;在元素body下面又多了一层&#xff0c;嵌套了4个元素&#xff1a; <?xml version"1.0" encoding"UTF-8"?><request><reqtype>04</reqtype><secret>test</secret>…

​软考-高级-系统架构设计师教程(清华第2版)【第10章 软件架构的演化和维护(P345~382)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第10章 软件架构的演化和维护&#xff08;P345~382&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

细节决定成败——我的日志去哪了?

概述 编写本文档的目的有两点。 本周遇到了一个日志丢失的问题&#xff0c;经过分析&#xff0c;觉得挺有意思的。向大家分享一下我的分析及解决思路。应该在很多项目中都会有该问题。领导和我私下讨论过多次&#xff0c;当前的autodomain代码对文件读取的频率太高了,如何去避…

数学建模-图与网络模型解题方法和代码实现

本文针对以下几个方面问题进行整理&#xff1a; 最短路问题 两个指定顶点之间的最短路径任意顶点之间的最短路径 2.最小生成树问题 求最小生成树 3.网络最大流问题 源点与汇点之间的最大流基于最大流的最小费用求解 4.旅行商问题 基于哈密顿(Hamilton)圈求解旅行商线性…

基于Qt QList和QMap容器类示例

## QList<T> QList<T>容器是一个数组列表,特点如下: 1.大多数情况下可以用QList。像prepend()、append()和insert()这种操作,通常QList比QVector快的多。这是因为QList是基于index标签存储它的元素项在内存中(虽然内存不连续,这点与STL的list 是一样的),比…

网络连接Android设备

参考&#xff1a;https://blog.csdn.net/qq_37858386/article/details/123755700 二、网络adb调试开启步骤 1、把Android平板或者手机WiFi连接到跟PC机子同一个网段的网络&#xff0c;在设置-系统-关于-状态 下面查看设备IP,然后查看PC是否可以ping通手机的设备的IP。 2、先…

深度学习人脸表情识别算法 - opencv python 机器视觉 计算机竞赛

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人脸表情识别系…

口袋参谋:找关键词的三种方法!

​如何找热搜关键词&#xff1f;99%的商家都不知道。那么今天可以根据我说的三种方法去做。 第一种方法&#xff1a;利用竞争对手 通过分析竞争对手&#xff0c;正在使用和采取何种优化方法&#xff0c;来帮助你理解市场上正在流行什么样的关键字&#xff0c;这些热词可以直接从…

美国DDoS服务器:如何保护你的网站免遭攻击?

​  在当今数字化时代&#xff0c;互联网已经成为人们生活中不可或缺的一部分。随着互联网的普及和发展&#xff0c;网络安全问题也日益严重。其中&#xff0c;DDoS攻击是目前最常见和具有破坏性的网络攻击之一。那么&#xff0c;如何保护你的网站免遭DDoS攻击呢?下面将介绍…

自动化物流运输设备模组要选择哪种类型?

在自动化物流运输设备中&#xff0c;选择合适的模组类型取决于具体的运输需求和应用场景。 1、同步带模组&#xff1a;同步带模组是一种低噪音、低成本的物流运输设备&#xff0c;适用于中短距离、轻型货物的运输。它采用同步带传动的方式&#xff0c;具有传动准确、运行稳定、…

代码随想录二刷 | 链表 | 翻转链表

代码随想录二刷 &#xff5c; 链表 &#xff5c; 翻转链表 题目描述解题思路 & 代码实现双指针法递归法 206.翻转链表 题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4…