现象

发起HTTP请求调用,控制台报错:

org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation

​ at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:322) ~[spring-webmvc-6.1.10.jar:6.1.10]

阅读全文 »

任何接口,如果只包含一个抽象方法,它就是一个函数式接口。对于函数式接口,可以通过Lambda表达式来创建该接口的对象

Runnable就是一个函数式接口。

  1. 新建一个函数式接口,并在抽象方法上加上@Transactional的事务注解。
  2. 在实际业务逻辑中需要事务的地方,通过Lambda表达式创建该接口的对象(必须实现唯一的抽象方法)。
  3. 执行该接口对象的方法,即将方法内的逻辑以事务的形式去执行。

spring事务的依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
</dependency>
public class Test {
    public void testTransaction() {
        int a = 0;
        int b = 1;

        TransactionConsumer transactionConsumer = () -> {
            insertDataA(a);
            updateDataB(b);
        };
        transactionConsumer.doInTransaction();
    }

    private void updateDataB(int b) {
    }

    private void insertDataA(int a) {
    }
}

@FunctionalInterface
public interface TransactionConsumer {
    @Transactional
    void doInTransaction();
}

Lombok介绍

Lombok 项目其实就是一个 java 库,它可以自动插入到编辑器和构建工具中,增强 java 的性能。以后你 只需要一些简单的注解,就可以再也不用在类中反复去写 getter、equals 等方法,让代码变得简介,不用过多地去关注相应的方法。属性修改时,也简化了维护这些属性所生成的 get、set 方法。

使用方式:

  1. IDEA 下载 Lombok 插件

  2. 项目中导入 Lombok 的 jar 包

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.26</version>
</dependency>
  1. 实体类 上使用 Lombok 注解
阅读全文 »

“::” 的含义

在 Java 中,双冒号 “::” 是 方法引用的语法。方法引用是一种 简化 Lambda 表达式的语法结构,使代码更加简洁易读。并且在使用方法引用时,会根据上下文推断参数类型,因此 特别适用于直接引用已有方法的情况


“::” 的用法

方法引用的一般形式:

ClassName::methodName

其中,ClassName 是包含方法 methodName 的类名。

Optional 被设计出来,主要是为了解决空指针异常

以一个 Student 类为例:

@Getter
class Student {
    private String name;
    private int age;
    private int score;

    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public Student() {
    }
}
阅读全文 »

基础理论

CAP理论和BASE理论

一致性的三种级别

  • 强一致性

    系统写入了什么,读出来就是什么。

  • 弱一致性

    不一定可以读取到最新写入的值,也不保证多少时间之后能读取到最新的,只尽量保证某个时刻达到数据一致的状态。

  • 最终一致性

    弱一致性的升级版。不一定可以读取到最新写入的值,但保证在一定时间内达到数据一致的状态。

业界比较推崇最终一致性,但是某些对数据一致要求非常严格的场景,比如银行转账,还是要保证强一致性。

阅读全文 »

PriorityQueue的使用场景

Queue<Integer> queue1 = new PriorityQueue<>();

Queue<Integer> queue2 = new PriorityQueue<>(Comparator.comparingInt(o -> o));

Queue<Integer> queue3 = new PriorityQueue<>(Comparator.comparingInt(o -> -o));

Java中的PriorityQueue常用于实现小顶堆、大顶堆。由上述代码可见,通常有默认无参构造函数、传入Comparator对象等方式的实现。


困惑

从构造函数层面,完全不清楚默认构造函数的排序规则,也完全不明白Comparator实现的comparingInt方法对应的排序规则

根据网上绝大多数文章的描述,都没有解释或者只是说明了对应的排序规则,但是不知道为什么是对应升序或降序。通过阅读源码,才得到准确的认知。

阅读全文 »

概念

图床是一个网络术语,指的是一种用于存储和托管图片的在线服务。用户可以上传图片文件,并获得一个唯一的链接或嵌入代码,方便在网页、博客、论坛等地方分享和展示这些图片。


七牛云

将七牛云作为图床,实际上是使用了七牛云的对象存储服务(OSS)

新建存储空间

image-20220227224537701

image-20220227224443813

获取密钥(AK、SK)

image-20250124095003483

通过Java进行文件上传

官方开发文档:Java SDK_SDK 下载_对象存储 - 七牛开发者中心

首先添加依赖:

<dependency>
    <groupId>com.qiniu</groupId>
    <artifactId>qiniu-java-sdk</artifactId>
    <version>7.17.0</version>
</dependency>

注意:

7.17.0版本,qiniu-java-sdk自身依赖的gson的scope为runtime,在编译 main 目录下的 Java 类时,编译器会无法找到 gson需要显式添加gson的依赖

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.11.0</version>
</dependency>

将密钥和目标桶 添加到配置文件中

qiniu:
  accessKey: "实际的ak"
  secretKey: "实际的sk"
  bucket: "目标存储空间名称"

编写文件上传测试代码

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.beans.factory.annotation.Value;

@SpringBootTest
@Slf4j
public class OSSTest {
    @Value("${qiniu.accessKey}")
    private String accessKey;

    @Value("${qiniu.secretKey}")
    private String secretKey;

    @Value("${qiniu.bucket}")
    private String bucket;


    @Test
    public void testOSS() {
        // 构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.autoRegion());
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本

        UploadManager uploadManager = new UploadManager(cfg);

        // 默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = "hunter.jpg";

        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);

        try {
            InputStream inputStream = new FileInputStream("C:\\Users\\Hunter\\Pictures\\Saved " +
                    "Pictures\\Rukawa-Sara-Sakuragi-Hanamichi-Slam-Dunk-slum-dunk.jpg");
            Response response = uploadManager.put(inputStream, key, upToken, null, null);
            // 解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
        } catch (QiniuException ex) {
            ex.printStackTrace();
            if (ex.response != null) {
                System.err.println(ex.response);

                try {
                    String body = ex.response.toString();
                    System.err.println(body);
                } catch (Exception ignored) {
                }
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            System.err.println("file not found");
        }
    }
}

实际应用代码

实际应用,需要获取图片上传至图床后的访问链接

七牛云提供了临时的外链域名

image-20250124154458026
qiniu:
  accessKey: "实际的ak"
  secretKey: "实际的sk"
  bucket: "目标存储空间名称"
  domain: "http://外链域名/"

  • 为了便于后期管理上传的图片,当前的 年份/月份/日期 + 随机数 + 文件类型的形式作为文件名,实际会/为分隔,按多级目录进行存储
  • 上传文件后,返回文件访问链接${qiniu.domain} + 文件名
@Service
@Slf4j
public class UploadServiceImpl implements UploadService {
    @Value("${qiniu.accessKey}")
    private String accessKey;

    @Value("${qiniu.secretKey}")
    private String secretKey;

    @Value("${qiniu.bucket}")
    private String bucket;

    /**
     * 域名
     */
    @Value("${qiniu.domain}")
    private String domain;

    @Override
    public ResponseResult<String> uploadImg(MultipartFile img) throws IOException {
        // 判断文件类型
        String originalFilename = img.getOriginalFilename();
        assert originalFilename != null;

        String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));

        if (!".jpg".equals(fileType) && !".jpeg".equals(fileType)
                && !".png".equals(fileType)) {
            throw new GlobalException(HttpCodeEnum.FILE_TYPE_ERROR);
        }

        // “当前的 年份/月份/日期 + 随机数 + 文件类型”作为文件名
        String fileName =
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/"))
                        + UUID.randomUUID()
                        + fileType;

        // 上传文件到七牛云,返回链接地址
        String url = uploadToQiniu(img, fileName);
        return ResponseResult.success(url);
    }

    /**
     * 上传文件到七牛云
     *
     * @param img      上传的文件
     * @param fileName 文件名
     * @return 文件存储的地址
     */
    private String uploadToQiniu(MultipartFile img, String fileName) throws IOException {
        // 构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.autoRegion());
        // 指定分片上传版本
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;

        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);

        try {
            UploadManager uploadManager = new UploadManager(cfg);
            InputStream inputStream = img.getInputStream();
            // 默认不指定key的情况下,以文件内容的hash值作为文件名
            Response response = uploadManager.put(inputStream, fileName, upToken, null, null);
            // 解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            log.info("文件成功上传到七牛云,key: {}, hash: {}", putRet.key, putRet.hash);
            return domain + putRet.key;
        } catch (QiniuException ex) {
            log.error("上传文件到七牛云失败: {}", ex.getMessage(), ex);
            throw ex;
        } catch (FileNotFoundException ex) {
            log.error("文件不存在: {}", ex.getMessage(), ex);
            throw ex;
        }
    }
}
0%