文章目录

  • SpringBoot
  • 系统配置要求
  • 第一个Hello
  • 1. 版本继承父工程
  • 2. 添加web相关依赖
  • 3. 编写所需要的主程序controller
  • 4. 打包
  • 5. 部署
  • 容器功能
  • 组件添加
  • 1、@Configuration& @Bean
  • 2、proxyBeanMethods的差别
  • 3、使用@Import(Class<?>[])
  • 4、条件装配@Conditional
  • 5、@ImportResource迁移在xml配置的bean
  • 配置绑定
  • 方式一、@Component & @ConfigurationProperties
  • 方式二、@EnableConfigurationProperties
  • 自动配置原理
  • @SpringBootConfiguration
  • @ComponentScan
  • @EnableAutoConfiguration
  • AopAutoConfiguration自动配置分析
  • DispatcherServletAutoConfiguration自动配置分析
  • 总结



idea2024每次重启都要更新索引_idea2024每次重启都要更新索引


微服务是一种架构风格,将一个应用拆分为一组小型服务,每个服务运行在自己的进程里,也就是可独立部署和升级,且去中心化,服务自治也就是不同的服务可以用不同的语言和不同的存储技术。

springboot 写一个微服务
sprngcloud 将很多个微服务整合起来
spring data flow 负责传输微服务之间的数据信息

云原生 cloud native

  • 服务自愈:服务器在炸掉的时候,自动部署另外一台
  • 弹性伸缩: 需求增多的时候,部署的服务器自动扩充,当需求变少,再自动恢复原来的部署情况
  • 服务隔离:一台服务器上有许多的服务,但是A服务发生故障不要影响其他的服务
  • 自动化部署:在云上自动部署
  • 灰度发布:当新版本不稳定,先在一个服务器上部署,如果确定了api都没啥问题,再在提供相同服务的服务器上逐渐部署
  • 流量治理:由于服务器性能不一样,可以限制被访问的流量,以及监测

SpringBoot

springboot1 jdk7以下
springboo2 jdk8以上

  • 支持jdk8的新特性
  • lambda表达式
  • stream api
  • 函数式接口
  • 响应式编程
  • HTTP2

因此需要时刻关注springboot版本的变化

系统配置要求

  • Java8+
  • Maven 3.3+
  • 尽量配好aliyun镜像
  • jdk1.8版本
  • idea 2019.1.2

检查环境 mvn -v

idea2024每次重启都要更新索引_配置文件_02

第一个Hello

1. 版本继承父工程

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

原理:

可以看到,我们自己所创建的项目是继承了父项目spring-boot-starter-parent

spring-boot-starter-parent 中又继承了spring-boot-dependencies

而在spring-boot-dependencies里包含了几乎所有的常用jar包版本号以及springboot当前版本号所依赖的jar包

idea2024每次重启都要更新索引_spring_03


spring-boot-starter-* ,其中*代表某个场景,代表只要引入starter,这个场景所需要的依赖就会被引用

见到 *-spring-boot-starter,就是第三方为我们提供的简化开发的场景启动器

idea2024每次重启都要更新索引_配置文件_04


因此在依赖管理部分,

引入依赖默认都可以不写版本,
引入非版本仲裁的jar,要写版本号,也就是自动版本仲裁机制,但是如果想使用特定的版本驱动,就在pom.xml声明即可

所有的springboot的自动配置功能都在这个包里面

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

2. 添加web相关依赖

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

3. 编写所需要的主程序controller

//主程序类,表示这事一个springboot应用
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class,args);
    }
}
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01() {
        return "Hello,Spring Boot 2!";
    }
}

4. 打包

到这里已经成功运行了,通过spring提供的方法,创建一个可执行的jar包,其中包含了整个运行环境,此时这个spring提供的方法需要添加一个插件

注意:添加一个version与自己的SpringBoot的版本号要一致,我这里是2.5.4

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

在maven管理的插件使用clear+package进行打包

idea2024每次重启都要更新索引_idea2024每次重启都要更新索引_05

5. 部署

可以看到打好的jar包

idea2024每次重启都要更新索引_System_06


然后就可以通过该命令,进行部署

java -jar jar包名

容器功能

组件添加

使用到的注释

@Configuration(proxyBeanMethods = false)
@Bean("id")
@Import(Class<?>[])
@Conditional
1、@Configuration& @Bean

