3.黑马Springboot原理篇自己修改笔记

news2024/10/6 4:12:53

原理篇

1.自动配置的工作流程

1.1 bean的加载方式

方式一:配置文件+<bean/>标签

image-20230403161415267

  • 缺点:配置bean太繁琐

方式二:配置文件扫描+注解定义bean⭐️

  • 获取bean方式

  • ①通过配置文件,扫描指定包,加载bean

    ②通过注解声明bean,本地的加上@component注解;第三方声明一个配置类,提供一个返回第三方对象的方法(方法名为bean的id名),然后配置为bean即可

    ③@configuration注解和@component注解功能相似,但是@configuration注解实现的是单例配置bean

image-20230403163520478

image-20230403163626044

  • 缺点:只需要一个配置文件,可否干掉配置文件?

方式三:注解方式声明配置类

  • 声明一个总配置类,然后加上@componentScan注解,扫描要加载bean的包即可
  • image-20230403165337560
1.使用FactroyBean接口⭐️

配置bean特例:@Bean注解所得到的bean对象不一定是该类本身,如果一个类实现了FactoryBean接口,那么配置到@bean中,返回的对象是其实现FactoryBean接口中的getObject方法返回的对象

  • 实现FactoryBean接口好处:

    可以在给spring配置一个bean的时候,**检查bean对象是否满足一些条件,**或者是给bean初始化一些属性,然后再交给bean管理

  • 使用FactoryBean方法:

    ①实现接口,要注意加上泛型

    ②实现三个方法(分别为真正配置的bean,bean的class类型,交给spring管理的模式)

    ③配置bean注解,交给spring管理

    交给spring一般是单例模式的类

  • getBean(“dog”)得到的是book对象,但是如果通过这个配置类调book方法,返回的是BookFactoryBean对象,也就是说BookFactoryBean会造对象,但是不会送到Spring容器中

  • image-20230403170454186

2.注解格式导入XML格式配置的bean
  • 解决问题:如果老项目中有用到xml文件配置bean,迁移到新工程中需要用一个注解**@importResource**导入xml注解配置的bean,这样不用吧原来xml配置的bean再一个个修改成注解的了,以后再用注解开发即可。

    image-20230403171618380

proxyBeanMethods属性(探讨@configuration注解)⭐️
  • @component注解和@configuration注解区别

    ① @component注解注释的类交给spring管理是普通的bean,如果再用这个类的下的方法获取对象的时候,那么获取的是不同的对象;

    image-20230403174549163

    ② 而@configuration注解注释的类,交给spring管理是cglib生成的代理类对象,无论是获取IOC容器中这个配置类下的对象还是从这个配置类的方法中获取,获取的都是同一个bean,实现了单例

    proxyBeanMethods属性可控制代理类的开关

    image-20230403174823020

image-20230403174838336

  • 一般开发中推荐使用@configuration注解,交给spring容器管理的都是单例配置对象,节省内存

  • 总结:

    与工厂bean中的单例方法区别:

    工厂bean中如果单例方法返回true,那么这个bean交给IOC容器单例管理,不用区分@component注解;

    而如果要获取这个工厂类,就要根据两个注解区别,来区别交给IOC容器的模式,并且工厂类不会交给spring容器管理

image-20230403172821607

方式四:使用@Import注解注入bean

  • 加载名称为全路径名

  • @Import注解导入响应的class文件,并将其放入spring容器中,无侵入式编程

  • @Import放入容器的话是单例的,但是如果放入容器的配置类不加@configuration注解,那么通过方法获取到的不是和容器中同一个对象,加了@configuration注解得到和容器中一样。

  • @import注解常用来导入配置类和第三方class文件,省去了扫描包的过程

  • image-20230403193802119

方式五:编程形式注册bean

  • 必须为annotationConfigApplicationContext类声明,不能用ApplicationContext,ApplicationContext没有相应的注册方法

image-20230404153245215

方式六:导入实现了ImportSelector接口的类⭐️

  1. 源码常用
  • 功能:各种条件的判定,判定完之后才决定加载不加载bean

  • 检测位置:哪导入了这个实现类,检测哪里

  • 属性注释:metadata是元数据,对原位置的描述信息

  • image-20230404154428371

方式七:导入实现了ImportBeanDefinitionRegistrar接口的类

  • 这一个方式相对于方式六,开放了bean的注册方式,权限更大了

    image-20230404155914976

方式八:导入实现了BeanDefinitionRegistryPostProcessor接口的类

  • 解决优先级问题

  • 实现对导入的bean的覆盖的最终裁定,就是说如果有多个重复的bean加载,实现了PostProcessor的实现类中的bean中配置,会做到优先覆盖加载

    image-20230404161149209

