第二章SpringBoot基础学习

news2025/1/19 14:16:30

文章目录

  • SpringBoot依赖管理特性
      • 依赖管理
      • 开发导入starter场景启动器
  • SpringBoot自动配置特性
      • 自动配好Tomcat
      • 自动配好SpringMVC
      • 默认的包结构
      • 各种配置拥有默认值
      • 按需加载所有自动配置项
  • SpringBoot注解底层注解
      • @Configuration
      • @Import导入组件
      • @Conditional条件装配
      • @ImportResource导入Spring配置文件
      • @ConfigurationProperties配置绑定
  • 自动配置
    • @SpringBootApplication的源码
      • @SpringBootConfiguration
      • @ComponentScan
      • @EnableAutoConfiguration
    • 自动包规则原理
      • @AutoConfigurationPackage
    • 初始加载自动配置类
      • @Import(AutoConfigurationImportSelector.class)
      • @Import(AutoConfigurationImportSelector.class)
      • 按需配置的例子
    • 自动配置流程

SpringBoot依赖管理特性

依赖管理

我们在HelloWorld中导入的父项目依赖

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.3.4.RELEASE</version>
</parent>

spring-boot-starter-parent的父项目中的一个依赖如下:

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-dependencies</artifactId>
	<version>2.3.4.RELEASE</version>
</parent>

spring-boot-dependencies几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制

  • 导入spring-boot-starter-parent父项目是用来做依赖管理的,这样我们导入依赖无需关注版本号,自动版本仲裁
    • 引入依赖默认都可以不写版本
    • 引入非版本仲裁的jar,要写版本号。
<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
</dependency>
  • 可以修改默认版本号

    • 查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。

    • 在当前项目里面重写配置,如下面的代码。

<properties>
	<mysql.version>5.1.43</mysql.version>
</properties>

开发导入starter场景启动器

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 见到很多 spring-boot-starter-* : *就某种场景
    • web就是我们开发web应用的场景
  2. 只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
  3. 更多SpringBoot所有支持的场景
  4. 当我们觉得不够用,还可以自己创建starter启动器
    • 见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。

所有场景启动器最底层的依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
	<version>2.3.4.RELEASE</version>
	<scope>compile</scope>
</dependency>

SpringBoot自动配置特性

这里我们以我们导入的web场景下的starter为例子

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>

自动配好Tomcat

  • 引入Tomcat依赖。
  • 配置Tomcat
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-tomcat</artifactId>
	<version>2.3.4.RELEASE</version>
	<scope>compile</scope>
</dependency>

自动配好SpringMVC

  • 引入SpringMVC全套组件
  • 自动配好SpringMVC常用组件(功能)
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.9.RELEASE</version>
      <scope>compile</scope>
</dependency>
  • 自动配好Web常见功能,如:字符编码问题
    • SpringBoot帮我们配置好了所有web开发的常见场景
public static void main(String[] args) {
    //1、返回我们IOC容器
    ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

    //2、查看容器里面的组件
    String[] names = run.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}
//部分输出结果
tomcatWebServerFactoryCustomizer
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
characterEncodingFilter--------设置字符编码的过滤器
localeCharsetMappingsCustomizer
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
multipartConfigElement
multipartResolver
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
org.springframework.aop.config.internalAutoProxyCreator

我们之前想解决中文乱码问题,需要我们自己配置字符编码过滤器,有了SpringBoot,我们都不需要设置

默认的包结构

  • 主程序(也就是被我们@SpringBootApplication修饰的类)所在包及其下面的所有子包里面的组件都会被默认扫描进来
    • 自动代替我们之前Ioc容器xml文件中配置的自动扫描组件
  • 无需以前的包扫描配置
  • 想要改变扫描路径
    • @SpringBootApplication(scanBasePackages=“com.lun”)
    • 为什么不能直接通过@ComponentScan 指定扫描路径 因为我们的@SpringBootApplication()是一个合成注解
@SpringBootApplication(scanBasePackages="com.lsc")
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.lun")

各种配置拥有默认值

  • 默认配置最终都是映射到某个类上,如:MultipartProperties
#设置服务器的端口号
server.port=8080
#设置文件上传的最大大小
spring.servlet.multipart.max-file-size=10MB

配置文件的值最终会绑定每个类上,因为这个类会在容器中创建对象

按需加载所有自动配置项

  • 我们的SpringBoot非常多的starter
  • 引入了哪些场景这个场景的自动配置才会开启
  • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
    • 但是不是所有的配置都会生效,至于原理需要了解相关注解的原理