对比Spring和SpringBoot注册组件的差别

  • 使用Spring配置文件配置
  • 创建beans.xml的Spring配置文件
  • 在该文件下注册
<bean id="user01" class="com.lee.boot.bean.User">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>

    <bean id="cat" class="com.lee.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>
  • 使用@Configurationg告知这是个配置文件
  • 添加@Configuration
  • 配置bean
  • 返回类型 = 上面的class = “com.lee.boot.bean.Pet”
  • tomcatPet = 上面的id = “cat” ,这里还可以在@Bean中自定以bean的名,优先于方法名
  • 返回值就是组件在容器中的实例
@Bean("tom")
public Pet tomcatPet(){
    return new Pet("tomcat");
}

因此在SpringBoot中创建一个MyConfig类来管理组件

2、proxyBeanMethods的差别
  • @configuration告诉SpringBoot当前类是配置文件,可以在里面通过@Bean配置组件
  • 全配置Full
  • @Configuration(proxyBeanMethods = true)
  • 从容器中通通过代理类去找组件,因此已经保存了,不用再通过bean去new一个新对象
  • 轻量级配置Lite
  • @Configuration(proxyBeanMethods = true)
  • 容器不会再去保留代理对象,每一次的调用都会产生一个新的代理,新的代理去找组件的时候又产生一个新方法,新方法又返回new

来一个测试对比区别

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {

        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        //从容器中获取组件
        String[] names = run.getBeanDefinitionNames();

        for (String name : names) {
            System.out.println(name);
        }

        System.out.println("==============测试用run.getBean来调用注册的bean============");
        Pet tom1 = run.getBean("tom", Pet.class);
        Pet tom2 = run.getBean("tom", Pet.class);
        System.out.println("是否相同!"+ (tom1==tom2));

        System.out.println("==============测试用myConfig来调用注册的bean============");
        //如果proxybeanmehtods = true 那么获取的代理对象,调用方法,如果有就直接拿,如果没有再注册
        //反正就是springboot总会检查这个组件在容器中的实例,本质是因为是通过代理对象去调用方法,如果不用代理对象的话,就会每次都去创建一个
        MyConfig myConfig = run.getBean("myConfig", MyConfig.class);
        Pet pet1 = myConfig.tomcatPet();
        Pet pet2 = myConfig.tomcatPet();
        System.out.println("是否相同!"+(pet1==pet2));

    }

}

总结:

false:就代表不会在容器中进行组件检查,整个springboot启动就会变的非常的快,因此如果不存在组件依赖的情况,一般都调成false,对应的就是lite轻量级配置
true:就代表每一次调用都需要在容器中找,是不是有这个组件,保证依赖的组件就是容器中的组件这样启动的慢因此如果存在这个组件还要被别的使用,就要开启代理这样对应的就是full全配置

在我理解就是一个是单例模式,一个是原型模式

3、使用@Import(Class<?>[])

在配置文件使用@Import可以向容器中创建指定类型的组件

@Import({User.class,Pet.class})

测试在容器中是否存在该类型的bean

String[] userTypes = run.getBeanNamesForType(User.class);
        for (String type : userTypes) {
            System.out.println(type);
        }

        String[] petTypes = run.getBeanNamesForType(Pet.class);
        for (String petType : petTypes) {
            System.out.println(petType);
        }

idea2024每次重启都要更新索引_配置文件_07

4、条件装配@Conditional

作用:spring4加载的特性,按照一定的条件进行判断,满足条件就给容器注册bean

应用背景:

  • 对于组件依赖的问题,就比如在这上面的例子中,程序中都没有user,那么就user自然也不需要一个pet,因此就需要用到条件装配
  • 反之,如果pet都没有,这个bean没有怎么能注入到user里呢,如果真发生了,在之前还会空指针,其实也就是springboot自动装配的原理

@ConditionalOnBean // 当给定的在bean存在时,则实例化当前Bean
@ConditionalOnMissingBean // 当给定的在bean不存在时,则实例化当前Bean
@ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean

使用@conditional,介个写的非常详细
Spring @Conditional注解 详细讲解及示例

  • 标记在方法上
    一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入
  • 标记在类上
    一个类中可以注入很多实例,@Conditional标注在类上就决定了一批bean是否注入。
