SpringBoot(二) :基础

x33g5p2x  于2021-10-25 转载在 Spring  
字(22.2k)|赞(0)|评价(0)|浏览(172)

写在前面

 1、入门

系统要求

  • Java 8
  • Maven 3.3+
  • IntelliJ IDEA 2019.1.2

Maven配置文件
新添内容:

<mirrors>
    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>central</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
</mirrors>

<profiles>
    <profile>
        <id>jdk-1.8</id>

        <activation>
            <activeByDefault>true</activeByDefault>
            <jdk>1.8</jdk>
        </activation>

        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
        </properties>
    </profile>
</profiles>

1.1、HelloWorld项目

需求:浏览发送/hello请求,响应 “Hello,Spring Boot 2”创建maven工程

父:spring-boot-starter-parent
引入依赖:

  • web支持:spring-boot-starter-web
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

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

创建主程序

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

编写业务
 

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01(){
        return "Hello, Spring Boot 2!";
    }
}

运行&测试
        运行MainApplication类
        浏览器输入http://localhost:8080/hello,将会输出Hello!。

设置配置
maven工程的resource文件夹中创建application.properties文件。
/# 设置端口号
server.port=8888


更多配置信息

打包部署

之前是打成war包,然后放到tomcat中运行。

现在: 在pom.xml添加

<!--  这个插件,可以将应用打包成一个可执行的jar包-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
    </plugins>
</build>

        在IDEA的Maven插件上点击运行 clean 、package,把helloworld工程项目的打包成jar包

打包好的 jar 包被生成在helloworld工程项目的target文件夹内。

用cmd运行java -jar boot-01-helloworld-1.0-SNAPSHOT.jar,既可以运行helloworld工程项目。 

将jar包直接在目标服务器执行即可。

1.2、Sprigboot项目总结:

1、引入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <!-- web支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2、创建主程序

/**
 * @SpringBootApplication 标志这个一个Springboot应用
 */
@SpringBootApplication
@Slf4j
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class,args);
        log.info("系统正常启动");
    }
}

3、编写业务

@RestController  //@RestController = @Controller + @ResponseBody
public class HelloController {

    @RequestMapping("/hello")
    public String handle01(){
        return "hello";
    }
}

4、测试

直接运行Main

5、简化配置

application.properties:

server.port=8888

6、简化部署

<!--  这个插件,可以将应用打包成一个可执行的jar包-->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

把项目打成jar 包,直接在目标服务器执行就行。

1.3、自动配置管理

依赖管理

1.3.1、父项目做依赖管理

管理依赖的版本号,默认引入的引来可以不写版本号,有默认值,当然也可以写。

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

上面项目的父项目如下:
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

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

1.3.2、开发导入starter场景启动器

  • 见到很多 spring-boot-starter-/* : /*就某种场景
  • 只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
  • 更多SpringBoot所有支持的场景
  • 见到的 /*-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
    所有场景启动器最底层的依赖

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

1.3.3、无需关注版本号,直接版本仲裁

  • 引入依赖默认都可以不写版本
  • 引入非版本仲裁的jar,要写版本号

1.3.4、可以修改版本号

  • 查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
  • 在当前项目里面重写配置,如下面的代码,则可以替代默认的版本号。
<properties>
    <mysql.version>5.1.43</mysql.version>
</properties>

IDEA快捷键:

  • option + command + U:以图的方式显示项目中依赖之间的关系。

自动配置

引入spring-boot-starter-web依赖,会默认包含:

spring-boot-starter-tomcat、spring-web、spring-webmvc

**1.3.1、**自动配好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>  

**1.3.2、**自动配好SpringMVC

  • 引入SpringMVC全套组件
  • 自动配好SpringMVC常用组件(功能)

**1.3.3、**自动配好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);
    }
}

**1.3.4、**默认的包结构

  • 主程序(MainApplication)所在包及其下面的所有子包里面的组件都会被默认扫描进来

  • 无需以前的包扫描配置

  • 想要改变扫描路径

  • @SpringBootApplication(scanBasePackages=“com.lun”)

  • 或者

  • @ComponentScan 指定扫描路径
    @SpringBootApplication
    等同于
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan("com.lun")

**1.3.4、**各种配置拥有默认值

  • 默认配置最终都是映射到某个类上,如:MultipartProperties
  • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象

1.3.5、按需加载所有自动配置项

  • 非常多的starter
  • 引入了哪些场景这个场景的自动配置才会开启
  • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

2、底层注解-@Configuration详解

  • @Configuration(proxyBeanMethod="true")
  • @Bean:方法名就是bean id,返回值是bean实例,返回类型是bean类型
  • 配置类本身也是组件:是一个代理组件

Full模式与Lite模式

/**
 * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 * 2、配置类本身也是组件
 * 3、proxyBeanMethods:代理bean的方法
 *      Full(proxyBeanMethods = true)(保证每个@Bean方法被调用多少次返回的组件都是单实例的)(默认)
 *      Lite(proxyBeanMethods = false)(每个@Bean方法被调用多少次返回的组件都是新创建的)
 */
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {

    /**
     * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
     * @return
     */
    @Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        //user组件依赖了Pet组件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
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);

        User user01 = run.getBean("user01", User.class);
        Pet tom = run.getBean("tom", Pet.class);//用户获取的pet是容器中唯一的

        System.out.println("用户的宠物:"+(user01.getPet() == tom));//true
    }
}

