SpringBoot01--运行原理和自动装配原理


三步:“是什么?”、“怎么做?”、“为什么?”

一、是什么

1.Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程。

2.使用 Spring Boot 可以很容易地创建出能直接运行的独立的、生产级别的基于 Spring 的应用。我们对 Spring 平台和第三方类库有自己的考虑,因此您可以从最基本的开始。大多数 Spring Boot 应用只需要很少的 Spring 配置。

3.您可以使用 Spring Boot 来创建一个可以使用 java -jar 命令来运行或者基于传统的 war 包部署的应用程序。我们还提供了一个用于运行 spring scripts 的命令行工具。

4.Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。

5.简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。

目标:

  • 为所有 Spring Boot 开发提供一个更快、更全面的入门体验。
  • 坚持自我虽好,但当需求出现偏离,您需要能迅速摆脱出来。
  • 提供大量非功能性特性相关项目(例如:内嵌服务器、安全、指标、健康检查、外部配置)。
  • 绝对没有代码生成,也不要求 XML 配置。

二、SpringBoot优点

  • 为所有Spring开发者更快的入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码生成和XML配置的要求

两个重要策略:

SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置。

  • 开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。
  • 约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

来吧按照惯例整个helloworld

创建一个新项目

1、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现

2、填写项目信息

3、选择初始化的组件勾选web

4、填写项目路径

5、等待项目构建成功

这里注意,查看pom文件时可能会遇到spring-boot-maven-plugin爆红的问题,这里把你的maven仓库改为本地的

看一下pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!-- 父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.shy</groupId>
    <artifactId>hello</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- web场景启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springboot单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

编写一个HelloController,注意controller包跟你的主启动类同级~

package com.shy.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        return "hello world~";
    }
}

去主启动类启动项目

打开浏览器输入http://localhost:8080/hello就可以喽~

helloworld结束~

打成jar包就可以在任何地方运行了

步骤:右边maven,点击package,然后install,成功之后在target目录下生成一个jar包

三、运行原理

1.从pom.xml开始

​ 父依赖:其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!

<!-- 父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

​ 摁住ctrl点击2.4.5

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.5</version>
  </parent>

这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了

2.启动器 spring-boot-starter

		<!-- web场景启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

springboot-boot-starter-xxx:就是spring-boot的场景启动器

spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;

3.主启动类

package com.shy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class HelloApplication {
	//启动服务
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

4.但是**一个简单的启动类并不简单!**我们来分析一下这些注解都干了什么

@SpringBootApplication

还是摁住ctrl点击这个注解,进去之后

@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 {...}

前四个注解早就见过了,重点说下面的几个

@ComponentScan

​ 这个注解在Spring中很重要 ,它对应XML配置中的元素。

​ 作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

@SpringBootConfiguration

​ 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

​ 点进去瞅瞅~

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {...}

​ 这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;

​ 继续点@Configuration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {...}

​ 里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!到这就ok了,回去吧

​ 回到 SpringBootApplication 。

@EnableAutoConfiguration开启自动配置功能

​ 以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置 功能,这样自动配置才能生效;

​ 点进去瞅瞅~

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}

@AutoConfigurationPackage :自动配置包

​ 点进去

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {...}

@import :Spring底层注解@import , 给容器中导入一个组件

​ AutoConfigurationPackages.Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

​ 退回上一步

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

​ AutoConfigurationImportSelector :自动配置导入选择器,点进去,有这么一个方法

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-			INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
}

​ 这里的getSpringFactoriesLoaderFactoryClass()方法
​ 返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
}

​ 这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
    	//这里它又调用了 loadSpringFactories 方法
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

​ 点击查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		//获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    	Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            //将读取到的资源遍历,封装成为一个Properties
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

​ 继续点击FACTORIES_RESOURCE_LOCATION

//去获取一个资源 "META-INF/spring.factories"
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

发现一个多次出现的文件:spring.factories,全局搜索它 按两下shift,搜索,点击files

打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!这些文件都可以点进去~

点击你本地仓库的那个,后面不是跟着路径嘛~

自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

SpringApplication

