Java教程

SpringBoot源码分析(一)

本文主要是介绍SpringBoot源码分析(一),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

SpringBoot

01、Spring和springboot是关系是什么?

spring是为了解决企业级开发的复杂性,通过IOC控制反转和DI依赖注入对我们的Java bean进行管理。在我们spring4.0版本中,为了进一步简化spring开发的复杂性,springboot就这样出现了,目的是为了解决spring进行开发时候需要配置大量的xml配置文件,以及手动添加很多jar包,自定义tomcat等问题。

因此springboot框架是构建在spring框架的基础之上的,springboot不能离开spring而使用,springboot是spring发展的产物。

为什么这样说呢?
是因为我们springboot所做的任何事情都离不开springioc容器,springboot最终会将一切都会放入springioc容器中。

- 02、Springboot是如何做到所谓的零配置(少量配置文件)呢?

在之前spring开发时我们需要配置大量的xm配置文件。
在springboot中有四种方式将bean注入到SpringIOC容器中:

1.@ComponentScan + 注解(@Component@Repository@Service@Controller@RestController)
2.@Configuration + @Bean
3.@Import(ImportSelector) + @EnableAutoConfiguration
4.@ImportResource + xml

- 03、Springboot它的启动类在做什么事情呢?做的事情和Spring有什么关系吗?

@SpringBootApplication
@MapperScan("com.kuangstudy.mapper")
public class AdminApplication {

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

}

执行main函数后,会调用类加载器,并且去加载对应的bean,并将其全部放入到ioc容器中

触发类加载器的目的:
1、去获取上下文环境,获取系统信息
2、加载pom.xml所有的依赖jar中编译好的类
3、加载相关的jdk

核心代码:SpringApplication.run(AdminApplication.class, args);
其中AdminApplication.class为了去触发类加载器,并通过反射机制获取该类的注解@SpringBootApplication。
springboot主启动类中调用SpringApplication的run方法,我们看到run方法中有调用了一个重载的run方法

 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

核心代码:return (new SpringApplication(primarySources)).run(args);
其中new SpringApplication(primarySources)会进行初始化:

 //初始化
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

初始化中还做了如下的事情:从我们类加载器中获取所有META-INF/spring.factories中的资源

 private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

         //   try {
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");
           //     ```
          }
}

其中一个包下的META-INF/spring.factories中有如下的数据

# Logging Systems
org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
org.springframework.boot.logging.java.JavaLoggingSystem.Factory

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver
···

这些都是springboot中Import机制中将我们需要的bean解析后放入Map中

那我们是如何传入IOC容器的呢?
我们之前已经通过传入启动类类名触发类加载器进行类加载,将我们所需要的jar都进行加载,之后就通过反射机制获取主类上的@SpringBootApplication注解将项目中的pom.xml配置文件、jdk的jar文件、主启动类包下全部加载到内存中。通过我们springboot提供的@Configuration+@Bean、@ComponentScan + Component(及子注解)、@Import、@ImportResource方式经过过滤后,都加载到我们的IOC容器中。
在此期间,我们会触发springioc提供的生命周期机制,同时触发其他的初始化:banner、日志、上下对象、应用参数接口、运行时run接口等。
同时,我们的selectorImport机制就是run方法的这个阶段将初始化好的所有符合条件的配置类放入到selectorImport数组中,进行ioc的初始化。

- 04、springboot提供的注解@SpringBootApplication 能够解决什么?写它的作用是干嘛?

@SpringBootApplication是一个复合注解

@Configuration
@SelectorImport
@Component + @Component

@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}
)}
)
public @interface SpringBootApplication {
}

解决IOC容器的自动注入问题。
写它的作用是为了在类启动的时候在类加载完成后去通过反射获取该注解进行IOC容器的自动注入。

- 05、Springboot中提供开关类是什么?@EnableAsync 等原理是什么?在做些什么事情

Springboot中提供的开关类是一个注解@EnableAutoConfigration。
@EnableAutoConfigration的内部主要是因为@Import注解
当在类上声明这个注解后,会加载对应@Import中的@configuration类或者ImportSelector接口实现类。将其中的类信息加载到IOC容器中。

- 06、Springboot中的starter机制它的原理是什么?为什么要starter机制?解决传统spring的什么问题?

starter是以一种服务,将开发中需要使用的各模块都各自进行整合。
当我们在pom.xml文件中导入spring-boot-starter-web等包时,maven会自动将starter的所有子包都导入进来,不用我们一个一个去导入所有有关web的jar包了。

例如:spring-web、spring-mvc、spring-boot-starter-tomcat、spring-boot-starter-json、spring-boot-starter-logging等等,之后我们只需要添加spring-boot-starter-web就可以完成以上所有的配置。

解决了传统spring在maven中配置繁琐的问题,简化配置。
starter包不仅可以将jar包导入到我们的工程中,有些包还可以帮我们做配置等功能。