我们的spring-boot-starter-web中存在spring-boot-starter依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
</dependency>

我们的spring-boot-starter中存在spring-boot-autoconfigure

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
</dependency>

SpringBoot注解底层注解

@Configuration

表示当前这个类是配置类,类似我们xml配置文件的作用

/**
 * @author lsc
 * #Description MyConfig
 * #Date: 2023/2/11 11:11
 */
@Configuration(proxyBeanMethods = true)
public class MyConfig {
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User user = new User(1, "刘颂成");
        user.setCat(tomcat());
        return user ;

    }
    @Bean("tomcat")//如果不想以方法名为名称,可以自己自定义
    public Cat tomcat(){
        return new Cat("tomcat");
    }
}
  • 配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
  • 配置类本身也是组件
  • proxyBeanMethods:代理bean的方法
    •  Full(proxyBeanMethods = true)(保证每个@Bean方法被调用多少次返回的组件都是单实例的)(默认)
      
    •  Lite(proxyBeanMethods = false)(每个@Bean方法被调用多少次返回的组件都是新创建的)
      

测试

@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {

    public static void main(String[] args) {
    //1、返回我们IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

    //2、查看容器里面的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

    //3、从容器中获取组件
        Pet tom01 = run.getBean("tom", Pet.class);
        Pet tom02 = run.getBean("tom", Pet.class);
        System.out.println("组件:"+(tom01 == tom02));

    //4、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

    //如果@Configuration(proxyBeanMethods = true) 代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。
        //保持组件单实例
        User user = bean.user01();
        User user1 = bean.user01();
        System.out.println(user == user1);//true

        User user01 = run.getBean("user01", User.class);
        Cat tom = run.getBean("tomcat", Cat.class);

        System.out.println("用户的宠物:"+(user01.getPet() == tom));//true
    }
}
  • 最佳实战
    • 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
    • 配置 类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认)

@Import导入组件

  • @Bean、@Component、@Controller、@Service、@Repository,它们是Spring的基本标签,在Spring Boot中并未改变它们原来的功能。

  • @ComponentScan表示扫描组件,来指定要扫描的包

@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User user = new User(1, "刘颂成");
        user.setCat(tomcat());
        return user ;

    }
    @Bean("tomcat")//如果不想以方法名为名称,可以自己自定义
    public Cat tomcat(){
        return new Cat("tomcat");
    }
}
  • @Import自动导入对应的类,利用其无参构造方法创建一个Bean给容器进行管理
  • @Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名

测试类:

@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {
    public static void main(String[] args) {
        //返回的是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class,args);
        String[] beanNamesForType = run.getBeanNamesForType(User.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
        DBHelper bean1 = run.getBean(DBHelper.class);
		  System.out.println(bean1);
    }
}
//
com.lsc.springboot.pojo.User
user01
ch.qos.logback.core.db.DBHelper@73d6d0c

@Conditional条件装配

条件装配:满足Conditional指定的条件,则进行组件注入

image-20230211120740450

  • ConditionalOnBean,当容器存在某个Bean,才会进行注入以下的对象
  • ConditionalOnMissingBean,当容器不存在某个Bean,才会进行注入以下的对象

用@ConditionalOnBean举例说明

/**
 * @author lsc
 * #Description MyConfig
 * #Date: 2023/2/11 11:11
 */
@Configuration(proxyBeanMethods = true)
@ConditionalOnBean(name = "tomcat")//只有当有tom名字的Bean时,MyConfig类的Bean才能生效。
public class MyConfig {
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User user = new User(1, "刘颂成");
        user.setCat(tomcat());
        return user ;

    }
//    @Bean("tomcat")//如果不想以方法名为名称,可以自己自定义
    public Cat tomcat(){
        return new Cat("tomcat");
    }
}

  • 现在我们的容器中是不存在user01这个bean
  • 对应的条件装配注解可以放在方法上也可以放在类上
@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {
    public static void main(String[] args) {
        //返回的是IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class,args);
        //查看我们IOC容器中Bean的组件名称
        boolean tomcat = run.containsBean("tomcat");
        System.out.println("名称为tomcat的存在"+tomcat);
        boolean user01 = run.containsBean("user01");
        System.out.println("名称为user01的bean存在"+user01);
    }
}
//输出结果
名称为tomcat的存在false
名称为user01的bean存在false
  • 我们看到我们的tomcat这个bean不存在,所以连user01这个bean也没有注入容器

