Web后端开发-原理篇(1)

时间:2024-01-26 17:51:58

        hello啊各位,我们前期一直在讲解web后端开发都是面向应用层面的,而我们今天讲解的是Web后端开发的最后一个篇章——springboot原理篇,即springboot的原理。

那我们今天讲解的内容包括三个部分,分别是配置优先级、Bean管理、SpringBoot原理

配置优先级

        首先是SpringBoot项目当中属性配置的常见方式以及配置的优先级,也就是如果我们用多种方式配置了同一个属性,那到底哪种方式生效?前面的博客我们已经说明了SpringBoot中支持的三种格式的配置文件:application.properties、application.yml、application.yaml,要想配置一个属性,我们通过这三种文件的任意一种都可以,那如果我在三种配置文件中配置了同一个属性(servlet的端口号),那到底哪个配置文件生效呢?我们直接演示一下

        启动完之后,我们发现生效的端口号为8081,即properties文件中配置的,那说明这三个配置文件中优先级最高的是properties,我们将其注释,再次重启服务测试另外两个,现在端口号变为了8082,即优先级properties>yml>yaml

那么虽然springboot支持多种格式配置文件,但是在项目开发中,推荐统一使用一种格式的配置

SpringBoot除了支持配置文件属性配置,还支持java系统属性和命令行参数的方式进行属性配置

  • Java系统属性:-Dserver.port=9000
  • 命令行参数: -- server.port=10010

我们如何配置呢?idea已经为我们贴心的提供了可视化的界面

我们可以在VM options里面添加java系统属性,在Program arguments里面添加命令行参数

我们注释掉前面的三种,再次运行,发现端口号为10010,即命令行参数>Java系统属性

那么如果我们之后将项目上线如何进行java系统属性和命令行参数的添加呢?步骤如下

  • 执行maven打包指令package,将其打包成jar
  • 执行java指令,运行jar包
  • java -Dserver.port=9090 - jar tlias-web-management-0.0.1-SNAPSHOT.jar -- server.port=10010

        具体不再演示,以上便是我们所介绍的五种优先级了,那么我们再次全部打开,来比较五种配置的优先级,最后配置优先级结果如下

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

Bean管理

        接着我们来讲解SpringIOC容器中Bean的管理,我们前面已经讲过如何用spring当中提供的注解component,以及它的三个衍生注解,来声明IOC容器中的bean对象,我们也讲解了如何为应用程序注入依赖的bean对象。那这一小节我们主要来讲解Bean的获取、Bean的作用域、第三方Bean的管理等bean的使用细节。

        Bean的获取

首先,我们来讲解一下从IOC容器中获取bean对象,默认情况下,Spring项目启动时,会把bean创建好放在IOC容器中,如果想要主动获取这些bean,可以通过如下方式

  • 根据name获取bean:Object getBean(String name)
  • 根据类型获取bean:<T> T getBean(Class<T> requiredType)
  • 根据name获取bean(带类型转换):<T> T getBean(String name, Class<T> requiredType)

接下来我们进行代码演示

package com.itheima;

import com.itheima.controller.DeptController;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

@SpringBootTest
class SpringbootWebConfig2ApplicationTests {

    @Autowired
    private ApplicationContext applicationContext; //IOC容器对象

