SpringBoot原理

一、Maven前置知识

1、Maven中的坐标:

  • 什么是坐标:用于描述仓库中资源的位置
  • 坐标的组成:
    <1>groupId:当前maven项目隶属的组织的名称
    <2>artifactId:当前项目的名称
    <3>version:当前项目版本号
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
  • 坐标的作用:唯一性定位资源的位置

2、添加依赖

  • 依赖的类型:
    <1>直接依赖:当前项目直接依赖于一个资源
    <2>间接依赖:当前项目依赖的资源又依赖于其他资源,当前项目也可以使用间接依赖的资源
    <3>依赖冲突:当依赖中出现相同资源时,依赖的层次越浅依赖的优先级越高;层级相同时,配置靠前的优先级高
    在这里插入图片描述
  • 可选依赖和排除依赖
    <1>可选依赖:假设A依赖了junit,如果在依赖中添加true,如果B依赖了A那B就不可以使用junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<optional>true</optional>
</dependency>

<2>排除依赖:如果依赖了spring-boot-starter-web,spring-boot-starter-web中依赖了spring-boot-starter-json,添加了当前项目就不会传递依赖于spring-boot-starter-json。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
  • 依赖作用范围:通过设置scope数据来设置依赖的作用范围。complie表示任何地方都可用,test只在测试代码中用(比如junit,它只用于测试,没必要在打包时将依赖也添加上去),provided主代码和测试代码中用(比如servlet-api,tomcat中有servlet-api,如果在打包中添加依赖就会产生冲突),runtime只在打包时使用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>

在这里插入图片描述

3、继承依赖

一个资源(项目)除了可以依赖于另一个资源(项目)外,还可以继承另一个资源(项目)。在一个资源依赖于另一个资源时,它会间接依赖于其他资源;而在一个资源继承另一个资源时,它会继承这个资源对依赖的配置。

  • 父工程:对于一个工程,我们可以添加<dependencyManagement></dependencyManagement>来对依赖的版本进行管理。对于<version></version>,还可以在<properties></properties>中进行定义,然后在<dependencyManagement></dependencyManagement>中进行引用。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zyf</groupId>
    <artifactId>ssm</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.context>5.3.22</spring.context>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.context}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
  • 子工程:子工程可以继承父工程(父工程可以是第三方提供的jar包),在使用依赖时,如果这个依赖在父工程中定义过,可以不添加<version></version>,会直接使用父工程的version。这样可以有效的避免依赖冲突。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zyf</groupId>
    <artifactId>controller</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>com.zyf</groupId>
        <artifactId>ssm</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../ssm/pom.xml</relativePath>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
    </dependencies>
</project>

Maven生命周期

  • Clean生命周期:清理项目
    1、pre-clean:执行一些清理前需要完成的工作
    2、clean:清理上一次构建生成的文件
    3、post-clean:执行一些清理后需要完成的工作
  • Default生命周期:构建项目
    1、validate:验证工程是否正确,所有需要的资源是否可用。
    2、compile:编译项目的源代码。
    3、test:使用合适的单元测试框架来测试已编译的源代码。这些测试不需要已打包和布署。
    4、Package:把已编译的代码打包成可发布的格式,比如jar。
    5、integration-test:如有需要,将包处理和发布到一个能够进行集成测试的环境。
    6、verify:运行所有检查,验证包是否有效且达到质量标准。
    7、install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。
    8、Deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。
  • Site生命周期:建立和发布项目站点
    1、pre-site:生成项目站点之前需要完成的工作
    2、site:生成项目站点文档
    3、post-site:生成项目站点之后需要完成的工作
    4、site-deploy:将项目站点发布到服务器

二、什么是SpringBoot

SpringBoot是Spring开源组织下的一个子项目,降低了使用SpringBoot的难度,简省了Spring的配置,提供了各种启动器。

三、ben的加载方式

1、使用XML配置文件的方式加载ben

<?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-->
<bean id="bookService"
class="com.itheima.service.impl.BookServiceImpl"
scope="singleton"/>
<!--声明第三方开发bean-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
</beans>

使用XML文件初始化IOC容器

ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");

2、使用注解

  • 使用XML去扫描注解并加载bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.itheima"/>