**注意:**springboot使用 配置类 代替 bean配置文件

<bean id="user01" class="com.rc.spring.boot.bean.User">
    <property name="name" value="zhangsan"></property>
    <property name="age" value="18"></property>
</bean>
@Configuration //告诉spring容器这是一个配置类,==配置文件
public class MyConfig {
   //给容器添加组件,方法名为组件id,返回类型为组件类型,返回的值为组件在容器中的实例。
    @Bean 
    public User user01(){
        return new User("zhangsna",18);
    }
}

最佳实战

  • 配置类 组件之间无依赖关系用Lite模式加速容器启动过程,减少判断

  • 因为使用Lite模式,每次从容器获取组件时不会在容器中再执行判断操作,判断组件是否存在。

  • 配置类 组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认)

  • 容器中bean相互依赖时,full模式能保证调用的依赖组件是同一个。

3、底层注解-@Import导入组件

  • @Bean、@Component、@Controller、@Service、@Repository,它们是Spring的基本标签,在Spring Boot中并未改变它们原来的功能。
  • @ComponentScan 在07、基础入门-SpringBoot-自动配置特性有用例。
  • @Import({User.class, DBHelper.class}) 给容器中自动创建出这两个类型的组件、默认组件的名字(beanid)就是全限定类名。
  • @Import放在容器中 各种组件类 上面即可。
@Import({User.class, DBHelper.class})
@Configuration
public class MyConfig {

    @Bean 
    public User user01(){
        return new User("zhangsna",18);
    }

    @Bean("cat02")
    public Pet cat(){
        return new  Pet("tomcat");
    }

}

@SpringBootApplication(scanBasePackages = "com.rc.spring.boot")
@Slf4j
public class MainApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
        String[] beanNamesForType = context.getBeanNamesForType(User.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
        //com.rc.spring.boot.bean.User
        //user01
        DBHelper bean1 = context.getBean(DBHelper.class);
        System.out.println(bean1);//ch.qos.logback.classic.db.DBHelper@61942c1
    }
}

4、底层注解-@Conditional条件装配

  • 条件装配:满足Conditional指定的条件时,才进行组件注入。
  • 以前标注@Bean的都会注册到容器中
  • @Conditional可以标注在类上,或方法上

举例:

用@ConditionalOnBean举例说明,user有一只猫,即存在依赖关系,故需要实现当容器中没有猫组件时,user组件也不注册到容器中。

@Configuration(proxyBeanMethods = false)
public class MyConfig {

    
//在容器中有tom名字的Bean时,user01的bean才会注册到容器中。否则不会注册
    @ConditionalOnBean(name = "tom")
    @Bean
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

   
    public Pet tom(){
        return new Pet("tomcat");
    }
}

