第29天 三方集成自动配置

目录

三方集成

1. 配置优先级

1.1 配置方式

1.2 配置优先级

2. Bean管理

2.1 第三方Bean

2.2 获取bean

2.3 bean的作用域

3. 自动配置原理

3.1 起步依赖

3.2 自动配置


三方集成

1. 配置优先级

1.1 配置方式

  • SpringBoot除了支持properties的配置文件格式以外,还支持yaml格式的配置文件(yaml或yml)。

A. application.yaml

server:
  port: 8080

B. application.yml

server:
  port: 8081

C. application.properties

server.port=8082

注意事项:

  • 虽然SpringBoot支持多种格式的配置文件,但是在项目开发时,推荐统一使用一种格式的配置(yml是主流)。
  • SpringBoot 除了支持配置文件属性配置以外,还支持Java系统属性和命令行参数的方式进行属性配置。

A. Java系统属性配置
 

-Dserver.port=8083

如果我们的应用程序是在IDEA中运行,可以在IDEA工具栏的 "VM options" 栏设置Java系统属性。

B. 命令行参数配置
 

--server.port=8084

如果我们的应用程序是在IDEA中运行,可以在IDEA工具栏的 "Program arguments" 栏设置命令行参数。

如果项目开发完了,打包运行,我们可以在运行jar包时,在命令行中加入指定参数:

  • 执行maven的打包指令package(或者直接通过图形化界面操作)
     

  • 执行java命令,运行jar包
java -Dserver.port=8083 -jar tlias-web-management-0.0.1-SNAPSHOT.jar --server.port=8084

注意事项:

  • SpringBoot项目进行打包时,需要引入插件 spring-boot-maven-plugin (通过官方骨架创建的工程自带了)。
     

1.2 配置优先级

上述我们提到,同一个属性,我们可以通过五种形式来进行配置。 而这五种配置方式,优先级由低到高为:

  • application.yaml
  • application.yml
  • application.properties
  • Java系统属性(-Dxxxx=xxxx)
  • 命令行参数(--xxxx=xxxx)

这些配置的优先级,我们可以参考官方文档:

注意: 上述顺序,是加载顺序,如果属性名相同,后加载的会覆盖先加载的。 官方文档地址:Core Features

2. Bean管理

2.1 第三方Bean
 

  • 如果要管理的对象是来自于第三方的(不是自定义的),是无法直接使用 @Component 及衍生注解来声明 bean 对象的,就需要用到 @Bean注解。
@SpringBootApplication
public class SpringbootThirdbeanApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootThirdbeanApplication.class, args);
    }
    
    @Bean
    public BASE64Decoder base64Decoder(){
        return new BASE64Decoder();
    }
}
  • 如果要管理的第三方 bean 比较多,建议对这些bean进行集中分类配置,可以通过 @Configuration 注解声明一个配置类。
@Configuration
public class CommonConfig {
    @Bean
    public BASE64Encoder base64Encoder(){
        return new BASE64Encoder();
    }
	
    @Bean
    public BASE64Decoder base64Decoder(){
        return new BASE64Decoder();
    }
}

注意事项:通过 @Bean 注解的name属性 或 value 属性可以声明 bean 的名称,如果不指定,默认bean的名称就是方法名。

@Component 及 衍生注解 与 @Bean注解的使用场景:

  • 项目中自定义的,直接使用@Component及其衍生注解来声明bean即可。
  • 项目中引入的第三方的,使用@Bean注解声明bean即可。

2.2 获取bean

Spring 容器启动时,会把其中的 bean 都创建准备好,如果想要主动获取这些 bean,可以使用如下方法:

  • 根据类型获取 bean : <T> T getBean(Class<T> requiredType)
    • 可以传递父类型,返回子类型对象
    • 可以传递接口类型,返回实现类型对象
  • 根据 指定bean名称获取 bean :Object getBean(String name)
  • 根据 指定bean名称和bean类型获取(带类型转换) : <T> T getBean(String name, Class<T> requiredType)

注意事项:

  • 上述所说的 "Spring容器启动时,会把其中的bean都创建好",还会受到作用域 scope 及延迟初始化 lazy 影响,这里主要针对于 默认的单例非延迟加载的bean而言。

测试:

@SpringBootTest
class SpringbootThirdbeanApplicationTests {
	
    @Autowired
    private ApplicationContext context;
	
    @Test
    public void testGetBean(){
		DeptController deptController = applicationContext.getBean(DeptController.class);
         System.out.println(deptController);
        
         	
         Object object = applicationContext.getBean("deptController");
         System.out.println(object);
        
         DeptController deptController2 = applicationContext.getBean("deptController", DeptController.class);
         System.out.println(deptController2);
    }
	
}

2.3 bean的作用域

Spring 支持五种作用域,后三种在 web 环境才生效

作用域

说明

singleton

容器内同 id 的 bean 只有一个实例(单例)(默认)