package com.shy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class HelloApplication {
	//启动服务
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

点进去,找她的构造方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

四、Springboot注解

1.@SpringBootApplication:该注解是springboot最核心注解,也是组合注解,声明它就可以让springboot自动给程序进行必要的配置(简单的说,开启组件扫描和自己配置的功能)。

包含了**@ComponentScan**、@Configuration和**@EnableAutoConfiguration**注解。

2.@Configuration 等同于spring的XML配置文件;使用Java代码可以检查类型安全。

3.@EnableAutoConfiguration 自动配置。

4.@SpringBootConfiguration:是标志当前的类的配置类。

5.@ComponentScan 组件扫描,可自动发现和装配一些Bean。

6.@Component可配合CommandLineRunner使用,在程序启动后执行一些基础任务。

7.@RestController注解是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器。

8.@Controller:该注解用于定义控制器,在spring项目中是由控制器负责用户发来的请求,然后控制器将用户请求的URL转发到对应的接口service层,进行调用相应的业务,在使用该注解时,还用结合@RequestMapping一起使用,处理http请求。

9**.@ResponseBody**:使用该注解表示方法的返回结果直接写入HTTP response body中的,当我们异步请求的时候常使用,用于构建restful的API。

10.@Repository:该注解用于标注数据访问组件,DAO组件的。

11.@service:该注解使用于标注业务层,当在业务层的类上使用时,bean就是自动找到该类就是service的。

12.@PathVariable:该注解使用来绑定函数中的参数用于获取参数的,当默认的情况下,spring会对@该注解的变量进行自动赋值的,我们也可以自己指定。

13.@RequestParam注解:是指获取请求参数的值,@GetMapping是组合注解。

14.@Autowired自动导入。

15.@JsonBackReference解决嵌套外链问题。

16.@RepositoryRestResourcepublic配合spring-boot-starter-data-rest使用。

17.@Transactional: 通过这个注解可以声明事务,可以添加在类上或者方法上。

18.@Resource:@Resource和@Autowired一样都可以用来装配bean,都可以标注字段上,或者方法上。 @resource注解不是spring提供的,是属于J2EE规范的注解。 两个之前的区别就是匹配方式上有点不同,@Resource默认按照名称方式进行bean匹配,@Autowired默认按照类型方式进行bean匹配。

19.资源导入注解:@ImportResource @Import @PropertySource,这三个注解都是用来导入自定义的一些配置文件。

@ImportResource (locations={})导入其他xml配置文件,需要标注在主配置类上,导入property的配置文件@PropertySource指定文件路径,相当于使用spring的标签来完成配置项的引入。@import注解是一个可以将普通类导入到spring容器中做管理

20.@ServletComponentScan:Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,这样通过注解servlet ,拦截器,监听器的功能而无需其他配置,所以这次相中使用到了filter的实现,用到了这个注解。

21.@MapperScan:spring-boot支持mybatis组件的一个注解,通过此注解指定mybatis接口类的路径,即可完成对mybatis接口的扫描。它和@mapper注解是一样的作用,不同的地方是扫描入口不一样。@mapper需要加在每一个mapper接口类上面。所以大多数情况下,都是在规划好工程目录之后,通过@MapperScan注解配置路径完成mapper接口的注入。

五、自动装配原理

1、加载META-INF/spring-autoconfigure-metadata.properties文件

2、获取注解的属性及其值(PS:注解指的是@EnableAutoConfiguration注解)

3、从classpath中搜索所有META-INF/spring.factories配置文件然后,将其中

org.springframework.boot.autoconfigure.EnableAutoConfiguration key对应的配置项加载到spring容器。只有spring.boot.enableautoconfiguration为true(默认为true)的时候,才启用自动配置,最终得到EnableAutoConfiguration的配置值,并将其封装到一个List中返回。

4、对上一步返回的List中的元素去重、排序。

5、依据第2步中获取的属性值排除一些特定的类,排除方式有两种,一是根据class来排除(exclude),二是根据classname(excludeName)来排除。

6、对上一步中所得到的List进行过滤,过滤的依据是条件匹配。这里用到的过滤器是org.springframework.boot.autoconfigure.condition.OnClassCondition最终返回的是一个ConditionOutcome[]数组。(PS:很多类都是依赖于其它的类的,当有某个类时才会装配,所以这次过滤的就是根据是否有某个class进而决定是否装配的。这些类所依赖的类都写在META-INF/spring-autoconfigure-metadata.properties文件里)

关键点

1、ImportSelector 该接口的方法的返回值都会被纳入到spring容器管理中

2、SpringFactoriesLoader 该类可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置。同理,其实Spring框架本身也提供了几个名字为@Enable开头的Annotation定义。比如@EnableScheduling、@EnableCaching、@EnableMBeanExport等,@EnableAutoConfiguration的理念和这些注解其实是一脉相承的。

观察@EnableAutoConfiguration可以发现,这里Import了@EnableAutoConfigurationImportSelector,这就是Spring Boot自动化配置的“始作俑者”。

至此,我们知道,由于我们在Spring Boot的启动类上使用了@SpringBootApplication注解,而该注解组合了@EnableAutoConfiguration注解,@EnableAutoConfiguration是自动化配置的“始作俑者”,而@EnableAutoConfiguration中Import了@EnableAutoConfigurationImportSelector注解,该注解的内部通过SpringFactoriesLoader.loadFactoryNames()读取了ClassPath下面的META-INF/spring.factories文件中key为的字符串数组,然后加载字节码,最后纳入到Spring IOC容器中~

根据当前不同的条件判断,决定这个配置类是否生效

  • 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类

**xxxxAutoConfigurartion:自动配置类;**给容器中添加组件

xxxxProperties:封装配置文件中相关属性;

自动配置类必须在一定的条件下才能生效

@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。

我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

#开启springboot的调试类
debug=true

Positive matches:(自动配置类启用的:正匹配)

Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

Unconditional classes: (没有条件的类)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值