总结:

image-20230404161530953

1.2 bean的加载控制

  1. 基于编程式控制
  • 前面import之后的加载方式

  • image-20230404163259550

  • image-20230404163558981

  1. 基于注解管理bean⭐️
  • @conditional等子注解用来决定是否配置bean

  • @conditional注解Spring里面提供,但是需要自己写实现类实现接口,Springboot扩展了@condition注解,有子注解使用,用的时候直接导入Springboot包即可

  • image-20230404165256334

    image-20230404165359250

    image-20230404165348009

    image-20230404165440306

  1. 控制作用:省去无用的配置依赖,spring管理资源更加高效准确

1.3 bean的依赖属性配置管理⭐️(自动配置类的开发)

  • 设计boot底层思想:约定大于配置(实现解耦功能)

    没有配置就用默认,有配置就根据属性类导入配置属性进行加载

  • 本节实现步骤

    image-20230404174712415

    1. @EnableConfigurationProperties为关联注解,强制某一个类加载成bean
    2. 用业务类中的时候,导入@import类
    3. 将业务功能bean运行需要的资源抽取成独立的属性类(******Properties),设置读取配置文件信息,实现解耦
    4. 定义业务功能bean,通常使用**@Import导入,解耦强制加载bean**,增强spring管控bean的能力
    5. 业务功能使用@EnableConfigurationProperties注解设定使用属性类时加载bean
  • image-20230405100644483

1.4 自动配置原理⭐️

  • 概述:

    image-20230405100852642

  • 具体注解作用:

    对应第四步,将技术集A全部加载

    1. @SpringBootApplication -->@EnableAutoConfiguration -->@AutoConfigurationPackage --> @Import(AutoConfigurationPackages.Registrar.class)

      设置当前配置包作为配置包(“com.itiheima”),后序会对当前包进行扫描

    2. @SpringBootApplication -->@EnableAutoConfiguration -->@Import(AutoConfigurationImportSelector.class)

      ①凡是以aware结尾的接口,作用就是让当前类拥有一个aware前缀的对象,如applicationContextAware接口,实现了此接口,该对象中就有了一个applicationContext对象

      ②Inordered接口:实现了此接口,就赋予该加载的bean的优先级

      DeferredImportSelector接口:延迟选择器接口,里面有一个Group接口,Group接口里面有一个process方法,process方法实现了加载技术集A的配置,调试的话process方法打断点

      image-20230405110717276

      process方法里面的getAutoConfigurationEntry()才是加载核心方法,加载spring.fatories文件中的技术名称

      image-20230405110813897

      image-20230405110923066

      spring.fatories文件在autoconfigure配置里面

      image-20230405111048388

对应第六、七步,将设置集B默认加载

xxxConfiguration.class --> xxxProperties.class --> application.yml

spring.fatories文件里的配置信息为xxxConfiguration,现在以RedisAutoConfiguration为例拆解自动配置属性

  1. 先做条件检测,如果环境中包含某某类,才会加载为bean(环境检测,降低spring管理bean的负载)
  2. 环境满足之后,去加载配置类为bean,配置类又依赖.yml文件,这样的话一个xxxConfiguration配置类含有配置信息bean
  3. 当技术中有需要配置条件的时候,直接去IOC容器中获取即可

image-20230405114119895

image-20230405114229147

  • 总结
    1. springboot启动时先加载spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration配置项,将其中配置的所有的类都加载成bean
    2. 在加载bean的时候,bean对应的类定义上都设置有加载条件,因此有可能加载成功,也可能条件检测失败不加载bean
    3. 对于可以正常加载成bean的类,通常会通过@EnableConfigurationProperties注解初始化对应的配置属性类并加载对应的配置
    4. 配置属性类上通常会通过@ConfigurationProperties加载指定前缀的配置,当然这些配置通常都有默认值。如果没有默认值,就强制你必须配置后使用了
  • image-20230405115046742

一句话总结:就是springboot加载的时候会全部加载配置类,如果检测到环境中存在环境,那么把配置类交给bean管理,实现自动配置,减轻spring的bean管控负载

1.5 变更自动配置

  • 唯一目的:帮开发者实现自定义自动配置类,管控bean的注入

image-20230405121344081

image-20230405121432478

从上述配置中可以看出,自动配置也是加载bean的方式

如果在上述配置中随便扔一个自定义类,那么会得到这个自定义类的bean,但是这个bean没有初始值

2.自定义starter开发