**@ConditionOnBeanname="tom")标注在类上:**是说只有在容器中有名字为 tom 的bean时,该类里的所有bean才会生效。否则不生效

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(name = "tom")//有tom名字的Bean时,MyConfig类的Bean才能生效。
public class MyConfig {

    @Bean
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom22")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

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);
    }

    boolean tom = run.containsBean("tom");
    System.out.println("容器中Tom组件:"+tom);//false

    boolean user01 = run.containsBean("user01");
    System.out.println("容器中user01组件:"+user01);//true

    boolean tom22 = run.containsBean("tom22");
    System.out.println("容器中tom22组件:"+tom22);//true

}

5、底层注解-@ImportResource导入Spring配置文件

比如,正常使用全注解开发后,bean.xml文件的内容已经不使用了,即里面的bean不会注册到容器中。

然而,有些公司以前使用bean.xml文件生成配置bean,当我们想继续复用bean.xml,不想全部改成注解的形式时,可以使用@ImportResource 来让bean.xml 继续生效,保证xml中的bean注册到容器中。

bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ...">

    <bean id="haha" class="com.lun.boot.bean.User">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>

    <bean id="hehe" class="com.lun.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>
</beans>

使用:

@Import({User.class, DBHelper.class})
@Configuration //告诉spring容器这是一个配置类,==配置文件
@ConditionalOnMissingBean(name="tom")
@ImportResource("classpath:bean.xml")
public class MyConfig {

}
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
}

6、底层注解-@ConfigurationProperties配置绑定

6.1

只有在容器中的组件,才能拥有springboot 提供的强大功能,即容器提供的强大功能,即才能使用容器里的使用组件。

如何使用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。
         }
     }
 }

6.2、Spring Boot 一种配置绑定

@ConfigurationProperties + @Component

1、配置文件application.properties
mycar.brand=BYD
mycar.price=100000

2、使用@ConfigurationProperties(prefix="mycar")

将 文件内容和 实体 绑定。注意加上@Component表示是容器中组件

@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "mycar")
@Component
public class Car {
    String price;
    String branch;
}

3、验证

@RestController  //@RestController = @Controller + @ResponseBody
public class HelloController {

    @Autowired
    private Car car;

    @RequestMapping("/car")
    public Car getCar(){
        return car;
    }
}

只有在容器中的组件,才会拥有SpringBoot提供的强大功能
 

7.3、Spring Boot 另一种配置绑定

  • @EnableConfigurationProperties + @ConfigurationProperties

  • 一定 在配置类上 写 @EnableConfigurationProperties(Car.class)

  • 开启Car配置绑定功能

  • 把这个Car这个组件自动注册到容器中

  • 在实体类上 @ConfigurationProperties(prefix="mycar")

application.properties
server.port=8888

mycar.branch=bmw

mycar.price=100000

@Import({User.class, DBHelper.class})
@Configuration //告诉spring容器这是一个配置类,==配置文件
@ConditionalOnMissingBean(name="tom")
@ImportResource("classpath:beans.xml")
@EnableConfigurationProperties(Car.class)
public class MyConfig {

 
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "mycar")
//@Component
public class Car {

    String price;
    String branch;
}

7、自动配置【源码分析】-自动包规则原理

@SpringbootApplicaion

@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

7.1、@SpringBootConfiguration

@Configuration代表当前是一个配置类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
 

7.2、@ComponentScan

指定扫描哪些Spring注解

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

7.3.1、@AutoConfigurationPackage

自动配置包,指定了默认的包规则。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入一个组件
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

  Class<?>[] basePackageClasses() default {};
}

  1. 利用Registrar给容器中导入一系列组件
  2. 将指定的一个包下的所有组件导入进MainApplication所在包下。

即Registrar 作用 就是将某个包下的所有 组件 注册到容器中。

7.3.2、@Import(AutoConfigurationImportSelector.class)

