SpringBoot3

学习视频

工具

IDEA2021+、JDK17+、maven3.5+vscode

概述

Spring Boot是Spring提供的一个子项目,用于快速构建Spring应用程序

传统方式构建项目的弊端

image

传统方式是通过核心子项目,按需求整合其他功能模块。

存在的问题【感同身受】

  • 导入依赖繁琐
  • 项目配置繁琐

SpringBoot方式

因为传统的繁琐,spring官方提供了一个子项目spring boot来构建spring程序

SpringBoot特性

  • 起步依赖

    本质上就是一个Maven坐标,整合了完成一个功能需要的所有坐标

    image

  • 自动配置

    遵循约定大约配置的原则,在boot程序启动后,一些bean对象会自动注入到ioc容器,不需要手动声明,简化开发

  • 其他特性

    • 内嵌的Tomcat、Jetty(无需部署WAR文件

      不用打war包了,打jar包就行,因为内置tomcat了,spring启动时,内置tomcat自动启动

    • 外部化配置

      打成jar包以后如果配置文件需要修改,不用再重新编译打包部署了,只需修改外部配置文件即可

    • 不需要XML配置(properties/yml)

SpringBoot入门

使用 SpringBoot 开发一个web应用,浏览器发起请求 /hello后,给浏览器返回字符串 “hello world ~"

传统方式:

image

用SpringBoot:

image

用现有框架创建

  1. 创建Maven工程

    image

    image

  2. 导入spring-boot-stater-web起步依赖

    image

    这里已经自动生成了

    <!--    boot工程的父工程,用于管理起步依赖的版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    ....
    
    <!--        web起步依赖,这里不需要写版本号-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
  3. 编写Controller ,和之前一样

    package com.ario.springbootquickstart.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^-^";
        }
    }
    
  4. 提供启动类

    //启动类
    @SpringBootApplication
    public class SpringbootQuickstartApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootQuickstartApplication.class, args);
        }
    }
    

    这里自动生成了

  5. 点击运行

    image

    image

手动创建

  1. image

  2. 手动配置pom文件

    <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>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.4.3</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <artifactId>springboot-manualstart</artifactId>
        <packaging>jar</packaging>
    
        <name>springboot-manualstart</name>
        <url>http://maven.apache.org</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    
  3. 启动类

    image

    @SpringBootApplication
    public class SpringbootManualstartApplication
    {
        public static void main( String[] args )
        {
            //固定代码,启动类的字节码文件和main的参数
            SpringApplication.run(SpringbootManualstartApplication.class, args);
        }
    }
    
  4. 再把上面的controller复制过来就行,测试完成

  5. 当然可以手动添加resource,添加application.properties文件

配置文件

各种配置在官方网站都有

properties配置文件

以配合端口号和虚拟路径为例

application.properties

server.port=9090
server.servlet.context-path=/start

image

yaml配置文件【推荐】

可以把上面文件加.bak后缀名,使其失效

application.yml或application.yaml。常用前者

server:
  port: 9191
  servlet:
    context-path: /test

优点

  1. 层次清晰
  2. 关注数据

yml配置信息书写与获取

三方技术配置信息

在maven中导入对应jar包以后,直接写配置信息即可,不用手动获取,自动就完成了

自定义配置信息

通过配合可以减少硬编码,避免改变配置时重新对项目编译测试打包部署,减少损失。

  1. 信息书写

    值前边必须有空格,作为分隔符
    使用空格作为缩进表示层级关系,相同的层级左侧对齐

    application.yml文件

    #发件人相关的信息
    email:
      user: 111@qq.com
      code: ddfdf
      host: stmp.qq.com
      auth: true
    
    #学生爱好,这里用来展示数组的赋值
    hobbie:
      - 唱
      - 跳
      - rap
      - 打篮球
    
  2. 信息获取

    • @Value(“${键名}”)方式

      controller文件

      @Value("${email.user}")
      public String user;
      @Value("${email.code}")
      public String code;
      @Value("${email.host}")
      public String host;
      @Value("${email.auth}")
      private String auth;
      
    • @ConfigurationProperties(prefix=“前缀”)

      可以看出@Value(“${键名}”)方式的繁琐,可以用这种方式

      注意:实体类的成员变量名与配置文件中的键名保持一致

      @RestController
      @ConfigurationProperties(prefix = "email")
      public class HelloController {
      
          public String user;
          public String code;
          public String host;
          private String auth;
      
          @RequestMapping("/hello")
          public String hello(){
              return "hello world^--^";
          }
      }
      