自动配置学习完后,我们就可以基于自动配置的特性,开发springboot技术中最引以为傲的功能了,starter。其实通过前期学习,我们发现用什么技术直接导入对应的starter,然后就实现了springboot整合对应技术,再加上一些简单的配置,就可以直接使用了。这种设计方式对开发者非常友好,本章就通过一个案例的制作,开发自定义starter来实现自定义功能的快捷添加。

YL-2-1.案例:记录系统访客独立IP访问次数

​ 本案例的功能是统计网站独立IP访问次数的功能,并将访问信息在后台持续输出。整体功能是在后台每10秒输出一次监控信息(格式:IP+访问次数) ,当用户访问网站时,对用户的访问行为进行统计。

​ 例如:张三访问网站功能15次,IP地址:192.168.0.135,李四访问网站功能20次,IP地址:61.129.65.248。那么在网站后台就输出如下监控信息,此信息每10秒刷新一次。

         IP访问监控
+-----ip-address-----+--num--+
|     192.168.0.135  |   15  |
|     61.129.65.248  |   20  |
+--------------------+-------+

​ 在进行具体制作之前,先对功能做具体的分析

  1. 数据记录在什么位置

    最终记录的数据是一个字符串(IP地址)对应一个数字(访问次数),此处可以选择的数据存储模型可以使用java提供的map模型,也就是key-value的键值对模型,或者具有key-value键值对模型的存储技术,例如redis技术。本案例使用map作为实现方案,有兴趣的小伙伴可以使用redis作为解决方案。

  2. 统计功能运行位置,因为每次web请求都需要进行统计,因此使用拦截器会是比较好的方案,本案例使用拦截器来实现。不过在制作初期,先使用调用的形式进行测试,等功能完成了,再改成拦截器的实现方案。

  3. 为了提升统计数据展示的灵活度,为统计功能添加配置项。输出频度,输出的数据格式,统计数据的显示模式均可以通过配置实现调整。

    • 输出频度,默认10秒
    • 数据特征:累计数据 / 阶段数据,默认累计数据
    • 输出格式:详细模式 / 极简模式

​ 在下面的制作中,分成若干个步骤实现。先完成最基本的统计功能的制作,然后开发出统计报表,接下来把所有的配置都设置好,最后将拦截器功能实现,整体功能就做完了。

image-20230405201938677

YL-2-2.IP计数业务功能开发(自定义starter)

原理就是将设置一个spring.factories文件,让文件随着boot工程加载而加载,顺便导入所有的bean

​ 本功能最终要实现的效果是在现有的项目中导入一个starter,对应的功能就添加上了,删除掉对应的starter,功能就消失了,要求功能要与原始项目完全解耦。因此需要开发一个独立的模块,制作对应功能。

步骤一:创建全新的模块,定义业务功能类

​ 功能类的制作并不复杂,定义一个业务类,声明一个Map对象,用于记录ip访问次数,key是ip地址,value是访问次数

public class IpCountService {
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
}

​ 有些小伙伴可能会有疑问,不设置成静态的,如何在每次请求时进行数据共享呢?记得,当前类加载成bean以后是一个单例对象,对象都是单例的,哪里存在多个对象共享变量的问题。

步骤二:制作统计功能

​ 制作统计操作对应的方法,每次访问后对应ip的记录次数+1。需要分情况处理,如果当前没有对应ip的数据,新增一条数据,否则就修改对应key的值+1即可

public class IpCountService {
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
    public void count(){
        //每次调用当前操作,就记录当前访问的IP,然后累加访问次数
        //1.获取当前操作的IP地址
        String ip = null;
        //2.根据IP地址从Map取值,并递增
        Integer count = ipCountMap.get(ip);
        if(count == null){
            ipCountMap.put(ip,1);
        }else{
            ipCountMap.put(ip,count + 1);
        }
    }
}

​ 因为当前功能最终导入到其他项目中进行,而导入当前功能的项目是一个web项目,可以从容器中直接获取请求对象,因此获取IP地址的操作可以通过自动装配得到请求对象,然后获取对应的访问IP地址。

public class IpCountService {
    private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();
    @Autowired
    //当前的request对象的注入工作由使用当前starter的工程提供自动装配
    private HttpServletRequest httpServletRequest;
    public void count(){
        //每次调用当前操作,就记录当前访问的IP,然后累加访问次数
        //1.获取当前操作的IP地址
        String ip = httpServletRequest.getRemoteAddr();
        //2.根据IP地址从Map取值,并递增
        Integer count = ipCountMap.get(ip);
        if(count == null){
            ipCountMap.put(ip,1);
        }else{
            ipCountMap.put(ip,count + 1);
        }
    }
}