@ImportResource导入Spring配置文件

比如,公司使用bean.xml文件生成配置bean,然而你为了省事,想继续复用bean.xml,@ImportResource登场。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="com.lsc.springboot.pojo.User" id="haha">
        <property name="id" value="2"></property>
        <property name="name" value="sss"></property>
    </bean>
    <bean class="com.lsc.springboot.pojo.Cat" id="hehe">
        <property name="name" value="小猫"></property>
    </bean>
</beans>

测试

public static void main(String[] args) {
    //1、返回我们IOC容器
    ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

	boolean haha = run.containsBean("haha");
	boolean hehe = run.containsBean("hehe");
	System.out.println("haha:"+haha);//true
	System.out.println("hehe:"+hehe);//true
}

@ConfigurationProperties配置绑定

如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用

传统方式

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }
  • 我们需要自己读取文件的内容,然后进行判断存入

Spring Boot一种配置配置绑定:

@ConfigurationProperties + @Component

假设有配置文件application.properties

mycar.brand=BYD
mycar.price=10000
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
    private String  brand;
    private Integer price;
    .......
}
  • 因为只有被spring管理,才能拥有SpringBoot强大的功能
@RestController
public class HelloWorldController {
    @Autowired
    Car car;
    @RequestMapping("/car")
    public Car car(){
        return car;
    }

image-20230212224559262

SpringBoot另一种配置配置绑定

@EnableConfigurationProperties + @ConfigurationProperties

  1. 开启Car配置绑定功能
  2. 把这个Car这个组件自动注册到容器中
@ConfigurationProperties(prefix = "mycar")
public class Car {
...
}
@EnableConfigurationProperties(Car.class)
public class MyConfig {
...
}

自动配置

被@SpringBootApplication表示为主类

@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {
    public static void main(String[] args) {
      SpringApplication.run(MainApplication.class,args);
      
}

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

重点分析@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@Configuration代表当前是一个配置类。

@ComponentScan

指定扫描哪些Spring注解。

@EnableAutoConfiguration

这个注解需要重点分析

@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)

自动包规则原理

@AutoConfigurationPackage

标签名直译为:自动配置包,指定了默认的包规则。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}
  • @Import({Registrar.class})利用Registrar给容器中导入一系列组件
  static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
     Registrar() {
     }

     public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
       AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
     }

     public Set<Object> determineImports(AnnotationMetadata metadata) {
       return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
      }
    }

Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器

初始加载自动配置类

@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

1、这个类中有一个这样的方法

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

2getAutoConfigurationEntry中调用了getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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 correct.");
        return configurations;
 }

3 这个方法又调用了SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
      MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
 }

发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories
文件里面写死了spring-boot一启动就要给容器中加载的所有配置类

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:

@Import(AutoConfigurationImportSelector.class)

  • AutoConfigurationImportSelector类中利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件

  • getAutoConfigurationEntry方法中通过调用 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取到所有需要导入到容器中的配置类

    • SpringFactoriesLoader利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件

      • META-INF/spring.factories位置来加载一个文件。

      • 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件

spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories

# 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
# spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...

虽然我们127个场景的所有自动配置启动的时候默认全部加载,但是xxxxAutoConfiguration按照条件装配规则(@Conditional),最终会按需配置。

springboot所有的自动配置都是在启动的时候扫描并加载,扫描了spring.properties配置文件,所有的自动配置类都在这里面,但是不定生效,因为要判断条件是否成立,只要导入了对应的start,就有对应的启动器,有了启动器我们自动装配就会生效,然后就配置成功

按需配置的例子

AopAutoConfiguration类:

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnProperty(
    prefix = "spring.aop",
    name = "auto",
    havingValue = "true",
    matchIfMissing = true
)
public class AopAutoConfiguration {
    public AopAutoConfiguration() {
    }
	...
}
  • prefix为配置文件中的前缀,
  • name为配置的名字
  • havingValue是与配置的值对比值,当两个值相同返回true,配置类生效.
  • matchIfMissing表示如果没有这么配置的默认值

自动配置流程

DispatcherServletAutoConfiguration的内部类DispatcherServletConfiguration为例子:

@Bean
@ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
	// Detect if the user has created a MultipartResolver but named it incorrectly
	return resolver;//给容器中加入了文件上传解析器;
}
  • 给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
  • 方法名为multipartResolver ,SpringMVC multipartResolver防止有些用户配置的文件上传解析器不符合规范

SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先

总结

  • SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
  • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。(xxxxProperties里面读取,xxxProperties和配置文件进行了绑定)
  • 生效的配置类就会给容器中装配很多组件
  • 只要容器中有这些组件,相当于这些功能就有了
  • 定制化配置
    • 用户直接自己@Bean替换底层的组件
    • 用户去看这个组件是获取的配置文件什么值就去修改。

xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties

image-20230213001502353

面试官问你你可以这样说,springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,还会它是通过启动类的SpringBootApplication注解进行开始解析的,他会根据EnableAutoConfiguration开启自动化配置,里面有个核心方法ImportSelect选择性的导入,根据loadFactoryNames根据classpath路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python入门自学进阶-Web框架——33、瀑布流布局与组合查询

一、瀑布流&#xff0c;是指页面布局中&#xff0c;在显示很多图片时&#xff0c;图片及文字大小不相同&#xff0c;导致页面排版不美观如上图&#xff0c;右边的布局&#xff0c;因为第一行第一张图片过长&#xff0c;第二行的第一张被挤到第二列了。示例&#xff1a;def flow…

大数据框架之Hadoop:MapReduce(八)常见错误及解决方案

1、导包容易出错。尤其Text和CombineTextInputFormat。 2、Mapper中第一个输入的参数必须是LongWritable或者NullWritable&#xff0c;不可以是IntWritable. 报的错误是类型转换异常。 3、java.lang.Exception: java.io.IOException: Illegal partition for 13926435656 (4)&…

51单片机LCD1602的使用

文章目录前言一、LCD1602简单介绍二、LCD1602中各个引脚的作用四、LCD1602命令解析1.写命令2.写数据3.清屏指令4.光标归位指令5.进入模式设置指令6.显示开关控制指令7.设定显示屏或光标移动方向指令三、LCD1602代码编写四、代码测试总结前言 本篇文章将为大家讲解LCD1602的使用…

CPU扫盲-CPU如何执行指令以及流水线技术

在CPU扫盲-CPU与指令集中阐述了CPU与指令集之间的关系&#xff0c;并在CPU扫盲-自研指令集中以创造者的身份深入讲解了指令集&#xff0c;这篇文章则是针对CPU的专场&#xff0c;以x86架构下的CPU为例具体分析一下CPU如何执行指令。 计算机基本硬件由控制器、储存器、运算器、输…

基于java的五子棋游戏设计

技术&#xff1a;Java、JSP等摘要&#xff1a;随着互联网迅速的发展&#xff0c;网络游戏已经成为人们普遍生活中不可或缺的一部分&#xff0c;它不仅能使人娱乐&#xff0c;也能够开发人的智力&#xff0c;就像本文所主要讲的五子棋游戏一样能挖掘人们聪明的才干与脑袋的机灵程…

【大数据 AI 人工智能】数据科学家必学的 9 个核心机器学习算法

如今,机器学习正改变着我们的世界。借助机器学习(ML),谷歌在为我们推荐搜索结果,奈飞在为我们推荐观看影片,脸书在为我们推荐可能认识的朋友。 机器学习从未像在今天这样重要。但与此同时,机器学习这一领域也充斥着各种术语,晦涩难懂,各种机器学习的算法每年层出不穷…

思科2.7.6 Packet Tracer - Implement Basic Connectivity(作业)

Packet Tracer - 实施基本连接地址分配表目标第 1 部分&#xff1a;对 S1 和 S2 执行基本配置第 2 部分&#xff1a;配置 PC第 3 部分&#xff1a;配置交换机管理界面背景信息在这个练习中&#xff0c;您会首先执行基本的交换机 配置。然后您会通过在交换机和 PC 上配置 IP 编址…

【C++】string

【C修炼秘籍】string 目录 【C修炼秘籍】string 文章目录 前言 一、标准库里的string 二、string常用接口功能简介&#xff08;具体使用和底层转到模拟实现&#xff09; 1、string类的常见构造函数 2、string类对象的容量操作 3、string类对象的访问及遍历操作 4、 string类对象…

数影周报:LastPass数据泄露事件最新细节

本周看点&#xff1a;LastPass&#xff1a;关键运维员工遭定向攻击&#xff1b;Waymo今年第二轮裁掉137名员工&#xff1b;国家网信办发布《个人信息出境标准合同办法》&#xff1b;京麦商家“取消订单页面”升级&#xff1b;“智研汇”获千万级天使轮投资......数据安全那些事…

