原创

springboot核心技术与响应式编程一

温馨提示:
本文最后更新于 2022年10月27日,已超过 918 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

一. 基础入门

1.相关资料

原文档地址 https://www.yuque.com/atguigu/springboot

2.springboot优点

  • Create stand-alone Spring applications

    创建独立Spring应用

  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)

    内嵌web服务器

  • Provide opinionated 'starter' dependencies to simplify your build configuration

    自动starter依赖,简化构建配置

  • Automatically configure Spring and 3rd party libraries whenever possible

    自动配置Spring以及第三方功能

  • Provide production-ready features such as metrics, health checks, and externalized configuration

    提供生产级别的监控、健康检查及外部化配置

  • Absolutely no code generation and no requirement for XML configuration

    无代码生成、无需编写XML

springboot是整合spring技术栈得一站式框架

springboot是简化spring技术栈快速开发脚手架

二.时代背景

1.微服务

  • 微服务是一种架构风格
  • 一个应用拆分为一组小型服务

  • 每个服务运行在自己的进程内,也就是可独立部署和升级

  • 服务之间使用轻量级HTTP交互

  • 服务围绕业务功能拆分

  • 可以由全自动部署机制独立部署

  • 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术

2. 分布式

1. 分布式困难

  • 远程调用
  • 服务发现

  • 负载均衡

  • 服务容错

  • 配置管理

  • 服务监控

  • 链路追踪

  • 日志管理

  • 任务调度

2. 分布式解决

  • SpringBoot + SpringCloud

3. 云原生

三. helloword入门

1. 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>

2. 简化部署--打包配置

 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3. 自动配置原理

1.自动配置tomcat

自动引入了tomcat依赖

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

自动配置tomcat

自动配置springMVC

​ 引入springMVC的全套组件

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.3.8</version>
  <scope>compile</scope>
</dependency>

​ 自动配置好springMVC的常用组件

自动配置web

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.3.8</version>
  <scope>compile</scope>
</dependency>

默认包结构

​ 主程序所在的包及其下面的所有子包里面的组件都会被扫描

​ 其他的包可以在启动类上添加scanBasePackages

@SpringBootApplication(scanBasePackages = {""})

​ 添加包扫码结构

@ComponentScan("com.wu")

各种配置拥有默认值

​ 默认的配置,最终都是映射到某一个类上

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

按需加载所有自动配置项

​ 非常多的starter

​ 引入了哪些场景,该场景的配置才会开启

​ springboot所有的自动配置功能都在autoconfigure场景下

四. 核心注解

1.@Configuration

告诉springboot这是一个配置类,等于以前的配置文件

package com.wu.springboot.config;

import com.wu.springboot.pojo.Pet;
import com.wu.springboot.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 1.在配置类里面使用@Bean注解在方法上,给容器添加组件。这些组件都是单例模式。当组件有对象参数传递时,会在当前容器中查询是否存在当前类型的组件。然后使用查询到的组件作为参数传递
 * 2. 配置类本身也是组件
 * 3. proxyBeanMethods 代理实例方法
 *      full(proxyBeanMethods = true) 全代理 springboot在启动的时候,会检查每个组件的实例是否存在
 *      lite(proxyBeanMethods = false)轻量代理 springboot 在启动的时候,不会检查每个组件的实例(跳过检查。
 *      在其他地方使用对象调用方法时,生成的实例也不是单例
 *      组件依赖
 *      如果只是在容器中注册组件,其他地方对其没有依赖。修改为false,能提升springboot的启动速度
 */
@Configuration(proxyBeanMethods = true)  //告诉springboot,这是一个配置类,相当于之前的配置文件
public class MyConfigruation {

    @Bean //给容器添加组件,以方法名作为组件的id。返回类型就是组件类型, 返回的值就是组件在容器中的实例
    public User userO1(){

        return new User("张三",20);
    }

    @Bean("tom01")  //可以不使用方法名作为组件的id。在Bean注解内自定义组件id
    public Pet tom(){

        return new Pet("tom",2);
    }


}
package com.wu.springboot;

import com.wu.springboot.config.MyConfigruation;
import com.wu.springboot.pojo.Pet;
import com.wu.springboot.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//ComponentScan已经在SpringBootApplication有了。默认扫描的是当前类所在的包
@ComponentScan("com.wu")
@SpringBootApplication
public class SpringbootMain {

    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootMain.class, args);
        //配置类也是一个实例
        MyConfigruation myConfigruation = run.getBean("myConfigruation", MyConfigruation.class);
        System.out.println(myConfigruation);
        //从容器中获取user和pet的实例,对比调用对象方法生成实例
        User userO1 = run.getBean("userO1", User.class);
        Pet tom01 = run.getBean("tom01", Pet.class);
        Pet tom = myConfigruation.tom();
        User user = myConfigruation.userO1();
        System.out.println(tom01==tom); //1配置配代理方法为true时@Configuration(proxyBeanMethods = true)   为true  proxyBeanMethods = false-->false
        System.out.println(userO1==user);//1配置配代理方法为true时@Configuration(proxyBeanMethods = true)  为true  proxyBeanMethods = false-->false


    }
}