prototype

每次使用该 bean 时会创建新的实例(非单例)

request

每个请求范围内会创建新的实例(web环境中,了解)

session

每个会话范围内会创建新的实例(web环境中,了解)

application

每个应用范围内会创建新的实例(web环境中,了解)

以上五种作用域,配合 @Scope 注解来进行配置:

@Scope("prototype")
@RestController
@RequestMapping("/depts")
public class DeptController {
	//....
}

注意事项:

  • 默认singleton的bean,在容器启动时被创建。
  • prototype的bean,每一次使用该bean的时候,都会创建一个新的实例。

测试:

A. @Scope 为取默认值 singleton 的时候

@RestController
@RequestMapping("/depts")
public class DeptController {
	//....
}

测试代码:

@Autowired
private ApplicationContext applicationContext;

@Test
public void testGetBean(){
    for (int i = 0; i < 10; i++) {
        DeptController deptController = applicationContext.getBean(DeptController.class);
        System.out.println(deptController.hashCode());
    }
}

测试结果:

1674938191
1674938191
1674938191
1674938191
1674938191
1674938191
1674938191
1674938191
1674938191
1674938191

B. @Scope 为取默认值 prototype的时候

测试代码:

@Autowired
private ApplicationContext applicationContext;

@Test
public void testGetBean(){
    for (int i = 0; i < 10; i++) {
        DeptController deptController = applicationContext.getBean(DeptController.class);
        System.out.println(deptController.hashCode());
    }
}

测试结果:

1476484694
1295803795
1948689480
1365533388
54879576
771459166
1824423245
507383828
913955118
1202790087

3. 自动配置原理

前面我们提到,Spring家族的其他框架都是基于SpringFramework的。 但是直接使用Spring框架进行集成开发比较繁琐,入门难度很大,所以在现在的企业开发中,都是直接基于Springboot进行开发,简单、快捷、高效,而且spring官方也是建议直接从springboot开始。

那是什么原因,让springboot这么火、这么好用呢?其实呢,原因主要有两点:

  • 起步依赖
  • 自动配置

3.1 起步依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
	
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
	
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

这里看到的spring-boot-starter-xxx 就是 SpringBoot的起步依赖。SpringBoot通过提供众多起步依赖降低项目依赖的复杂度。起步依赖本质上是一个Maven项目对象模型,定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。很多起步依赖的命名都暗示了他们提供的某种或某类功能。
 

在SpringBoot中还提供了很多的起步依赖,具体可以参考SpringBoot的官网:

3.2 自动配置

3.2.1 介绍

  • SpringBoot的自动配置就是当spring容器启动后,一些配置类就自动装配的IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置等操作。
  • 要想明白SpringBoot 自动配置原理,先需要明白两个前置知识 @Conditional 条件注解 和 @Import 导入配置。

3.2.2 @Conditional

SpringBoot 提供了很多 @ConditionalOnXxx 注解,来判断当达成某一条件后,才加载对应的Bean。

  • 作用:按照一定的条件进行判断,需要声明的Bean,在满足给定条件后才会注册到Spring IOC容器中。

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

  • @ConditionalOnClass:判断环境中是否有对应字节码文件,才注册bean到IOC容器。
  • @ConditionalOnMissingBean:判断环境中没有对应的bean ,才注册bean到IOC容器。
  • @ConditionalOnProperty:判断配置文件中是否有对应属性和值,才注册bean到IOC容器。

演示1:

@Configuration
public class CommonConfig {
    @Bean
    @ConditionalOnClass(name = "io.jsonwebtoken.Jwts")
    public BASE64Encoder base64Encoder(){
        return new BASE64Encoder();
    }
}

当io.jsonwebtoken.Jwts 对应Class文件存在时,才加载BASE64Encoder这个Bean。

演示2:

@Configuration
public class CommonConfig {

    @Bean
    @ConditionalOnProperty(name = "secret",havingValue = "itheima")
    public BASE64Decoder base64Decoder(){
        return new BASE64Decoder();
    }
    
}

当配置文件中存在 secret: itheima,这一个配置项时,才会加载 BASE64Decoder 这个Bean。

演示3:

@Configuration
public class CommonConfig {

    @Bean
    @ConditionalOnMissingBean(name = "empController")
    public BASE64Decoder base64Decoder(){
        return new BASE64Decoder();
    }
    
}

当IOC容器中,没有名称为 empController 的bean的时候,才会加载 BASE64Decoder 这个Bean。

3.2.3 @Import

@Import注解用于导入一些Bean 和 配置类到IOC容器中。

先思考一个问题,SpringBoot是否能够直接加载第三方Bean?不能。因为第三方Bean很大可能和当前项目包结构不匹配。

故而可以采用@Import 注解导入。@Import注解主要可以导入形式有以下几种:

  1. Bean
  2. 配置类
  3. ImportSelector接口子类