5、@ImportResource迁移在xml配置的bean

在会被SpringBoot扫描到配置文件的开头加入@ImportResource

@ImportResource("classpath:xxxxxxxbeans.xml")

配置绑定

方式一、@Component & @ConfigurationProperties

意思就是在连接数据库的时候,一般会写一个.properties存放连接数据库所需要的信息。

如果想让.properties中文件的内容与bean的属性进行绑定,那么就可以通过以下方式

首先一个来配置信息的文件

mycar.brand=BYD
mycar.price=10000

再来一个Car类

@lombok
@Component
@ConfigurationProperties(prefix = "mycar")
//这里就会自动扫描配置文件的前缀为mycar的,然后进行属性匹配
public class Car {
    private String brand;
    private Integer price;
 }
方式二、@EnableConfigurationProperties

应用背景:当需要引用第三方的jar包的时候,不能把别人的加入一个@component,那么就在自己定义的配置文件类MyConfig中将需要把配置属性的功能打开。

//1.开启car的属性配置功能 = @ConfigurationProperties
//2.MyConfig自身就是一个组件 = @Component
//3.把这个Car自动注册到容器中
@EnableConfigurationProperties(Car.class)

自动配置原理

涉及到的注解

@SpringBootApplication

实则是由下面三个组合在一起的

@ComponentScan(“com.lee.boot”)
@EnableAutoConfiguration
@SpringBootConfiguration

idea2024每次重启都要更新索引_idea2024每次重启都要更新索引_08


那么这三个注解分别做了啥呢?

@SpringBootConfiguration

源码

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

可以看到里面有一个configuration,说明这是会被SpringBoot扫描成为一个配置类

@ComponentScan

没啥好说的貌似

@EnableAutoConfiguration

源码

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

可以看到这个注解是由@AutoConfigurationPackage @Import组合成的

  1. 来看自动配置包@AutoConfigurationPackage
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

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

能够看到自动配置包里有个@import,而import就是在容器中创建一个指定类型的bean,因此就是利用这个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));
        }
    }

得到主类的元信息,得到主类的包名,将指定的一个包下的组件导入进来,而这个指定的包就是Main程序,为啥是Main程序,是因为在头上加了EnableAutoConfiguration

  1. @Import({AutoConfigurationImportSelector.class})
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);
        }
    }

实际位置是spring-boot-autoconfigure-2.5.4 jar包下的META-INF/spring.factories中的写死的127个需要加载的组件

虽然127个场景的所有自动配置在默认启动的全部默认自动加载,但是最终会按需配置因此就会用到之前提到的@ConditionalOnClass,就能够解决不同场景需要加载啥。

可能不太清楚具体流程,那么来看个例子8

AopAutoConfiguration自动配置分析

例子:分析一个aop是否能够在自动配置生效

1、首先能看到在AopAuto这个类下

  • 首先有个注释@configuration,代表是一个配置类
  • 其次@ConditionalOnPropert后面那部分就是是否存在一个为spring.aop.auto的配置为真,matchIfMissing代表从application.properties中读取某个属性值,如果该值为空,默认值为true
  • 最后还有个proxyBeanMethods,其属性默认值是true,也就是说该配置类会被代理。如果true了就会被代理,其实是为了启动变得更快,false的时候不进容器,true的时候进容器,并且进容器前还需要经过一层代理,导致启动比false的时候慢了。因此设置为false的目的,就是容器启动更快了,对于那些不常用的bean完全可以设置为false。
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnProperty(
    prefix = "spring.aop",
    name = {"auto"},
    havingValue = "true",
    matchIfMissing = true
)
public class AopAutoConfiguration{
	public AopAutoConfiguration() {
	    }
}

2、内部类AspectJAutoProxyingConfiguration

同样代表一个配置文件,且需要检查是否存在Advice.class这个类,然而这里的advice

idea2024每次重启都要更新索引_配置文件_09


不在,因此该配置不生效。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({Advice.class})
static class AspectJAutoProxyingConfiguration {
     AspectJAutoProxyingConfiguration() {
     }
}

3、ClassProxyingConfiguration

  • @ConditionalOnMissingClass({“org.aspectj.weaver.Advice”})也就是当系统中没有weaver.Advice这个类的时候就生效
  • @ConditionalOnProperty 看配置文件中是否前缀为spring-aop,属性名为"proxy-target-class",的值为true,如果为true就生效。