2. @import注解

导入注解

 * 4.Import注解
 * 在容器中自动创建对应类型的组件。默认组件名字就是全类名
 * 如@Import({DBHelper.class, JNDIConnectionSource.class})
 * 会自动创建DBHelper和JNDIConnectionSource的实例

打印容器内的实例

        for (String name :run.getBeanDefinitionNames()){
            System.out.println(name);
        }
        输出中包含
        ch.qos.logback.core.db.DBHelper
        ch.qos.logback.core.db.JNDIConnectionSource

3.@Conditional 注解

条件装配,满足对应的条件,则进行组件注入

    @ConditionalOnBean(name = "tom01")//条件装配,当存在实例tom01的时候,才装配该组件
    @ConditionalOnMissingBean(name = "tomm") //条件装配,当容器中不存在tomm实例时,才装配该组件

用在配置类上面,则作用于整个配置类下面的组件

如果用在方法上,只作用于当前方法

注意:实例在生成的时候有先后顺序!!!

4. @ImportResource 导入原生的资源配置文件

@ImportResource("classpath:beans.xml")//导入原生的资源配置文件

5. 配置绑定

组件注入时,使用配置文件中对应的属性值

yml中配置

#自定义配置
car.carName: BYD
car.price: 10000

实例配置

@Data
@Component//将该类注册到IOC容器中。只有容器中的组件,才能使用springboot提供的相关功能
@ConfigurationProperties(prefix="car")  //使用配置文件中,前缀和car匹配的值.
public class Car {

    private String carName;

    private String price;
}

使用(自动注入使用):

@Resource
private Car car;

另一种配置方式,在配置文件中使用

@EnableConfigurationProperties(Car.class) //开启car类的属性配置功能。将car组件添加到容器中

五. 自动配置原理

SpringBootApplication包含注解分析

1.@SpringBootConfiguration

主要注解Configuration 代表该类是一个配置文件

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


   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

2. @ComponentScan

组件扫描

3. @EnableAutoConfiguration

自动配置包,指定了默认的包规则

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

1. @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)

@Import 导入对应的组件

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

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

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

}

register()将该包下的所有组件导入到IOC容器中

new PackageImports(metadata).getPackageNames()获取到的包名时Main方法所的包。侧面印证了springboot在启动的时候,只有Main方法所在包及子包下的组件才会生效的原因

register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
   if (registry.containsBeanDefinition(BEAN)) {
      BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
      beanDefinition.addBasePackages(packageNames);
   }
   else {
      registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
   }
}

2. @Import(AutoConfigurationImportSelector.class)

先获取所有的组件配置信息,再通过各种条件注入动态的注入组件

@Import(AutoConfigurationImportSelector.class)//利用该方法给容器导入组件

引用流程链

  1. selectImports方法 查询导入的配置
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
  1. getAutoConfigurationEntry() 获取自动配置的组件

        protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            }
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
            List<String> configurations = getCandidateConfigurations(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);
        }
    
  2. getCandidateConfigurations(annotationMetadata, attributes) 获取所有配置组件信息

    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;
    }
  1. SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),

    getBeanClassLoader()); 使用工厂加载器加载
    
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
       ClassLoader classLoaderToUse = classLoader;
       if (classLoaderToUse == null) {
          classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
       }
       String factoryTypeName = factoryType.getName();
       return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
  2. loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
       Map<String, List<String>> result = cache.get(classLoader);
       if (result != null) {
          return result;
       }
    
       result = new HashMap<>();
       try {
          Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
          while (urls.hasMoreElements()) {
             URL url = urls.nextElement();
             UrlResource resource = new UrlResource(url);
             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
             for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                      StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                   result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                         .add(factoryImplementationName.trim());
                }
             }
          }
    
          // Replace all lists with unmodifiable lists containing unique elements
          result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
          cache.put(classLoader, result);
       }
       catch (IOException ex) {
          throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
       }
       return result;
    }
    
  3. Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); 从配置文件中获取对应的加载信息

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
  4. spring.factories的所有自动配置

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
    org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
    ...........
    

六.最佳实践

引入场景依赖。springboot列举了所有第三方配置的依赖

https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters

查看自动配置了哪些

​ 配置文件中使用debug=true

是否需要配置项

​ 参照文档修改配置项https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties

​ 自己分析xxxproperties绑定了哪些内容

​ 自定义加入或替换组件

​ 自定义器 xxxCustomizer

七.开发小技巧

1. lombok

2. dev-tools

​ 热更新

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

3. spring initailizr

正文到此结束