步骤三:定义自动配置类

​ 我们需要做到的效果是导入当前模块即开启此功能,因此使用自动配置实现功能的自动装载,需要开发自动配置类在启动项目时加载当前功能。

public class IpAutoConfiguration {
    @Bean
    public IpCountService ipCountService(){
        return new IpCountService();
    }
}

​ 自动配置类需要在spring.factories文件中做配置方可自动运行。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.itcast.autoconfig.IpAutoConfiguration

步骤四:在原始项目中模拟调用,测试功能

​ 原始调用项目中导入当前开发的starter

<dependency>
    <groupId>cn.itcast</groupId>
    <artifactId>ip_spring_boot_starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

温馨提示

​ 由于当前制作的功能需要在对应的调用位置进行坐标导入,因此必须保障仓库中具有当前开发的功能,所以每次原始代码修改后,需要重新编译并安装到仓库中。为防止问题出现,建议每次安装之前先clean然后install,保障资源进行了更新。切记切记!!

当前效果

​ 每次调用分页操作后,可以在控制台输出当前访问的IP地址,此功能可以在count操作中添加日志或者输出语句进行测试。

YL-2-3.定时任务报表开发

​ 当前已经实现了在业务功能类中记录访问数据,但是还没有输出监控的信息到控制台。由于监控信息需要每10秒输出1次,因此需要使用定时器功能。可以选取第三方技术Quartz实现,也可以选择Spring内置的task来完成此功能,此处选用Spring的task作为实现方案。

步骤一:开启定时任务功能

image-20230406160928265

步骤二:制作显示统计数据功能

​ 定义显示统计功能的操作print(),并设置定时任务,当前设置每5秒运行一次统计数据。

image-20230406160949231

当前效果

​ 每次调用分页操作后,可以在控制台看到统计数据,到此基础功能已经开发完毕。

YL-2-4.使用属性配置设置功能参数

配置类的设置与使用

​ 由于当前报表显示的信息格式固定,为提高报表信息显示的灵活性,需要通过yml文件设置参数,控制报表的显示格式。

步骤一:定义参数格式

​ 设置3个属性,分别用来控制显示周期(cycle),阶段数据是否清空(cycleReset),数据显示格式(model)

image-20230406162533599

步骤二:定义封装参数的属性类,读取配置参数

​ 为防止项目组定义的参数种类过多,产生冲突,通常设置属性前缀会至少使用两级属性作为前缀进行区分。

​ 日志输出模式是在若干个类别选项中选择某一项,对于此种分类性数据建议制作枚举定义分类数据,当然使用字符串也可以。

image-20230406162242062

步骤三:加载属性类

image-20230406162306613

步骤四:应用配置属性

​ 在应用配置属性的功能类中,使用自动装配加载对应的配置bean,然后使用配置信息做分支处理。

​ 注意:清除数据的功能一定要在输出后运行,否则每次查阅的数据均为空白数据。

image-20230406162441913

image-20230406162502537

当前效果

​ 在web程序端可以通过控制yml文件中的配置参数对统计信息进行格式控制。但是数据显示周期还未进行控制。

YL-2-5.使用属性配置设置定时器参数

无论是什么地方,读取配置文件都应该想法从配置类对象中读取,否则是一种不规范行为

解决问题:

①@EnableConfigurationProperties不能指定名称,导致@Scheduled注解上无法识别Java对象属性

②不同项目引用的包应该不同,否则会引发加载bean重名问题

​ 在使用属性配置中的显示周期数据时,遇到了一些问题。由于无法在@Scheduled注解上直接使用配置数据,改用曲线救国的方针,放弃使用@EnableConfigurationProperties注解对应的功能,改成最原始的bean定义格式。

image-20230406162753837

步骤一:@Scheduled注解使用#{}读取bean属性值

​ 此处读取bean名称为ipProperties的bean的cycle属性值

@Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?")
public void print(){
}

步骤二:属性类定义bean并指定bean的访问名称

​ 如果此处不设置bean的访问名称,spring会使用自己的命名生成器生成bean的长名称,无法实现属性的读取

@Component("ipProperties")
@ConfigurationProperties(prefix = "tools.ip")
public class IpProperties {
}

步骤三:弃用@EnableConfigurationProperties注解对应的功能,改为导入bean的形式加载配置属性类

@EnableScheduling
//@EnableConfigurationProperties(IpProperties.class)
@Import(IpProperties.class)
public class IpAutoConfiguration {
    @Bean
    public IpCountService ipCountService(){
        return new IpCountService();
    }
}

当前效果

​ 在web程序端可以通过控制yml文件中的配置参数对统计信息的显示周期进行控制