public class AutoConfigurationImportSelector{

@Override
public String[] **selectImports**(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationEntry autoConfigurationEntry = **getAutoConfigurationEntry(annotationMetadata);**
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry   **getAutoConfigurationEntry
**(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   List<String> configurations = **getCandidat**eConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = getConfigurationClassFilter().filter(configurations);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> **getCandidateConfigurations**(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.l**oadFactoryNames(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;
}

}

public final class SpringFactoriesLoader {

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();
  • 利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件

  • 调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类(共127个自动配置类,就是在配置文件里写死的,命名为xxxAutoConfiguration)

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

  • 从META-INF/spring.factories位置来加载一个文件。(在我们引入的所有jar包里,有的meta-inf里没有内容,有的有spring.factories)

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

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

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

  •  文件里面写死了启动配置类,共127个,springboot一启动就要从容器中加载所有的配置类。

  •   文件位置:weispring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories

AopAutoConfiguration类。

按需加载的条件装配:满足条件才能生效。下面aop里面不满足条件,Advice标红不能生效。


8、自动配置【源码分析】-自动配置流程

8.1、例子1

DispatcherServletAutoConfiguration的内部DispatcherServletConfiguration:

方法会从容器中找MultipartResolver类型的解析器,找到后赋值给resolver返回。

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

8.2、例子2

看CacheAutoConfiguration是否生效。

**@ConditionalOnClass(CacheManager.class):**首先找下CacheManager是否存在:双击shift,搜索CacheManager,在spring-context中故存在。

然后查看CacheAspectSupport类型的bean

//查找CacheAspectSupport类型的组件在容器中存在几个 
String[] CacheAspectSupport =        context.getBeanNamesForType(CacheAspectSupport.class); System.out.println("CacheAspectSupport.length:" + CacheAspectSupport.length); //CacheAspectSupport.length
@Configuration(proxyBeanMethods = false)
**@ConditionalOnClass(CacheManager.class)**
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class,
      HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import({ CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class })
public class **CacheAutoConfiguration** {

8.3、例子3

看DispatcherServletAutoConfiguration是否生效。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) 
@ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) 
public class DispatcherServletAutoConfiguration {
}

@ConditionalOnClass(DispatcherServlet.class)**:**首先找下DispatcherServlet是否存在:双击shift,搜索DispatcherServlet,在spring-web中故存在。

8.4、实例4

看 DispatcherServletAutoConfiguration中的内部类DispatcherServletConfiguration是否生效。

@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class **DispatcherServletConfiguration** {

@ConditionalOnClass(ServletRegistration.class):首先找下ServletRegistration是否存在:双击shift,搜索ServletRegistration,在tomcat-embed-core中故存在。

@EnableConfigurationProperties(WebMvcProperties.class):开启类WebMvcProperties与配置文件的绑定,即配置文件以spring.mvc为前缀的配置 可以修改 该类中的属性值。然后将WebMvcProperties放到容器中。

@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
   private DefaultMessageCodesResolver.Format messageCodesResolverFormat;
   private Locale locale;
   private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;

}
//查找 WebMvcProperties 类型的组件在容器中存在几个
        String[] WebMvcProperties = context.getBeanNamesForType(WebMvcProperties.class);
        System.out.println("WebMvcProperties.length:" + WebMvcProperties.length);
        //WebMvcProperties.length:1

8.5、实例5

HttpEncodingAutoConfiguration:解决请求编码问题,编码的自动解析,解析中文乱码。

如果用户没配置,则系统默认配置,如果用户自己配置了就用用户的。

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

**约定大于配置:**SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先。约定是指用户手动配置的,配置是指Springboot默认配置的。

9、总结:

  • SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration。

  • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。(xxxxProperties里面读取,xxxProperties和配置文件进行了绑定)。

  • 生效的配置类就会给容器中装配很多组件。

  • 只要容器中有这些组件,相当于这些功能就有了。

  • 定制化配置

  • 方法1:用户直接自己@Bean替换底层的组件

  • 方法2:在配置文件修改值,用户去看这个组件是获取的配置文件什么值就去修改。

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

参考:03、了解自动配置原理 · 语雀

相关文章

微信公众号

最新文章

更多