    @Test
    public void testGetBean(){
        //根据bean的名称获取
        DeptController bean1 = (DeptController) applicationContext.getBean("deptController"); //Bean名称类名首字母小写
        System.out.println(bean1);
        
        //根据bean的类型获取
        DeptController bean2 = applicationContext.getBean(DeptController.class);
        System.out.println(bean2);

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

        以上便是bean的三种获取方式了,我们运行后发现,这三个从IOC容器中获取的bean对象,其地址值是一样的,那就说明IOC容器中的bean对象有且只有一个,默认情况下这个bean是单例的,我们如何设置其为非单例的呢?那就涉及到我们的作用域了。

        Bean的作用域

        因为在spring当中容器当中bean对象默认是单例的,即只有一个实例对象,那我们所声明的bean对象到底是单例还是多例,其实是取决于bean的作用域的。

在spring当中支持五种作用域,但后三种在web环境才生效

        以上我们只记住前两种即可,那我们如何配置作用域呢?我们可以通过@Scope注解来进行配置作用域。

        第三方Bean

        讲解完了bean的获取、bean的作用域,接下来我们再来讲解最后一个方面,第三方bean的配置,我们之前声明的dao、service等都是我们自己定义的,为其添加bean只需加上@component注解或其衍生注解即可。但是,在项目开发中还有一种情况,就是这个类不是我们自己定义的,使我们引入的第三方依赖所提供的。此时我们就要用到@Bean注解

@Bean:如果我们要管理的对象来自于第三方(不是自定义的),是无法用@component及衍生注解声明bean的,就需要用到@Bean注解

接下来我们在启动类进行第三方bean的定义并进行测试

//声明第三方bean对象
    @Bean //将当前方法的返回值交给IOC容器管理,成为ioc容器bean
    public SAXReader saxReader(){
        return new SAXReader();
    }
@Test
    public void testThirdBean() throws Exception {
        SAXReader saxReader = new SAXReader();

        Document document  = saxReader.read(this.getClass().getClassLoader().getResource("1.xml"));
        Element rootelement = document.getRootElement();
        String name = rootelement.element("name").getText();
        String age = rootelement.element("age").getText();

        System.out.println(name + ":" + age);
    }

        但是在项目中我们要保证启动类的纯粹性,所以我们不建议在启动类中定义第三方bean,我们建议单独定义一个config类,并且通过一个@configuration声明一个配置类,在配置类中对第三方bean进行集中管理

@Configuration
public class CommonConfig {

    //声明第三方bean
    @Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
          //通过@Bean注解的name/value属性指定bean名称,如果未指定,默认是方法名
    public SAXReader reader(DeptService deptService){
        System.out.println(deptService);
        return new SAXReader();
    }
}

注意:

  • 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名
  • 如果第三方bean需要依赖其他对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配
  • 项目中定义的,使用@component及其衍生注解
  • 项目中引入第三方的,使用@Bean注解

SpringBoot原理

        最后我们再来剖析SpringBoot的底层应用原理,基于之前的博客,我们可以发现基于springboot开发是非常高效的。SpringBoot使我们能够更加专注的关注业务功能的开发,而不用过多关注框架本身的配置使用,通过springboot的原理分析,我们可以知道为什么springboot如此高效,也能让我们更加熟练地使用springboot进行开发了。

        在剖析springboot之前,我们先来简单回顾一下spring框架,spring是目前世界上最流行的java框架,他可以帮助我们更加容易的构建java项目,其所有的框架都是基于SpringFramework这个基础框架的。而我们使用spring框架进行开发会比较繁琐(依赖、配置),所以才会有了我们的SpringBoot,通过SpringBoot来简化spring的开发。为什么SpringBoot框架会较为简单高效呢?是因为springboot提供了两个非常重要的功能,一个是起步依赖,一个是自动配置。接下来我们会根据这两点对springboot的原理进行解析

        起步依赖

        如果我们使用原始的spring框架进行web程序开发,那么我们就必须自己去引入其需要的依赖,且必须保证其版本对应,而如果我们使用了springboot,就不需这么繁琐,只需引入一个起步依赖,spring-boot-starter-web,即web开发的起步依赖,其里面集成了所有常见的web开发依赖。他将通过maven的依赖传递传递进来,即a依赖b,b依赖c,这会将所有依赖都引入进来。这就是springboot的起步依赖,其原理就是maven的依赖传递。

        自动配置

        SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

        可能这样描述并不能很清楚的理解,我们现在来看一下,我们随便启动一个服务,查看其bean对象,可以发现里面会有这个项目引入以及未引入的bean对象,这些都是springboot自动加载进来的

自动配置原理

        我们解析自动配置的原理就是来分析在springboot当中,我们引入的依赖是如何将依赖jar包所提供的这些bean以及配置类直接加载到项目中的springIOC容器当中的

我们如果想要使用第三方包依赖,首先要将其导入,之后我们可以通过以下方式完成

方案一:@Component组件扫描

但是这种方案较为繁琐,每使用一个第三方依赖就需要进行组件扫描,这样会让性能不高

方案二:@import导入。使用@import导入的类会被Spring加载到IOC容器中导入形式有以下几种:

  • 导入普通类
  • 导入配置类
  • 导入ImportSelector
  • @EnableXxxx注解,封装@Import注解

我们通过这四种方式都可以导入配置操作。接下来我们通过源码追踪可以发现

@SpringBootApplication:该注解标识在SspringBoot工程引导类上,是SpringBoot最最最重要的注解。该注解由三部分组成:

  • @SpringBootConfiguration:该注解与@Confirguration注解作用相同,用来声明当前也是一个配置类
  • @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
  • @EnableAutoConfirguration:SpringBoot实现自动化配置的核心注解

@Confitional

作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到SpringIOC容器中

位置:方法、类

@Conditional本身是一个父注解,派生出大量的子注解:

  • @ConditionalOnClass:判断环境中是否有对应字节码文件,只有有才注册bean到IOC容器
  • @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),只有没有才注册bean到IOC容器       
  • @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器
案例(自定义starter)

自定义starter

        在实际开发中,经常会定义一下公共组件,提供给各个项目团队使用。而在SpringBoot项目中,一般会将这些公共组件封装为SpringBoot的starter

现在我们来完成一个案例

  • 需求:自定义aliyun-oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置
  • 目标:引入起步依赖之后,要想使用阿里云OSS,注入AliyunOSSUtils直接使用即可

步骤:

  • 创建aliyun-oss-spring-boot-starter模块
  • 创建aliyun-oss-spring-boot-autoconfigure模块,在starter中引入该模块
  • 在aliyun-oss-spring-boot-autoconfigure模块中的定义自动配置功能,并定义自动配置文件META-INF/spring.xxxx.imports

接下来我们就参照该步骤完成starter的开发

创建完模块把这两个文件留下即可

最后这个案例作者没有跟下来……

并且对于自动配置这点我并没有太听明白,所以只能尽力去写,是在抱歉

        以上便是这篇博客的全部内容了,最后还是建议大家去看一下黑马的视频多多理解原理篇,本篇博客如有不正确的,还希望大家多多指正