SpringBoot自动配置采用的是第三种。

演示:

@Import({TokenParser.class,HeaderConfig.class})
@SpringBootApplication
public class SpringbootThirdbeanApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootThirdbeanApplication.class, args);
    }
}

3.2.4 自动配置原理

  1. 当SpringBoot 程序启动时,引导类上 @SpringBootApplication 注解生效,该注解由三个注解组成

  • @SpringBootConfiguration:引导类也是一个配置类
  • @ComponentScan:包扫描
  • @EnableAutoConfiguration:自动配置。底层为:@Import(AutoConfigurationImportSelector.class),通过@Import导入配置类

  1. 查看 AutoConfigurationImportSelector源码


 

SpringBoot程序在启动时会自动加载 META-INF/spring.factories 文件 和 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,并导入其中定义的所有配置类。


 

  1. 由于这些配置类上都加了Condition条件注解,所有不会将所有Bean 加载到IOC容器中,只有满足条件的Bean才会加载。

3.2.5 自定义starter

在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot 的 starter,再上传到公司的私服中。将来引入对应坐标依赖,即可快速使用这些功能。一般要自定义starter,可以参考其他starter即可,比如 mybatis 的starter

本课程中以自定义 Aliyun的OSS工具类为例。

  1. 创建模块aliyun-oss-spring-boot-starter,该模块作为整合依赖模块(可以删除src目录)。
<groupId>com.itheima</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>

  1. 创建aliyun-oss-spring-boot-autoconfigure模块,用于提供自动配置功能,坐标如下参考:
<groupId>com.itheima</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>


aliyun-oss-spring-boot-autoconfigure的依赖配置如下:

<dependencies>
  
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
<!-- 阿里云SDK依赖 -->
  <dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.15.0</version>
  </dependency>
<!-- Lombok -->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
  </dependency>
</dependencies>
  1. 在aliyun-oss-spring-boot-starter引入 aliyun-oss-spring-boot-autoconfigure坐标。将来使用阿里云 oss功能,只需要导入aliyun-oss-spring-boot-starter即可。

aliyun-oss-spring-boot-starter的依赖配置如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.itheima</groupId>
        <artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

  1. 在aliyun-oss-spring-boot-autoconfigure模块中 编写核心逻辑。此例中复制 AliOSSUtils 工具类改造即可。
    创建包 com.itheima.oss

package com.itheima.oss;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
}
package com.itheima.oss;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 阿里云 OSS 工具类
 */
@Data
public class AliOSSUtils {

    private AliOSSProperties aliOSSPro;

    /**
     * 实现上传图片到OSS
     * @param multipartFile 接收到的前端文件
     * @return url
     */
    public String upload(MultipartFile multipartFile) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = multipartFile.getInputStream();

        // 避免文件覆盖
        String fileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + multipartFile.getOriginalFilename();

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(
                aliOSSPro.getEndpoint(),
                aliOSSPro.getAccessKeyId(),
                aliOSSPro.getAccessKeySecret());
        ossClient.putObject(aliOSSPro.getBucketName(), fileName, inputStream);

        //文件访问路径
        String url = aliOSSPro.getEndpoint().split("//")[0] + "//" + aliOSSPro.getBucketName() + "." + aliOSSPro.getEndpoint().split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}

  1. 定义自动配置类

package com.itheima;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(AliOSSProperties.class) //作用相当于@Import
public class AliyunOSSAutoConfiguration {

    @Bean
    public AliOSSUtils aliOSSUtils(AliOSSProperties aliyunOSSProperties){
        AliOSSUtils aliOSSUtils = new AliOSSUtils();
        aliOSSUtils.setAliOSSPro(aliyunOSSProperties);
        return aliOSSUtils;
    }
}

  1. 在resources目录下定义 META-INF/spring.factories,SpringBoot程序启动后会自动读取该文件加载键为:
    org.springframework.boot.autoconfigure.EnableAutoConfiguration的值 ,值要设定为配置类的全限定类名。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.itheima.AliyunOSSAutoConfiguration

  1. 在配置文件(properties或者yml或者yaml)中加入OSS的配置信息

以yml为例

  1. 测试,此例中,在需要使用的项目中中引入starter依赖即可,比如在之前所学的UploadController中注入 AliOSSUtils,测试使用

项目引入aliyun-oss-spring-boot-starter起步依赖。

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>aliyun-oss-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

在FileUploadController注入使用中使用

package com.itheima.controller;

import com.itheima.AliOSSUtils;
import com.itheima.pojo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@RestController
    public class FileUploadController {
        @Autowired
        AliOSSUtils aliOSSUtils; //引入起步依赖的工具

        @PostMapping("/upload")
        public Result fileUpload(MultipartFile image) throws IOException {
            //上传文件到alioss
            String url = aliOSSUtils.upload(image);
            return Result.success(url);
        }
    }

启动项目,在Postman中进行测试:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值