SpringBoot 原理

84 阅读6分钟

SpringBoot 原理

1 配置文件

1.1 配置文件优先级
1.1.1 数据冲突优先级

​ properties>yml>yaml

1.2 临时改变参数数据
1.2.1 java系统属性参数
-D属性名=属性值
1.2.2 命令行属性参数
--属性名=属性值
1.3 整个优先级

​ 命令行参数>java系统属性>properties>yml>yaml

2 bean的原理

2.1 bean的主动获取
2.1.1 根据名称获取
Object getBean (String name)
2.1.2 根据类型获取
<T> T getBean(Class<T> requiredType)
2.1.3 根据名称和类型获取
<T> T getBean(String name, Class<T> requiredType)
//引入applicantionContext 为spring容器
@Autowired
private ApplicationContext applicationContext;

//获取bean对象
@Test
public void testGetBean(){
   //根据bean的名称获取
   Object deptController = applicationContext.getBean("dvc");
   System.out.println(deptController);

   //根据bean的类型获取
   DeptController bean = applicationContext.getBean(DeptController.class);
   System.out.println(bean);

   //根据bean的名称 及 类型获取
   DeptController deptController1 = applicationContext.getBean("dvc", DeptController.class);
   System.out.println(deptController1);
}
2.1.4

​ 不进行属性依赖注入,也不进行@Bean方法参数注入,如果需要使用对象可以直接通过获取容器再获取对象方式得到对象

2.1.5 spring容器

​ BeanFactory 顶层父容父类(spring框架3.0版本之前使用)

​ AppcationContext子接口 (spring框架3.0版本之后使用)功能更加强大

2.2 bean的作用域

​ 加注与要注入spring ioc容器中的类上

@Scope("prototype")
@RestController (value = "dvc")
@RequestMapping("/depts")
public class DeptController {
		省略....
    }
}
2.2.1 singleton (单例)

​ 容器内同名称的bean只有一个实例, 加入到spring容器中, 被共享, 容器销毁这个单例对象才会销毁.

​ 单例在spring容器创建的时候就已经创建了, 如果需要延迟加载需添加@lazy

​ 运行以下代码, 输出十个相同的类

@Test
public void testScope(){
   for (int i = 0; i < 10; i++) {
      Object dvc = applicationContext.getBean("deptControlle");
      System.out.println(dvc);
   }
}

结果:

com.example.controller.DeptController@2c16677c
com.example.controller.DeptController@2c16677c
......
com.example.controller.DeptController@2c16677c
2.2.2 prototype (非单例)

​ 每次使用该bean时会创建新的实例, 多例会被jvm的垃圾回收器回收

​ 注意prototype本身就是懒加载, 只有被调用的时候才加载

​ 运行上面代码输出十个不同的类

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
ִDeptController已经被创建了....
com.example.controller.DeptController@790654d5
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
ִDeptController已经被创建了....
com.example.controller.DeptController@5f935d49
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
......
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
ִDeptController已经被创建了....
com.example.controller.DeptController@316d30ad
2.2.3 单例对象创建的时机

​ 项目启动的时候运行引导类,里面扫描spring注解有,有注解立刻创建对象加入spring容器,所以服务器启动时就创建对象

2.2.4 @Lazy (延迟创建)

​ 只有在单例创建对象模式有效。服务器启动时不创建对象,而是在第一次获取的时候spring容器才创建对象加入容器里面。不推荐使用

2.2.5 @Scope

​ 无参数设置就是单例创建

2.2.6 @Scope("prototype")

​ 设置当前对象多例创建, 每次从spring容器里面获取出来的时候都会创建新的对象

2.3 加载第三方bean
2.3.1 为什么需要@Bean

​ 因为项目中自定义的bean对象, 才可以使用@Component及其衍生注解, 但是部分第三方的bean对象在项目中也运用广泛, 就需要@Bean将第三方bean对象交给spring管理

2.3.2 @Bean

​ /\ @Bean 用于在方法上,可以将返回的返回值对象加入spring容器中,并且方法名作为对象的别名

​ /\ @Bean("abc") //@Bean也可以自定义别名

​ /\ 如果@Bean定义方法需要使用其他容器对象,可以通过形参注入使用

2.3.3 如何添加第三方Bean

​ 例如, 要添加一个第三方工具类Utils下的一个HeaderParser类

public class HeaderParser {