整合mybatis

之前mybatis的使用

  • 导入jar包
  • 配置文件
    • SqlSessionFactoryBean
    • MapperScannerConfigurer
    • Datasource

在SpringBoot中

  • 导入起步依赖

    <!--mysql驱动依赖-->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>
    
    <!--mybatis的起步依赖-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  • 在application.yml中配置datasource

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis
        username: root
        password: 1234
    

接下来就可以直接使用了,照常编写controller,service,mapper去进行对数据库操作。

简洁了很多

Bean管理

Bean扫描

之前是两种方式

  • 标签:<context:component-scan base-package="com.ario"/>
  • 注解:@ComponentScan(basePackages = "com.ario")

现在是通过注解自动扫描

image

注意:SpringBoot默认扫描启动类所在的包及其所在包的子包【如图所示】。如果controller在这个范围外,这需要用之前方式显示配置

Bean注册

在Spring中一下注解可以进行Bean注册

注解 说明 位置
@Component 声明 bean 的基础注解 不属于以下三类时,用此注解
@Controller @Component 的衍生注解 标注在控制器类上
@Service @Component 的衍生注解 标注在业务类上
@Repository @Component 的衍生注解 标注在数据访问类上(由于与 mybatis 整合,用的少)

如果要注册的bean对象来自于第三方(不是自定义的),是无法用 @Component 及衍生注解声明bean的,因为包中是只读的形式。

可以用以下两个注解解决问题【假设已导入一个包括Country和Province的三方包】

  1. @Bean

    • 在启动类中【不建议,启动类就干启动的活】

      @SpringBootApplication
      public class SpringBootRegisterApplication {
          @Bean //将方法返回值交给IOC容器管理,成为IOC容器的bean对象
          public Resolver resolver(){
      		return new Resolver();
      	}
      }
      

      测试

      @SpringBootApplication
      public class SpringBootRegisterApplication {
          public static void main(String[] args) {
              //可以测试时候注册
              ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
      
              Country country = context.getBean(Country.class);
              System.out.println(country);
          }
          
          @Bean //将方法返回值交给IOC容器管理,成为IOC容器的bean对象
          public Country country(){
      		return new Country();
      	}
      }
      
    • 如果要注册第三方bean,建议在配置类中集中注册

      在启动类所在包【跟默认扫描一个道理,可以建层包】中建立配置类

      package com.ario.config;
      @Configuration
      public class CommonConfig {   		
          //对象默认的名字是: 方法名
          //@Bean("aa"),这里可以指定名字
          //如果方法的内部需要使用到ioc容器中已经存在的bean对象,那么只需要在方法上声明即可,spring会自动的注入
          @Bean
          public Province province(Country country){
              System.out.println("province: "+country);
              return new Province();
          }
      }
      

      测试

      @SpringBootApplication
      public class SpringbootRegisterApplication {
      
          public static void main(String[] args) {
              ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
      
          System.out.println(context.getBean("province"));
          }
      }
      
  2. @Import

    在启动类上加@Import(XXX.class)即可

    • 导入配置类

      如果配置类没在默认扫描范围内,手动扫描,以上面为例,启动类要写成

      @SpringBootApplication
      @Import(CommonConfig.class)
      public class SpringbootRegisterApplication {
      
          public static void main(String[] args) {
              ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
      
              //Country country = context.getBean(Country.class);
              //System.out.println(country);
      
              System.out.println(context.getBean("province"));
          }
      
      }
      

      当配置类有多个是要写成@Import({CommonConfig.class,CommonConfig.class,CommonConfig.class,.......})。以数组方式注入。显得很麻烦,用小面方法

    • 导入 ImportSelector 接口实现类

      接口实现类

      public class CommonImportSelector implements ImportSelector {
          @Override
          public String[] selectImports(AnnotationMetadata importingClassMetadata) {
             //返回配置类
              return new String[]{"com.ario.config.CommonConfig"};
          }
      }
      

      启动类

      @SpringBootApplication
      @Import(CommonImportSelector.class)
      public class SpringbootRegisterApplication {
      
          public static void main(String[] args) {
              ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args); System.out.println(context.getBean("province"));
          }
      }
      

      配置类不变

      上面字符串写死了,可以用下面这种方式根据灵活

      在resources下建common.imports文件

      com.itheima.config.CommonConfig
      com.itheima.config.CommonConfig12
      com.itheima.config.CommonConfig2
      

      接口实现类

      public class CommonImportSelector implements ImportSelector {
          @Override
          public String[] selectImports(AnnotationMetadata importingClassMetadata) {
              //读取配置文件的内容
              List<String> imports = new ArrayList<>();
              InputStream is = CommonImportSelector.class.getClassLoader().getResourceAsStream("common.imports");
              BufferedReader br = new BufferedReader(new InputStreamReader(is));
              String line = null;
              try {
                  while((line = br.readLine())!=null){
                      imports.add(line);
                  }
      
              } catch (IOException e) {
                  throw new RuntimeException(e);
              } finally {
                  if (br!=null){
                      try {
                          br.close();
                      } catch (IOException e) {
                          throw new RuntimeException(e);
                      }
                  }
              }
              return imports.toArray(new String[0]);
          }
      }
      

      配置类和启动类同上。

    上面启动类注解还有参数,可以更优雅,自定义组合注解

    在com.ario.anno包下建个注解

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

    这样启动类中@Import(CommonImportSelector.class)可以替换成@EnableCommonConfig