ubuntu-8-安装nfs服务共享目录

Ubuntu最新版本(Ubuntu22.04LTS)安装nfs服务器及使用教程 ubuntu16.04挂载_如何在Ubuntu 20.04上设置NFS挂载 Ubuntu 20.04 设置时区、配置NTP同步 timesyncd 代替 ntpd 服务器 10.0.2.11 客户端 10.0.2.121 NFS简介 (1)什么是NFS NFS就是Network File System的缩写&#xf…

W800系列||STM32最小版|CKLINK|待完善|学习(3-2):自制cklink调试工具测试(win11系统识别错误待解决)

续前文&#xff1a; W800系列|ST-LINK|STM32最小版|HEX文件|CKLINK|DebugServer|学习&#xff08;3-1&#xff09;&#xff1a;自制cklink调试工具_打酱油的工程师的博客-CSDN博客 硬件接线 CK-LINK W806 3V3 3V3 RST RST&#xff08;复位脚&#xff09; TCK CLK&…

MySQL日志管理

日志管理在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这种时候&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了数据库中数据丢失或被破坏可能原因&#xff1a;误删除数据库数据库工作时&a…

openpnp - error - 回位精度差的问题

文章目录openpnp - error - 回位精度差的问题概述齿隙矫正方法不能用openpnp默认的, 要自己选合适的方法ENDopenpnp - error - 回位精度差的问题 概述 用openpnp向导进行完x/y齿隙校正后, 进行回位精度测试. 在设备上挑选2个点: 主基准点次基准点 都是固定位置(将带mark点的…

Go中sync 包的 Once 使用

文章目录背景One 简介示例注意源码解读背景 在系统初始化的时候&#xff0c;某些代码只想被执行一次&#xff0c;这时应该怎么做呢&#xff0c;没有学习 Once 前&#xff0c;大家可能想到 声明一个标识&#xff0c;表示是否初始化过&#xff0c;然后初始化这个标识加锁&#x…

Lazada(来赞达)箱包什么产品好卖?东南亚热销国家选品分析

东南亚市场&#xff1a;存在巨大的跨境电商出口机遇 和2021年前对比&#xff0c;2022年越南、马来西亚等东南亚国家普遍实现了贸易正增长&#xff0c;欧美国家则多仍处于负增长状态。 同时2022年欧美等发达经济体通胀压力迅速抬升&#xff0c;这直接影响到国家间货币汇率&…

微信小程序如何配置并使用less?

微信小程序如何配置并使用less&#xff1f;1、在VScode中下载Less插件2、在微信小程序中依次点击如下按钮3、选中刚在vscode中下载安装的插件文件4、在设置中选中编辑器设置5、找到less进行json配置6、在json文件中的less.compile添加如下配置7、如何使用1、在VScode中下载Less…

深圳大学计软《面向对象的程序设计》实验7 拷贝构造函数与复合类

A. Point&Circle(复合类与构造) 题目描述 类Point是我们写过的一个类&#xff0c;类Circle是一个新的类&#xff0c;Point作为其成员对象&#xff0c;请完成类Circle的成员函数的实现。 在主函数中生成一个圆和若干个点&#xff0c;判断这些点与圆的位置关系&#xff0c;…

最佳iOS设备管理器imazing 2.16.9官网Mac/Windows下载电脑版怎么下载安装

imazing 2.16.9官网Mac/Windows下载电脑版是款针对苹果设备所打造的管理工具。iMazing为用户提供多种设备管理功能&#xff0c;每一位用户都能以自己的形式管理苹果设备。iMazing与苹果设备连接后&#xff0c;用户就可以轻松传输文件&#xff0c;浏览保存信息等。 应用介绍 iM…

员工入职流程自动化的原因

人和人之间的第一印象非常重要&#xff0c;一段缘分能不能开始&#xff0c;就看第一印象够不够给力了。其实&#xff0c;公司和新员工之间也存在着这样的关系&#xff0c;但也有些许差别。公司对新员工的第一印象&#xff0c;更多是从第一次见面的时候就产生了&#xff0c;而新…

Flutter知识点(三)修改flutter工程名和包名

修改包名, 修改五个地方&#xff1a; debug/AndroidManifest.xml, main/AndroidManifest.xml, profile/AndroidManifest.xml,gradle.build的中application id,原生的android代码&#xff0c; 这一块建议用android工程的方式打开。不然无法使用refactor修改名字。 修改flutter工…