    public void parse(){
        System.out.println("HeaderParser ... parse ...");
    }

}

​ 1). 创建一个配置类, 添加注解@Configuration 添加构造的方法并@Bean, 注意此时的方法名就是Bean的名字

@Configuration
public class Configs {

    @Bean
    public HeaderParser headerParser(){
        return new HeaderParser();
    }
}

​ 2) 创建一个ImportSelect类, 并重写ImportSelect方法

public class ImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.Configs"};
    }
}

​ 3) 添加一个EnableHeaderConfig的注解并注入ImportSeclect

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ImportSelector.class)
public @interface EnableHeaderConfig {
}

​ 4) 在启动类上注入 (本义就是一个扫描器, 查找需要注入的bean), 可以直接注入其他class文件, 但是规范操作是注入importselector

// @ComponentScan({"com.example","com.example"})
// @Import({HeaderParser.class,HeaderGenerator.class})
// @Import(Configs.class)
@Import(ImportSelector.class)
// @EnableHeaderConfig
@SpringBootApplication
public class Day29SpringbootConfigTheoryApplication {

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

}

​ 5) 然后就可以调用了

	public void testItheimaUtils(){

		HeaderParser b = (HeaderParser)applicationContext.getBean("headerParser");
		System.out.println(b);

	}
2.4 自动配置的底层原理

运行类上的@SpringBootApplication

@SpringBootConfiguration: 用于标识该类是一个基于 Spring Boot 的配置类。它会替代传统的 @Configuration 注解,表示这个类被用作配置文件。

@EnableAutoConfiguration: 自动配置的核心, 用于自动配置 Spring Boot 应用程序。它会根据 classpath 中的依赖以及其他配置信息,尽可能地推断并自动配置 Spring Bean。通过这个注解,可以减少很多繁琐的手动配置。

@ComponentScan: 这个注解用于指定要扫描的组件的包路径。它会自动扫描并注册具有 @Component 及其衍生注解(如 @Service@Repository 等)的类,将它们转换为 Spring Bean。

@Import(AutoConfigurationImportSelector.class) 注解的作用是将 AutoConfigurationImportSelector 类作为一个配置类导入到当前配置类中,从而启用 Spring Boot 的自动配置机制。

通过导入AutoConfigurationImportSelector,Spring Boot 可以根据您的项目依赖和配置信息,自动导入相应的自动配置类,将其加入到应用的 Spring 上下文中。这些自动配置类会根据约定和规则为应用自动配置一些默认行为,例如设置数据源、配置日志、创建默认的 Bean 等。

getCandidateConfigurations(annotationMetadata, attributes) 方法用于根据注解元数据和属性信息获取候选的自动配置类列表, 筛选出符合条件的自动配置类,并最终由 Spring 容器加载和处理

底层就是在如图所示的两个路径中寻找是否有自动配置类, 有则添加, 没有则不添加

3 Springboot原理

3.1 起步依赖
3.1.1 定义

​ 由于Maven的依赖传递, 首先官方提供的启动器里面提供了很多相关的依赖, 其次模块或项目会依赖springboot父项目, 提供了很多依赖的版本锁定,所有springboot项目就可以统一使用父项目的依赖版本,解决了版本的冲突

3.2 自动配置
3.2.1 定义

​ springboot项目除了自己使用注解将对象放入IOC容器,还有一部分对象是通过springboot自动配置将其资源加入到IOC容器中

3.2.2 自动配置方案一

​ @ComponentScan({"com.example","com.itheima"}) 扫描指定包, 并在spring自动配置过程中扫描自己的项目的包(启动类所在的包及其子包)

3.2.3 自动配置方案二

​ 使用@Import导入, 导入的类会被Spring加载到IOC容器中.

​ 实现步骤:

​ 第三方资源:

​ 1) 创建类实现ImportSelector接口重写selectImports方法

​ 2) 创建自定义注解使用@Import设置上面的实现类

​ 3) 当前项目使用第三方资源加入IOC容器,在启动类上使用上面注解

3.3 自动配置原理分析
3.4 @Conditional
3.4.1 @ConditioanlOnClass

当前环境存在指定这个类时, 才声明该Bean

3.4.2 @ConditionalOnMissingBean

当不存在当前类型的bean时, 才声明该bean

3.4.3 @ConditionalOnProperty(name= "name", havingValue="itheima")

当配置文件中存在对应的属性和值, 才注册bean到IOC容器