YL-2-6.拦截器开发

功能:就是在starter中配置了一个web拦截器配置类,记得要加入总配置类中,这样就实现了每次导入starter,就自动加上了web拦截器

步骤一:开发拦截器

​ 使用自动装配加载统计功能的业务类,并在拦截器中调用对应功能

image-20230406171805840

步骤二:配置拦截器

​ 配置mvc拦截器,设置拦截对应的请求路径。此处拦截所有请求,用户可以根据使用需要设置要拦截的请求。甚至可以在此处加载IpCountProperties中的属性,通过配置设置拦截器拦截的请求。

image-20230406171938034

当前效果

​ 在web程序端导入对应的starter后功能开启,去掉坐标后功能消失,实现自定义starter的效果。

​ 到此当前案例全部完成,自定义stater的开发其实在第一轮开发中就已经完成了,就是创建独立模块导出独立功能,需要使用的位置导入对应的starter即可。如果是在企业中开发,记得不仅需要将开发完成的starter模块install到自己的本地仓库中,开发完毕后还要deploy到私服上,否则别人就无法使用了。

YL-2-7.功能性完善——开启yml提示功能

开启yml配置提示

​ 我们在使用springboot的配置属性时,都可以看到提示,尤其是导入了对应的starter后,也会有对应的提示信息出现。但是现在我们的starter没有对应的提示功能,这种设定就非常的不友好,本节解决自定义starter功能如何开启配置提示的问题。

​ springboot提供有专用的工具实现此功能,仅需要导入下列坐标。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

​ 程序编译后,在META-INF目录中会生成对应的提示文件,然后拷贝生成出的文件到自己开发的META-INF目录中,并对其进行编辑。打开生成的文件,可以看到如下信息。其中groups属性定义了当前配置的提示信息总体描述,当前配置属于哪一个属性封装类,properties属性描述了当前配置中每一个属性的具体设置,包含名称、类型、描述、默认值等信息。hints属性默认是空白的,没有进行设置。hints属性可以参考springboot源码中的制作,设置当前属性封装类专用的提示信息,下例中为日志输出模式属性model设置了两种可选提示信息。

{
  "groups": [
    {
      "name": "tools.ip",
      "type": "cn.itcast.properties.IpProperties",
      "sourceType": "cn.itcast.properties.IpProperties"
    }
  ],
  "properties": [
    {
      "name": "tools.ip.cycle",
      "type": "java.lang.Long",
      "description": "日志显示周期",
      "sourceType": "cn.itcast.properties.IpProperties",
      "defaultValue": 5
    },
    {
      "name": "tools.ip.cycle-reset",
      "type": "java.lang.Boolean",
      "description": "是否周期内重置数据",
      "sourceType": "cn.itcast.properties.IpProperties",
      "defaultValue": false
    },
    {
      "name": "tools.ip.model",
      "type": "java.lang.String",
      "description": "日志输出模式  detail:详细模式  simple:极简模式",
      "sourceType": "cn.itcast.properties.IpProperties"
    }
  ],
  "hints": [
    {
      "name": "tools.ip.model",
      "values": [
        {
          "value": "detail",
          "description": "详细模式."
        },
        {
          "value": "simple",
          "description": "极简模式."
        }
      ]
    }
  ]
}

总结

  1. 自定义starter其实就是做一个独立的功能模块,核心技术是利用自动配置的效果在加载模块后加载对应的功能
  2. 通常会为自定义starter的自动配置功能添加足够的条件控制,而不会做成100%加载对功能的效果
  3. 本例中使用map保存数据,如果换用redis方案,在starter开发模块中就要导入redis对应的starter
  4. 对于配置属性务必开启提示功能,否则使用者无法感知配置应该如何书写

3.springboot启动流程

掌握springboot如何加载容器,如何配置容器流程

核心:还是加载spring容器

  • springboot启动流程 = 初始化数据 + 加载容器

image-20230408144029417

image-20230408144133123

image-20230408144150747

​ 其实不管是springboot程序还是spring程序,**启动过程本质上都是在做容器的初始化,并将对应的bean初始化出来放入容器。**在spring环境中,每个bean的初始化都要开发者自己添加设置,但是切换成springboot程序后,自动配置功能的添加帮助开发者提前预设了很多bean的初始化过程,加上各种各样的参数设置,使得整体初始化过程显得略微复杂,但是核心本质还是在做一件事,初始化容器。作为开发者只要搞清楚springboot提供了哪些参数设置的环节,同时初始化容器的过程中都做了哪些事情就行了。