</beans>
  • 使用@ComponentScan注解扫描注解并加载bean
    <1>先定义一个类,然后在这个类上添加@ComponentScan注解,当这个类被加载到容器时,就会去扫描这个类所在包中所有添加了@Component/@Service/@Controller/@Repository/@Configuration注解的类,并创建对应的实列放入容器中,如果这些类中有添加了@Bean注解的方法(这个类也叫做配置类),添加@Bean注解方法的返回值也将会被放入到容器中称为bean。当然,如果这个类本身也是一个配置类,那它产生的bean也会被放入到容器中。需要注意的是@ComponentScan默认只会扫描当前类所在包中的bean,如果要扫描其他包需要加basePackages属性,比如@ComponentScan(basePackages = “xxx”)。
@ComponentScan
public class TotalConfig {
    @Bean
    public Cat cat(){
        return new Cat();
    }
}

<2>使用类文件初始化IOC容器,这个类也会被实列化并添加到容器中

ApplicationContext ctx = new AnnotationConfigApplicationContext(TotalConfig.class);

<3>配置类:含有添加了@Bean注解的方法的类。当这个配置类被实例化称为bean并且被放入IOC容器时,它的被加了@Bean注解的方法的返回值也将被放入IOC容器。

public class Config {
    @Bean
    public Cat cat(){
        return new Cat();
    }
}

<4>为什么需要配置类:
在使用@Component注解时,它只能去扫描当前项目中的包,并且如果是对项目的更新,之前项目中可能使用的xml文件加载bean而没有使用注解,这就导致使用@Component无法实列化这些类并将其放入容器中成为bean。使用配置类就可以解决这个问题。
<5>为什么要在配置类上添加@Configuration或@Component
添加@Configuration或@Component注解只是为了让配置类称为一个bean,可以被加载到容器中
<6>@Configuration和@Component的区别
@Configuration注解中含有@Component注解,可以使得一个类被扫描到并且初始化为Bean。但相比于@Component注解,@Configuration多了proxyBeanMethods()方法,默认返回值为true。当proxyBeanMethods()返回值为ture时,添加了@Configuration的类并不会被实列化,被实列化的是它的代理类。当我们从容器中获取这个实列后,调用它的添加了@Bean注解的方式时,不会重新创建对象,而是从容器中去取对应的ben。如果proxyBeanMethods()返回值为false,则每次调用添加了@Bean注解的方式时都会重新创建一新的对象。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}

比如对于下面这个类,它被实列化并且添加到IOC容器中时会创建一个Cat对象,这个Cat对象也会被添加到IOC容器中。如果调用Config对象的cat()方法就会去IOC容器中去取Cat对象而不是重新创建对象。但如果添加的是@Configuration(proxyBeanMethods = false)注解,每次调用cat()方法都会重新创建对象。

@Configuration
public class Config {
    @Bean
    public Cat cat(){
        return new Cat();
    }
}
  • FactoryBean
    <1>FactoryBean是java提供的一个接口,这个接口本质上就是一个工厂,用来创建对象,但这个工厂只能创建一个对象。这个工厂有三个方法需要实现,如下面的代码。我们实现了FactoryBean,并且设置它返回的类型是Cat()对象。
public class CatFactoryBean implements FactoryBean<Cat> {
    @Override
    public Cat getObject() throws Exception {
        return new Cat();
    }
    @Override
    public Class<?> getObjectType() {
        return Cat.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

<2>FactoryBean的作用:有的对象的创建是复杂的,如果都写在配置类中将会使配置类变得臃肿。
<3>FactoryBean在配置类中的使用:
直接在配置类中创建对应的FactoryBean并返回即可,不过需要注意的是虽然cat()方法返回值为CatFactoryBean,但放入容器中的并不是CatFactoryBean的实列而是CatFactoryBean中getObject()方法返回的实列。

@Configuration(proxyBeanMethods = false)
public class Config {
    @Bean
    public CatFactoryBean cat(){
        return new CatFactoryBean();
    }
}

3、使用@Import注解

如下,如果在一个类上使用了@Import注解,当这个类被实例化并放入IOC容器称为一个Bean时,@Import注解中的类也会被实例化并且放入IOC容器称为一个bean。

@Component
@Import(Cat.class)
public class Config {
}

4、直接在IOC容器(仅限于AnnotationConfigApplicationContext容器)中添加Bean

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig5.class); ctx.register(Cat.class);

5、实现ImportSelector接口

当使用@Import注解导入这个类时(通过在这个类上加@Component注解让这个类成为bean没有这个效果),selectImports方法返回值中的类将会被实例化并放入IOC容器中成为bean。

public class MyImportSelector implements ImportSelector {
	public String[] selectImports(AnnotationMetadata metadata) {
        return new String[]{"com.example.yuanli.example.Cat"};
    }
}

6、实现ImportBeanDefinitionRegistrar接口

实现ImportBeanDefinitionRegistrar接口,实现其中的registerBeanDefinitions方法。先定义一个BeanDefinition ,然后将BeanDefinition 注册到registry中,BeanDefinition中的Class对象对应的类就会被实现化并放入IOC容器中。需要注意的是MyImportBeanDefinitionRegistrar类也需要使用@Import注解导入。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
		BeanDefinitionRegistry registry) {
		BeanDefinition beanDefinition = BeanDefinitionBuilder
		.rootBeanDefinition(Cat.class)
		.getBeanDefinition();
		registry.registerBeanDefinition("cat", beanDefinition);
	}
}

