闲来无事,写个二维码耍耍

310 阅读4分钟

挺久没写文章了,今天写一篇关于二维码的吧。本篇文章会带大家做一个二维码的启动器,各位感兴趣的可以动手搞一下。如果公司内部有需求的话,还可以做成依赖放到公司自己的仓库中。

e8bd9419ly1g3kdptdjdqj20f40kfavc.jpg

你不想看到的东西,我都扔了,你可以放心的住进来了

要我说, springboot 启动器真是个好东西,把原先繁琐的配置都省掉了,但它们并不是消失了,而是换了一种方式在你身边。

创建 maven 聚合工程

聚合工程下面包含两个子工程:QRCode-spring-boot-starter 和 QRCode-spring-boot-autoconfigure

启动器项目结构.png

父工程的 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>test</groupId>
    <artifactId>Qr-code-starters</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>QRCode-spring-boot-starter</module>
        <module>QRCode-spring-boot-autoconfigure</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <dependencies>

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

    </dependencies>


</project>

其中 QRCode-spring-boot-starter 这个项目下面是没有包含任何代码的,只有一个 pom 文件,pom 文件会引入 QRCode-spring-boot-autoconfigure 这个依赖。外部使用启动器的时候也是导入 QRCode-spring-boot-starter 这个依赖即可:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Qr-code-starters</artifactId>
        <groupId>test</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>QRCode-spring-boot-starter</artifactId>

    <dependencies>
        
        <dependency>
            <groupId>test</groupId>
            <artifactId>QRCode-spring-boot-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>

编写自动配置的相关内容

在 springboot 中,所有启动器的编写都大差不差的。首先是在 pom 文件中导入所需的依赖; 其次就是编写 @ConfigurationProperties 注解修饰的配置类,用于封装使用到的所有配置,通常以 Properties 结尾;然后就是编写以 AutoConfiguration 结尾的配置类,配置类通过 @Bean 注解 + 各种条件注解往 ioc 容器中注入 bean 实例;最后一步,也是关键的一步,就是要在 resources 目录下创建一个名为 META-INF 的子目录。在这个子目录下,创建一个名为 spring.factories 的文件,这个文件中要指明启动器中包含了哪些自动配置类。

本篇文章是以二维码为例子,那就肯定要导入生成二维码要用到的依赖了:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Qr-code-starters</artifactId>
        <groupId>test</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>QRCode-spring-boot-autoconfigure</artifactId>

    <dependencies>

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.0</version>
        </dependency>

    </dependencies>


</project>

编写用于封装配置的 QRCodeProperties:

// 在配置文件中可以通过 prefix + 属性名的方式将属性值封装到该类中
@ConfigurationProperties(prefix = QRCodeProperties.QRCODE_PREFIX)
public class QRCodeProperties {

    public static final String QRCODE_PREFIX = "qrcode";

    private Integer width = 360;

    private Integer height = 360;

    public Integer getWidth() {
        return width;
    }

    public void setWidth(Integer width) {
        this.width = width;
    }

    public Integer getHeight() {
        return height;
    }

    public void setHeight(Integer height) {
        this.height = height;
    }
}

编写自动配置类 QRCodeAutoConfiguration:


@EnableConfigurationProperties(value = QRCodeProperties.class) // 用于导入配置类
// 自动配置类通常都是要在满足一定条件的情况下才可以生效,因此 @Conditional 系的注解不可少
@ConditionalOnClass(value = {QRCodeWriter.class, BarcodeFormat.class, MatrixToImageWriter.class, BitMatrix.class})
@Configuration
public class QRCodeAutoConfiguration {


    @ConditionalOnMissingBean
    @Bean
    public QRCodeGenerator defaultQRCodeGenerator(QRCodeProperties properties){
        return new QRCodeGenerator(properties);
    }

    @ConditionalOnMissingBean
    @Bean
    public BarcodeFormat defaultBarcodeFormat(){
        return BarcodeFormat.QR_CODE;
    }

}

QRCodeGenerator 就是负责生成二维码的类:

import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import java.io.ByteArrayOutputStream;
import java.io.IOException;


public class QRCodeGenerator implements InitializingBean {

    private Integer width = 360;

    private Integer height = 360;

    @Autowired(required = false)
    private BarcodeFormat format ;

    public QRCodeGenerator(){}

    public QRCodeGenerator(QRCodeProperties properties){
        this.width = properties.getWidth();
        this.height = properties.getHeight();

    }

    public byte[] getQRCodeImage(String text) throws WriterException, IOException {
        Assert.notNull(text,"text is not allow null ");
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(text, format, width, height);
        ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream);
        return pngOutputStream.toByteArray();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (format == null)
            format = BarcodeFormat.QR_CODE;
    }
}

关键一步,在 resources 目录下创建一个名为 META-INF 的子目录。在这个子目录下,创建一个名为 spring.factories 的文件:

关键一步.png

spring.factories 中的内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
spring.boot.qrCode.autoconfigure.QRCodeAutoConfiguration

等号右边跟的是自动配置类的全类名,也可以跟多个自动配置类,比如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

到这,一个简单的二维码启动器就完成了,其他项目需要用到的话,直接在 pom 文件中引入即可。

大家平时在编写配置文件时,idea 一般都会有以下的提示:

配置提示.png

这种提示其实就是 META-INF 目录下的 spring-configuration-metadata.json 文件在发挥作用,这种提示还是很有必要的,能够让使用你这个启动器的开发人员明白每个配置的作用是啥。

{
  "properties": [
    {
      "name": "qrcode.width",
      "type": "java.lang.Integer",
      "description": "qrcode width",
      "sourceType": "spring.boot.qrCode.autoconfigure.QRCodeProperties"
    },
    {
      "name": "qrcode.height",
      "type": "java.lang.Integer",
      "description": "qrcode height",
      "sourceType": "spring.boot.qrCode.autoconfigure.QRCodeProperties"
    }
  ]
}

加上了这个 .json 文件后就有了下面的效果:

二维码配置提示.png

笔者在这里只是做了一个简单的启动器,主要是想向大家展示一个启动器的编写流程。整个过程不难,各位看官可以动手实践一下。

笔者最近一直想培养一个兴趣爱好,各位屏幕前的大仙如果有推荐的话,可以在评论区中说下。说不定,误打误撞的就能发展成一门副业。毕竟,谁不羡慕把爱好当成事业的人呢。