在我这里是,

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass({"org.aspectj.weaver.Advice"})
@ConditionalOnProperty(
     prefix = "spring.aop",
     name = {"proxy-target-class"},
     havingValue = "true",
     matchIfMissing = true
 )
static class ClassProxyingConfiguration {
     ClassProxyingConfiguration() {
     }
}
DispatcherServletAutoConfiguration自动配置分析

1、

1.配置优先级的
@AutoConfigureOrder(-2147483648)

2.代表是个配置文件
@Configuration(
    proxyBeanMethods = false
)

3.如果存在servlet才生效,因为还可能遇到基于flux的web应用,到这里也生效
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

4.再看系统中有没有这个类型的DispatcherServlet,
@ConditionalOnClass({DispatcherServlet.class})

5.最后需要在ServletWebServerFactoryAutoConfiguration这个类配好了,
系统才会配置DispatcherServletAutoConfiguration,
也很好理解,web服务器都没有,你配置servlet也没用啊
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
public class DispatcherServletAutoConfiguration{}

紧接着里边分别有两个类

2、DispatcherServletConfiguration

1.标明配置类
@Configuration(proxyBeanMethods = false)

2.要求DefaultDispatcherServletCondition生效
@Conditional({DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition.class})

3.系统中有没有ServletRegistration的组件
@ConditionalOnClass({ServletRegistration.class})

4.这个enable有两个功能,一是配置绑定prefix = "spring.mvc"所有的配置文件
二放到容器中就会存在有这个WebMvcProperties的组件
@EnableConfigurationProperties({WebMvcProperties.class})
protected static class DispatcherServletConfiguration{
5.
在这里就能看到,他注册了一个叫dispatcherServlet的bean,
在第4步进行了配置文件绑定的那些信息,就被传入到了这个bean中,
最后再返回了一个dispatcherServlet,而这个就是spring mvc的核心
这不就是平时我们需要在xml中写入的dispatcher配置,然后放入容器中
但是在springboot的底层中,就把这个过程自动给你装配了。

	@Bean(name = {"dispatcherServlet"})
     public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
         dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
         dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
         dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
         dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
         return dispatcherServlet;
     }
}

同理,和配置上述的一样,配置了一个文件上传解析器

@Bean
1.系统中是否存在这个MultipartResolver类型的组件,这里是肯定有的
@ConditionalOnBean({MultipartResolver.class})

2.但是系统中可能并没有配置这个bean叫做“multipartResolver”的组件,这里如果也为真,继续执行
@ConditionalOnMissingBean(
    name = {"multipartResolver"}
)

3.给@bean标注的方法传入了参数,这个参数的值就会从容器中找,就在容器中找一个类型为MultipartResolver的解析器
然后再直接返回给你,相当于使得避免了一些开发的不规范会导致的问题
public MultipartResolver multipartResolver(MultipartResolver resolver) {
    return resolver;
}
总结

在springboot的自动配置中,如果用户没配比如servlet,resolver,或者characterEncoding之类的组件的话,框架自动给你配好

但是如果用户自定义配了,那么以用户的优先,这也就是那个conditional的特色之处。

  • SpringBoot先加载所有的配置类 xxxxAutoConfiguration
  • 配置类按照条件进行生效,默认都会绑定配置文件的值 xxxxProperties
  • 生效的话就会配置很多组件
  • 只要容器中有这些组件,就相当于这些功能就有了
  • 只要有用户自己配置的,就以用户优先
  • 用户直接自己@Bean替换底层组件
  • 用户去看组件获取的配置文件中属性的什么值,那么就去修改即可
  • 要么查官方文档
  • 要么就去看底层信息

在缓存中,和这个CacheProperties进行了绑定

idea2024每次重启都要更新索引_System_10


进入CacheProperties类定义中,发现他和spring.cache开头的配置文件属性名进行了绑定!

idea2024每次重启都要更新索引_idea2024每次重启都要更新索引_11


因此,俺们就可以在application.properties中开始以spring.cache开头的属性名进行配置了!

idea2024每次重启都要更新索引_spring_12

idea2024每次重启都要更新索引_配置文件_13