7、实现BeanDefinitionRegistryPostProcessor 接口

与ImportBeanDefinitionRegistrar唯一不同的是,这里的bean会被最后创建。

public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	BeanDefinition beanDefinition = BeanDefinitionBuilder
	.rootBeanDefinition(Cat.class)
	.getBeanDefinition();
	registry.registerBeanDefinition("cat", beanDefinition);
	}
}

三、Bean的加载控制

1、使用编码方式控制

在4、5、6、7三种方式创建bean时都可以使用编码的方式来控制Bean的加载

2、使用注解控制

用户控制加载bean的注解都以@Conditional开头,常用的有@ConditionalOnBean,@ConditionalOnClass,@ConditionalOnMissingBean,@ConditionalOnMissingClass。这些注解可以添加在类上也可以添加配置类的方法上。只有这个注解的条件满足了才能加载对应的Bean。

  • @ConditionalOnClass:只有当存在对应的类时才会去加载bean。
    <1>在类上加@ConditionalOnClass注解: 如下,当加实列化Config类成为Bean时,会先检查存不存在Cat类,存在则实例化Config类成为Bean。
@Configuration
@ConditionalOnClass(name = "com.example.yuanli.example.Cat") //@ConditionalOnClass(Cat.class)也可
public class Config {
}

<2>在方法上加@ConditionalOnClass注解:如下,只有在存在Cat类时才会加载Cat类对应的bean。

@Configuration
public class Config {
    @Bean
    @ConditionalOnClass(name = "com.example.yuanli.example.Cat")
    public Cat cat(){
        return new Cat();
    }
}
  • @ConditionalOnBean:只有存在对应的Bean时才会加载bean
    <1>在类上加@ConditionalOnBean注解: 如下,当加实列化Config类成为Bean时,会先检查存不存在dog这个bean,存在则实例化Config类成为Bean。
@Configuration
@ConditionalOnBean(name = "dog")
public class Config {
}

<2>在方法上加@ConditionalOnBean注解:如下,只有在存在dog这个bean时才会加载Cat类对应的bean。

@Configuration
public class Config {
    @Bean
    @ConditionalOnBean(name = "dog")
    public Cat cat(){
        return new Cat();
    }
}
  • @ConditionalOnMissingBean,@ConditionalOnMissingClass与@ConditionalOnBean,@ConditionalOnClass相反,只有在没有对应的类或bean才去加载bean。
  • 不扫描某个类
    @ComponentScan(excludeFilters = {
    @ComponentScan.Filter( type = FilterType.ANNOTATION, value = AvoidScan.class)
    })

3、属性配置

将bean运行需要的资源抽取成独立的属性类,然后在这个bean中注入属性类。

  • 定义属性类,并通过yml或者properties文件注入属性
    <1>注入属性需要使用@ConfigurationProperties(prefix = “xx”)注解,会从yml或者properties文件中读取以xx为前缀的配置。属性类的属性可以是自定义的类,需要为属性类和属性类依赖的类提供set方法。
    属性类Animal,添加@Component是为了让其成为bean,这样容器才会为其注入属性。
@Component
@Data
@ConfigurationProperties(prefix = "animal")
public class Animal {
    private Cat cat;
    private Mouse mouse;
}

属性类Animal依赖的类Cat和Mouse,只是一个普通的提供了set方法的类

@Data
public class Cat {
    private String name;
    private Integer age;
}
@Data
public class Mouse {
    private String name;
    private Integer age;
}

注入属性类,可以像使用普通的bean一样用@Resource等注入,也可以使用@EnableConfigurationProperties注解注入,不过需要注意是使用@EnableConfigurationProperties注解时需要提供构造函数,IOC容器会通过构造函数注入属性bean。

@Component
@EnableConfigurationProperties(Animal.class)
public class Config {
    private Animal animal;

    public Config(Animal animal) {
        this.animal = animal;
    }

    public void say(){
        System.out.println(animal.getCat().getName() + ":" + animal.getMouse().getName());
    }
}