注册条件

有时我们不想或不需要注入某些类时,可以用注解条件完成

SpringBoot提供了设置注册生效条件的注解 @Conditional。为了简化使用spring给我们提供了一些衍生注解,这里说三个常用的。

注解 说明
@ConditionalOnProperty 配置文件中存在对应的属性,才声明该 bean
@ConditionalOnMissingBean 当不存在当前类型的 bean 时,才声明该 bean
@ConditionalOnClass 当前环境存在指定的这个类时,才声明该 bean
  • @ConditionalOnProperty

    //注入Country对象
    //如果配置文件中配置了指定的信息,则注入,否则不注入
    @ConditionalOnProperty(prefix = "country",name = {"name","system"})
    @Bean
    //@Value也可以用在参数前面
    public Country country(@Value("${country.name}") String name,@Value("${country.system}") String system){
        Country country = new Country();
        country.setName(name);
        country.setSystem(system);
        return country ;
    }
    

    配置文件,注解以后spring中将没有Country,用的话会报错。

    #country:
    #  name: china
    #  system: socialism
    
  • @ConditionalOnMissingBean

    //如果ioc容器中不存在Country,则注入Province,否则不注入
    @Bean
    @ConditionalOnMissingBean(Country.class)
    public Province province(){
        return new Province();
    }
    
  • @ConditionalOnClass

    @Bean
    //如果当前环境中存在DispatcherServlet类,则注入Province,否则不注入
    //如果当前引入了web起步依赖,则环境中有DispatcherServlet,否则没有
    @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
    public Province province(){
        return new Province();
    }
    

    不用web时注入核心依赖就行,不用非要注入web起步依赖

    <!--springboot核心依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    

自动配置原理

自动配置

遵循约定大约配置的原则,在boot程序启动后,起步依赖中的一些bean对象会自动注入到ioc容器。

相对于整合mybatis时的自动配置,在Bean管理中的例子显然不是自动配置的。不仅要导入jar包和编写配置文件,还要手动注册。

那三方包中是如何实现自动配置的呢,下面探究一下源码。

以程序引入spring-boot-starter-web 起步依赖,启动后,会自动往ioc容器中注入DispatcherServlet为例:

image

在springboot核心依赖中spring-boot-autoconfigure中找到imports文件,其中和Bean管理中那样作用的配置文件,有全类名,对应了配置类。探索完毕