例如:mybatis-spring-boot-starter包下面有MybatisPlusAutoConfiguration自动配置类

在这里插入图片描述在这里插入图片描述

@Configuration与@Bean类似于我们applicationContext.xml文件中的bean标签注入。

- 07、Springboot中的条件注解@Conditionxxxx这些有用处?我如何去使用?

pringboot是常用的条件依赖注解有:

@ConditionalOnBean,仅在当前上下文中存在某个bean时,才会实例化这个Bean。

@ConditionalOnClass,某个class位于类路径上,才会实例化这个Bean。

@ConditionalOnExpression,当表达式为true的时候,才会实例化这个Bean。

@ConditionalOnMissingBean,仅在当前上下文中不存在某个bean时,才会实例化这个Bean。

@ConditionalOnMissingClass,某个class在类路径上不存在的时候,才会实例化这个Bean。

@ConditionalOnNotWebApplication,不是web应用时才会实例化这个Bean。

@AutoConfigureAfter,在某个bean完成自动配置后实例化这个bean。

@AutoConfigureBefore,在某个bean完成自动配置前实例化这个bean。

可以帮我们灵活的自动配置当前的配置类。

- 08、SpringBoot配置类@Configuration+@Bean解决了传统spring的什么问题?我该如何认识它?

这两个注解可以代替我们的xml文件配置。
@Configuration注解中有@Component注解,因此也需要开启ComponentScan来扫描该类才能被注入到SpringIOC中。

我们的@Configuration实际上是一个配置类,类似于我们spring中的applicationContext.xml文件,
@Bean就类似于我们xml文件中的<bean id=“xxx”,class=“XXX”>。

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

    boolean proxyBeanMethods() default true;
}

- 09、@ComponentScan+注解(@Service、@Controller、@RestController、@Component 、@Repository)

默认情况下springboot会以当前启动类的包做为扫描范围。将扫描包中所有的@Service、@Controller、Component、@Repository、@RestController都放入到我们的IOC容器中。
若当我们自定义时,必须将我们所有需要扫描的包路径全部声明出来,包括当前启动类的包。

@ComponentScan(basePackages ={"com.llj","com.llj2","com.llj3"})

09、为什么要使用:@Import机制 + @Eablexxxxx这些机制呢?

这是一种新的方式将bean放入IOC容器中
因为我们@Configuration+@Bean和扫描包+注解的方式不能很方便的解决加载第三方jar包。因此我们可以通过@Import + @EnableXXX的方式把它们管理起来。

首先在包目录下建javabean。
我们在@EnableXXX的注解@Import中传入一个实现了ImportSelector接口的实现类。
这个也是一个开关类。
当我们想要加载该bean到IOC时,只需要将该注解加到配置类上即可。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(UserCourseImportSelector.class)
public @interface EnableUserCourse {
}

这个实现类中重写了selectImports方法,将我们javabean的类全路径以字符串的形式传到spring数组中。

public class UserCourseImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{
                "com.llj.myselector.RoleBean",
                "com.llj.myselector.PersonBean"
        };
    }
}

我们的主类会根据主包路径下是否有@EnableXXX注解开关类来决定是否将其加入IOC容器中。

@SpringBootApplication
@EnableUserCourse
public class LljBootsSourceApplication {
    public static void main(String[] args) {
        //主启动类,springApplication中的run方法添加当前类的class,
        SpringApplication.run(LljBootsSourceApplication.class, args);
    }

}


10、@ImportResource机制是什么?

开门见山,@importResource出现的目的是为了我们可以通过xml配置将bean加入到我们IOC容器中。
我们如何实现的呢?
1、我们在非启动类包下创建一个@Service类(只要是扫描包下的注解都可以)

package com.llj2.Importresource;
import org.springframework.stereotype.Service;

/**
 * @author leo
 * @create 2021-07 -03-16:36
 */
@Service
public class Imservice {
    public void getInfo(){
        System.out.println("This service is going!");
    }
}

2、并在resources下添加相对应的applicationContext.xml文件,将注解开启,扫描包路径也进行填写

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName" default-lazy-init="false" >
    <description>Spring Configuration</description>
    <!-- 开启注解模式 -->
    <context:annotation-config/>
    <!-- 使用Annotation自动注册Bean -->
    <context:component-scan base-package="com.llj2.Importresource">
    </context:component-scan>
</beans>

3、为了可以让我们的配置类生效,我们需要在主启动类包下添加一个配置类

/**在启动包下添加ImportResource路径去扫描对应的xml文件,通过xml配置文件将其他非启动类包下的bean进行注入到IOC容器中
 * @author leo
 * @create 2021-07 -03-16:41
 */
@Configuration
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class XmlConfiguration {
}

流程:
当主类启动时,会将该配置类中的ImportResource注解中的xml配置文件激活,将开启配置文件中的注解扫描,将我们扫描路径下的@Service类添加到我们IOC容器中。

这篇关于SpringBoot源码分析(一)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!