​ springboot初始化的参数根据参数的提供方,划分成如下3个大类,每个大类的参数又被封装了各种各样的对象,具体如下:

  • 环境属性(Environment)
  • 系统配置(spring.factories)
  • 参数(Arguments、application.properties)

​ 上述过程描述了springboot程序启动过程中做的所有的事情,这个时候好奇宝宝们就会提出一个问题。如果想干预springboot的启动过程,比如自定义一个数据库环境检测的程序,该如何将这个过程加入springboot的启动流程呢?

​ 遇到这样的问题,大部分技术是这样设计的,设计若干个标准接口,对应程序中的所有标准过程。当你想干预某个过程时,实现接口就行了。例如spring技术中bean的生命周期管理就是采用标准接口进行的。

public class Abc implements InitializingBean, DisposableBean {
    public void destroy() throws Exception {
        //销毁操作
    }
    public void afterPropertiesSet() throws Exception {
        //初始化操作
    }
}

​ springboot启动过程由于存在着大量的过程阶段,如果设计接口就要设计十余个标准接口,这样对开发者不友好,同时整体过程管理分散,十余个过程各自为政,管理难度大,过程过于松散。那springboot如何解决这个问题呢?它采用了一种最原始的设计模式来解决这个问题,这就是监听器模式,使用监听器来解决这个问题。

​ springboot将自身的启动过程比喻成一个大的事件,该事件是由若干个小的事件组成的。例如:

  • org.springframework.boot.context.event.ApplicationStartingEvent
    • 应用启动事件,在应用运行但未进行任何处理时,将发送 ApplicationStartingEvent
  • org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
    • 环境准备事件,当Environment被使用,且上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent
  • org.springframework.boot.context.event.ApplicationContextInitializedEvent
    • 上下文初始化事件
  • org.springframework.boot.context.event.ApplicationPreparedEvent
    • 应用准备事件,在开始刷新之前,bean定义被加载之后发送 ApplicationPreparedEvent
  • org.springframework.context.event.ContextRefreshedEvent
    • 上下文刷新事件
  • org.springframework.boot.context.event.ApplicationStartedEvent
    • 应用启动完成事件,在上下文刷新之后且所有的应用和命令行运行器被调用之前发送 ApplicationStartedEvent
  • org.springframework.boot.context.event.ApplicationReadyEvent
    • 应用准备就绪事件,在应用程序和命令行运行器被调用之后,将发出 ApplicationReadyEvent,用于通知应用已经准备处理请求
  • org.springframework.context.event.ContextClosedEvent(上下文关闭事件,对应容器关闭)

​ 上述列出的仅仅是部分事件,当应用启动后走到某一个过程点时,监听器监听到某个事件触发,就会执行对应的事件。除了系统内置的事件处理,用户还可以根据需要自定义开发当前事件触发时要做的其他动作。

//设定监听器,在应用启动开始事件时进行功能追加
public class MyListener implements ApplicationListener<ApplicationStartingEvent> {
    public void onApplicationEvent(ApplicationStartingEvent event) {
		//自定义事件处理逻辑
    }
}

​ 按照上述方案处理,用户就可以干预springboot启动过程的所有工作节点,设置自己的业务系统中独有的功能点。

总结

  1. springboot启动流程是先初始化容器需要的各种配置,并加载成各种对象,初始化容器时读取这些对象,创建容器
  2. 整体流程采用事件监听的机制进行过程控制,开发者可以根据需要自行扩展,添加对应的监听器绑定具体事件,就可以在事件触发位置执行开发者的业务代码

监听器

2023-04-08T10_41_54

2023-04-08T10_39_52

  • 文章学习链接

原理篇完结

​ 原理篇到这里就要结束了,springboot2整套课程的基础篇、实用篇和原理篇就全部讲完了。至于后面的番外篇由于受B站视频上传总量不得超过200个视频的约束,番外篇的内容不会在当前课程中发布了,会重新定义一个课程继续发布,至于具体时间,暂时还无法给到各位小伙伴。