image

在理解的基础上可以改造之前的jar包实现自动配置

改造前

image

改造后

image

面试

说一说SpringBoot自动配置原理?

  1. 在主启动类上添加了SpringBootApplication注解,这个注解组合了EnableAutoConfiguration注解
  2. EnableAutoConfiguration注解又组合了Import注解,导入了AutoConfigurationImportSelector类
  3. 实现AutoConfigurationImportSelector类的selectImports方法,这个方法经过层层调用,最终会读取META-INF 目录下的 后缀名 为imports的文件,当然了,boot2.7以前的版本,读取的是spring.factories文件
  4. 读取到全类名了之后,会解析注册条件,也就是@Conditional及其衍生注解,把满足注册条件的Bean对象自动注入到IOC容器中

自定义starter

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

这里以mybatis起步依赖为例

一般情况下起步依赖由两个工程组成

  1. 自动配置功能
  2. 依赖管理功能【常引入自动配置工程】

image

实例

需求:自定义mybatis的starter

步骤【照葫芦画瓢】:

总目录

image

自留有用的文件夹就行,这样结构清晰

  1. 创建 dmybatis-spring-boot-autoconfigure 模块,提供自动配置功能,并自定义配置文件 META-INF/spring/xxx.imports

    • 新建maven工程

      image

    • 引入其他依赖

      可以从现有的mybatis相关jar包可以看出还有一些包要引入

      image

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>3.1.2</version>
      </dependency>
      
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-jdbc</artifactId>
          <version>3.0.0</version>
      </dependency>
      
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.13</version>
      </dependency>
      
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>3.0.0</version>
      </dependency>
      
      
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
      </dependency>
      
    • 编写配置类

      @AutoConfiguration//表示当前类是一个自动配置类
      public class MyBatisAutoConfig {
      
          //SqlSessionFactoryBean
          @Bean
          //因为已经引入了jdbc的依赖,它会把DataSource,这里直接在参数中声明即可
          public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
              SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
       	//给sqlSessionFactoryBean配置数据库信息      
              sqlSessionFactoryBean.setDataSource(dataSource);
              return sqlSessionFactoryBean;
          }
          
          //MapperScannerConfigure
          @Bean
          //总之,在方法中要用那些对象,在方法的参数列表中声明即可,将来SpringBoot会自动注入
          public MapperScannerConfigurer mapperScannerConfigurer(BeanFactory beanFactory){
              MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
              //扫描的包:启动类所在的包及其子包
              //通过SpringBoot的起步依赖提供的API【这里是AutoConfigurationPackages类】找到启动类所在包。
              List<String> packages = AutoConfigurationPackages.get(beanFactory);
              //所在包只会是一个,去数组第一个元素即可。
              String p = packages.get(0);
              mapperScannerConfigurer.setBasePackage(p);
      
              //扫描的注解,就是Mapper的注解
              mapperScannerConfigurer.setAnnotationClass(Mapper.class);
              return mapperScannerConfigurer;
          }
      
      }
      
    • 书写配置文件,使其找到配置类

      在resources/META-INF/spring包下创建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件【名字要对】

      com.itheima.config.MyBatisAutoConfig
      

    到处已经具备了自动配置功能

  2. 创建 dmybatis-spring-boot-starter 模块,在starter中引入自动配置模块

    • 引入自动配置工程的依赖及其所包含的依赖【官方规范,方便分析依赖】

      <dependency>
        <groupId>com.itheima</groupId>
        <artifactId>dmybatis-spring-boot-autoconfigure</artifactId>
        <version>1.0-SNAPSHOT</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>3.1.2</version>
      </dependency>
      
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
        <version>3.0.0</version>
      </dependency>
      
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
      </dependency>
      
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>3.0.0</version>
      </dependency>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
      </dependency>
      

问题

maven工程中默认的jdk版本是5,这里要在两个工程中都指定一些jdk版本

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
    </plugins>
</build>
posted @ 2025-02-22 23:31  韩熙隐ario  阅读(97)  评论(0)    收藏  举报