三、Spring的核心注解

@SpringBootApplication是SpringBoot的核心注解。

1、@SpringBootApplication的组成

@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})})
  • @Target:作用目标(这个类是作用在类上,方法上还是成员变量上),ElementType.TYPE表示允许被修饰的注解作用在类、接口和枚举上。
  • @Retention:注解保留多久(保留在源文件中,保留在class文件中还是保留到运行时),RetentionPolicy.RUNTIME表示保留到运行时
  • @Documented:表明这个注解应该被 javadoc工具记录。
  • @Inherited:添加了这个注解的类,它上面添加的注解可以被继承。
  • @SpringBootConfiguration:包含了@Configuration注解,说明只是一个配置类
  • @EnableAutoConfiguration:主要包含如下两个注解。
    <1>@AutoConfigurationPackage:将当前类所在的包设置为需要扫描的包。
    <2>@Import({AutoConfigurationImportSelector.class}):它会去扫描每个jar包(包括当前项目)下的META-INF文件夹中的spring.factories文件,这个文件中包含了这个jar中的所有配置类的类路径,IOC容器尝试创建这个配置类并将其加入到IOC容器中。下面以SpringBoot为例说明,SpringBoot把配置类的路径放在了spring-boot-autoconfigure jar包下的META-INF文件夹下的spring.factories文件中。
    在这里插入图片描述
    该文件部分内容如下,里面存放了大量的配置类的路径。
    在这里插入图片描述
    spring将尝试加载所有的配置类,但这些配置类的加载是有条件的,这里选取RedisAutoConfiguration配置类,它的部分内容如下。可以看到它加了@Configuration注解,那这个类不是应该能够被扫描到吗?为什么还要通过扫描spring.factories文件文件的方式去加载呢?其实,@ComponentScan注解只能扫描当前项目下的bean,而这些jar包是别人提供的并不能扫描到。这个配置类添加了@ConditionalOnClass注解,说明只有在添加了RedisOperations这个类(通过在pom文件中添加了对Redis的依赖后才会有RedisOperations这个类)后才能实例化这个配置类。@EnableConfigurationProperties({RedisProperties.class})这个注解用来导入配置信息。
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }
}

再看一下RedisProperties类,部分代码如下。可以看到,这个类中的变量就是我们常用的对Redis的配置,同时我们也可以通过再yml文件或者properties文件中进行配置来覆盖RedisProperties类中的默认值。

@ConfigurationProperties(
    prefix = "spring.redis"
)
public class RedisProperties {
    private int database = 0;
    private String url;
    private String host = "localhost";
    private String username;
    private String password;
    private int port = 6379;
    private boolean ssl;
    private Duration timeout;
    private Duration connectTimeout;
    private String clientName;
    private RedisProperties.ClientType clientType;
    private RedisProperties.Sentinel sentinel;
    private RedisProperties.Cluster cluster;
    private final RedisProperties.Jedis jedis = new RedisProperties.Jedis();
    private final RedisProperties.Lettuce lettuce = new RedisProperties.Lettuce();
}

经过上面的分析我们发现@Import({AutoConfigurationImportSelector.class})注解就是实现springboot中自动配置的关键。

  • @ComponentScan:扫描添加了@Component/@Service/@Controller/@Repository/@Configuration注解的类,并创建对应的实列放入容器中

四、自动配置的原理

1、收集技术集和配置集:

Spring去收集开发者常用的技术和这些技术常用的参数的信息。由此构成了技术集(这些技术集对应这一个一个的类比如说RedisTemplate,而这些技术集由一堆配置类创建,比如上面提到的RedisAutoConfiguration类)和设置集(也就是一堆属性类,比如RedisProperties类)。

2、初始化SpringBoot基础环境:

加载用户自定义的bean和导入的其他坐标,形成初始化环境(比如用户添加了对Redis的依赖)。

3、加载技术集:

SpringBoot初始化容器,并且尝试去加载这些技术集(也就是实例化各种配置类,比如RedisAutoConfiguration类,这些配置类会创建技术集)。当然并不是所有的配置类都会被实例化,只有满足条件的配置类才会被实例化(比如对于RedisAutoConfiguration,如果用户没有添加对Redis的依赖,RedisAutoConfiguration就不会被加载)。

4、为技术集添加设置集:

对于一个技术比如Redis,它需要各种配置各种参数。springboot通过在配置类中导入属性类就可以实现对参数的配置。比如RedisAutoConfiguration类通过@Import标签导入了RedisProperties类。

5、覆盖设置集中的配置:

用户可以通过在.yml中或者.properties文件中进行配置来实现对properties类中属性的覆盖。

五、SpringBoot中的starter

1、什么是starter

SpringBoot可以实现自动配置,而实现自动配置需要依赖的项目满足下面两个条件(也就是上面介绍的自动配置的元素):
<1>要在resources目录下建立一个META-INF目录,并且META-INF目录中提供一个spring.factories文件,这个文件中提供了配置类的类路径。
<2>要提供spring.factories文件中对应的配置类(有的配置类可能还需要提供属性类)。
满足上面两个条件的项目就能够成为一个starter,springboot添加了这个starter的依赖后就可以直接使用里面的功能而无需额外的配置。

2、starter的种类

分为springboot自己提供的starter和第三方stater。

  • springboot自己提供的starter
    一般项目名为:spring-boot-starter-xxx,这里的xxx一般是模块的功能,比如spring-boot-starter-web。springboot提供的starter一般是对多个功能模块的组合。
  • 第三方提供的starter
    一般项目名为:xxx-spring-boot-starter,这里的xxx一般是模块的功能,mybatisplus-spring-boot-starter。

六、SpringBoot的启动流程

在SpringBoot程序中,在主程序中会调用SpringApplication.run(App.class)方法,而这个方法最终会调用(new SpringApplication(primarySources)).run(args)这行代码。new SpringApplication(primarySources)创建了一个SpringApplication对象,然后调用其run方法。

1、初始化各种属性加载为对象

springboot会将读取环境属性(Environment),系统配置(spring.factories), 参数(Arguments、 application.properties),然后将这些信息抽象为对象。
在调用new SpringApplication(primarySources)创建一个SpringApplication对象时就会创建上面的这些属性对应的对象,SpringApplication(primarySources)会调用下面的代码。

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
		# 初始化资源加载器
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        # 初始化配置类的类名信息(格式转换)
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        # 确认当前容器加载的类型
        this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
        # 获取系统配置引导信息
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        # 获取ApplicationContextInitializer.class对应的实例
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        # 初始化监听器,对初始化过程及运行过程进行干预
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

2、创建Spring容器对象ConfigurableApplicationContext ,加载各种配置

SpringApplication对象的run方法就是用来创建一个Spring容器对象并且加载各种配置。

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

3、SpringBoot的监听器

  • 监听器的实现:
    <1>实现ApplicationListener,范型指定需要监听哪类事件,然后重新onApplicationEvent方法,当监听的类型的事件发生后就会调用onApplicationEvent方法。
public class MyListener implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("=================");
    }
}

<2>在项目的resources目标下创建META-INF文件夹,并在这个文件夹中创建spring.factories文件,添加如下内容。

org.springframework.context.ApplicationListener=\
com.example.demo.day1.MyListener
  • 监听器的类型
    <1>在应用运行但未进行任何处理时, 将发送 ApplicationStartingEvent。
    <2>当Environment被使用, 且上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent。
    <3>在开始刷新之前, bean定义被加载之后发送 ApplicationPreparedEvent。
    <4>在上下文刷新之后且所有的应用和命令行运行器被调用之前发送 ApplicationStartedEvent。
    <5>在应用程序和命令行运行器被调用之后,将发出 ApplicationReadyEvent,用于通知应用已经准备处理请求。
    <6>启动时发生异常,将发送 ApplicationFailedEvent。

七、Spring的优点

1、 起步依赖(简化依赖配置)

spring简配依赖配置的两个主要途径就是提供了spring-boot-starter-parent,和各种starter(如spring-boot-starter-web等)。
<1>spring-boot-starter-parent:主要的作用就是调(tiao)包,保证条件依赖时不会冲突。
spring-boot-starter-parent项目继承自spring-boot-dependencies,而spring-boot-dependencies对各种依赖进行了调包,保证了各种依赖不会发生冲突。在使用依赖,不需要指定,会直接使用spring-boot-dependencies配置的version。

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.7.1</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

<2>各种starter:合并了常用功能需要使用到的依赖。
它整合了若干项目,在用户使用一个功能时(比如web),不需要要再配置各种依赖(比如spring-context,spring-mvc),直接依赖(spring-boot-starter-web)就可。

2、自动配置(简化常用工程相关配置)

3、辅助配置(内置服务器)

sprinboot将一些常用的服务器抽象为对象(比如tomcat),并将这些对象交由IOC容器管理,需要访问服务器时就直接执行这些对象的方法。修改服务器的端口通过在yml中添加如下配置实现。

server:
  port: 8080
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值