​ 原理篇个人感觉略微有点偷懒,怎么说呢?学习原理篇需要的前置铺垫知识太多,比如最后一节讲到启动流程时,看到reflush方法时我就想现在在看这套课程的小伙伴是否真的懂这个过程呢?但是如果把这些东西都讲了,那估计要补充的知识就太多了,就是将spring的很多知识加入到这里面重新讲解了,会出现喧宾夺主的现象。很纠结,( ´•︵•` )

​ 课程做到这里就要和各位小伙伴先say顾拜了,感谢各位小伙伴的支持,也欢迎各位小伙伴持续关注黑马程序员出品的各种视频教程。黑马程序员的每位老师做课程都是认真的,都是为了各位致力于IT研发事业的小伙伴能够学习之路上少遇沟沟坎坎,顺利到达成功的彼岸。

​ 番外篇,さようなら! 안녕히 계십시오!แล้วเจอกัน!До свидания !خداحافظ !
事件触发位置执行开发者的业务代码

监听器

2023-04-08T10_41_54

2023-04-08T10_39_52

  • 文章学习链接

原理篇完结

​ 原理篇到这里就要结束了,springboot2整套课程的基础篇、实用篇和原理篇就全部讲完了。至于后面的番外篇由于受B站视频上传总量不得超过200个视频的约束,番外篇的内容不会在当前课程中发布了,会重新定义一个课程继续发布,至于具体时间,暂时还无法给到各位小伙伴。

​ 原理篇个人感觉略微有点偷懒,怎么说呢?学习原理篇需要的前置铺垫知识太多,比如最后一节讲到启动流程时,看到reflush方法时我就想现在在看这套课程的小伙伴是否真的懂这个过程呢?但是如果把这些东西都讲了,那估计要补充的知识就太多了,就是将spring的很多知识加入到这里面重新讲解了,会出现喧宾夺主的现象。很纠结,( ´•︵•` )

​ 课程做到这里就要和各位小伙伴先say顾拜了,感谢各位小伙伴的支持,也欢迎各位小伙伴持续关注黑马程序员出品的各种视频教程。黑马程序员的每位老师做课程都是认真的,都是为了各位致力于IT研发事业的小伙伴能够学习之路上少遇沟沟坎坎,顺利到达成功的彼岸。

​ 番外篇,さようなら! 안녕히 계십시오!แล้วเจอกัน!До свидания !خداحافظ !

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

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

相关文章

C++ STL之string容器的模拟实现

目录 一、经典的string类问题 1.出现的问题 2.浅拷贝 3.深拷贝 二、string类的模拟实现 1.传统版的string类 2.现代版的string类&#xff08;采用移动语义&#xff09; 3.相关习题* 习题一 习题二 4.写时拷贝 5.完整版string类的模拟实现[注意重定义] MyString.h…

磁珠的工作原理

磁珠是一个耗能元器件&#xff0c;他能把频率相对较高的信号以热量的形式耗散掉&#xff0c;保留频率相对较低的信号。 主要有这种插件的磁珠&#xff0c;还有这种贴片的磁珠。 下面我们来看下磁珠具体工作原理。 磁珠的构造我们可以简单的看成一个导线穿过环形铁氧体的磁性材…

[渗透教程]-015-网络与系统渗透

文章目录 1.0基本概念2.0 网络与系统渗透基本原理2.1 渗透测试2.2 入侵和预防2.3 案例一:从信息收集到入侵提权2.3.1 从域名到ip2.3.2 从IP获取旁站2.3.3 收集系统与⽹络配置详细信息2.3.4 踩点2.3.5发现漏洞2.3.6漏洞利用2.3.7维持系统控制权2.3.8清理访问痕迹2.4 案例二:Goo…

TryHackMe-Jeff(boot2root | Hard?)

Jeff 你能破解杰夫的网络服务器吗&#xff1f; 如果你发现自己在暴力破解SSH&#xff0c;你就做错了。 端口扫描 循例nmap 进80&#xff0c;是一个空页面&#xff0c;查看源代码 将jeff.thm加入hosts 上gobuster /admin是空页面&#xff0c;/backups也没东西&#xff0c;/up…

Centos安全加固策略

目录 密码安全策略 设置密码的有效期和最小长度 设置用户密码的复杂度 登录安全策略 设置用户远程登录的安全策略 安全的远程管理方式 访问控制 限制root用户登录 修改ssh 22端口 设置登录超时时间 限制IP访问 安全审计 审核策略开启 日志属性设置 查看系统登录…

基础巩固、探寻Java装箱和拆箱的奥妙!

前言 今天在逛某知名论坛的时候&#xff0c;看到一篇"请不要使用包装类型&#xff0c;避免造成性能损失"的文章。一下子就吸引了我的注意。大意就是&#xff0c;能用基本数据类型就尽量用基本数据类型&#xff0c;因为包装类型自动拆箱、装箱会带来性能损失尤其是循环…

函数式编程#3纯函数的概念

纯函数的概念 文章目录 纯函数的概念纯函数的两种形式&#xff1a;调用目标本身,不会改变函数内部,不受函数外部影响 函数的副作用如何理解"相同的输入得到相同的输出"不是纯函数的映射关系是纯函数的映射关系 纯函数的两种形式&#xff1a; 调用目标本身,不会改变 …

gcc编译 与交叉编译(x86 to arm) (一)单个文件编译

1.1、gcc编译单个c程序&#xff08;hello.c) gcc hello.c -o hello (hello是生成的可执行程序的名字&#xff09;1.2、交叉编译hello.c 源平台&#xff1a; UOS_X86_64 目标平台&#xff1a;UOS_arm 方法&#xff1a;使用现成的交叉编译工具链 参考资料&#xff1a;交叉编译…

【UE】water插件的简单使用

UE Editor版本&#xff1a;4.26 目录 一、岛屿外观修改 二、波浪参数设置 三、水体海洋的颜色设置 四、 水体河流 五、创建可浮在水体上的actor 一、岛屿外观修改 1. 保证“Landmass”和“Water”插件已启用 启用后&#xff0c;搜索water可以看到如下组件 2. 激活地形编…

LeetCode:6390. 滑动子数组的美丽值

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;6390. 滑动子数组的美丽值 题目描述&#xff1a;给你一个长度为 n 的整…

vulnhub DC:4渗透笔记

靶场下载地址:https://vulnhub.com/entry/dc416-2016,168/ 信息收集 使用nmap确定靶场ip地址 扫描ip确定开放端口 开放22 80端口&#xff0c;访问一下网页端(这边断了一次靶机ip改为192.168.100.138) 漏洞利用 登录框尝试爆破 发现用户名密码admin happy 登录进入后发现这里…

【自然语言处理】【大模型】LaMDA:用于对话应用程序的语言模型

LaMDA&#xff1a;用于对话应用程序的语言模型 《LaMDA: Language Models for Dialog Applications》 论文地址&#xff1a;https://arxiv.org/abs/2201.08239 相关博客 【自然语言处理】【大模型】LaMDA&#xff1a;用于对话应用程序的语言模型 【自然语言处理】【大模型】Dee…

如何衡量 SLO 的有效性?

衡量 SLO 及错误预算策略是否有效&#xff0c;其实就是看实际运行后&#xff0c;是否真的能达到我们的期望。我们可以从下面三个关键维度来看。 SLO 达成情况。我们用达成&#xff08;Met&#xff09;&#xff0c;或未达成&#xff08;Missed&#xff09;来表示。“人肉”投入…

阿里EGES

EGES&#xff1a;Billion-scale Commodity Embedding for E-commerce Recommendation in Alibaba 阿里的EGES是Graph Embedding的一个经典应用&#xff0c;在内容冷启和物料召回上面有较多的落地潜力。主要思想是根据用户交互的物料作为节点构建物料图&#xff0c;在传统的Dee…

(二)AIGC—Stable Diffusion(2)

越往后&#xff0c;加的噪声越多&#xff0c;逐渐递增 正常的话&#xff0c;类似RNN&#xff0c;前向传递&#xff0c;不利于模型训练。 如果直接从x0到xt最好&#xff0c;DPPM这篇论文就实现了这一目标 beta这一参数在扩散过程是已知的&#xff0c;前期设计好&#xff0c;从0…

从0搭建Vue3组件库(六):前端流程化控制工具gulp的使用

随着前端诸如webpack&#xff0c;rollup&#xff0c;vite的发展&#xff0c;gulp感觉似乎好像被取代了。其实并没有&#xff0c;只不过它从台前退居到了幕后。我们仍然可以在很多项目中看到它的身影&#xff0c;比如elementplus、vant等。现在gulp更多的是做流程化的控制。 比如…

【算法题解】28.子集的递归解法

这是一道 中等难度 的题 题目来自&#xff1a; https://leetcode.cn/problems/subsets/ 题目 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 …

51单片机(二)成功点亮LED

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其实STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

nuxt3 + pinia + swiper +element-plus + less + 腾讯地图 创建项目及使用

一。先说优点 1、基于Vue3&#xff1a; Nuxt3是基于Vue.js 3开发的&#xff0c;Vue.js 3是目前最流行的前端框架之一。 这意味着你可以利用Vue3的所有优势&#xff0c;如性能优化、响应式编程、更好的TypeScript支持等。2、服务端渲染&#xff08;SSR&#xff09;&#xff1a…

C++之引用的介绍

目录 前言 引用 1.引用的概念 2.引用特性 3.引用的权限 4. 使用场景 4.1 做参数 4.2 做返回值 5.引用和指针的区别 前言 相信大家都看过水浒传&#xff0c;里面的英雄人物除了自己的名字外都有自己的称号&#xff0c;比如&#xff1a;花和尚——鲁智深&#xff0c;豹…