吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7291|回复: 37
收起左侧

[Java 转载] 小说精品屋-plus数据服务层的设计和实现

  [复制链接]
Java学者 发表于 2020-12-7 09:19
本帖最后由 Java学者 于 2020-12-7 09:24 编辑

前言

小说精品屋中集成了比较多的数据服务。比如缓存相关的Redis和Ehcache,文件相关的本地、Aliyun OSS和FastDfs,搜索相关的ElasticSearch和Mysql,这些数据服务均可在配置文件中通过一行代码进行切换底层实现,下面以文件服务为例来说说数据服务层的具体设计与实现。

文件服务模块的设计与实现

  1. 新建文件服务接口,定义存储图片的抽象方法。
package com.java2nb.novel.service;

/**
 * @author 11797
 */
public interface FileService {

    /**
     * 将爬取的网络图片转存为自己的存储介质(本地、OSS、fastDfs)
     * @param picSrc 爬取的网络图片路径
     * @param picSavePath 保存路径
     * @return 新图片地址
     * */
    String transFile(String picSrc, String picSavePath);

}
  1. 新建本地文件服务实现类,实现文件服务接口,保存文件到本地,并通过@ConditionalOnProperty注解来控制当配置属性pic.save.storage=local时,该实现类会实例化被Spring容器管理。
package com.java2nb.novel.service.impl;

import com.java2nb.novel.core.utils.Constants;
import com.java2nb.novel.core.utils.FileUtil;
import com.java2nb.novel.service.FileService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

/**
 * @author 11797
 */
@Service
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "pic.save", name = "storage", havingValue = "local")
public class LocalFileServiceImpl implements FileService {

    @Override
    public String transFile(String picSrc, String picSavePath){

        return FileUtil.network2Local(picSrc, picSavePath, Constants.LOCAL_PIC_PREFIX);
    }
}
  1. 新建Aliyun OSS文件服务实现类,实现文件服务接口,保存文件到Aliyun OSS,并通过@ConditionalOnProperty注解来控制当配置属性pic.save.storage=OSS时,该实现类会实例化被Spring容器管理。
package com.java2nb.novel.service.impl;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.java2nb.novel.core.config.OssProperties;
import com.java2nb.novel.core.utils.Constants;
import com.java2nb.novel.core.utils.FileUtil;
import com.java2nb.novel.service.FileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.io.File;

/**
 * @author 11797
 */
@Service
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "pic.save", name = "storage", havingValue = "OSS")
@Slf4j
public class OssFileServiceImpl implements FileService {

    private final OssProperties ossProperties;

    @Override
    public String transFile(String picSrc, String picSavePath) {

        File file;
        String filePath = FileUtil.network2Local(picSrc, picSavePath, Constants.LOCAL_PIC_PREFIX);
        if (filePath.contains(Constants.LOCAL_PIC_PREFIX)) {
            file = new File(picSavePath+filePath);
        } else {
            //默认图片不存储
            return filePath;
        }

        filePath = filePath.replaceFirst(picSavePath,"");

        filePath = filePath.startsWith("/") ? filePath.replaceFirst("/","") : filePath;

        OSSClient ossClient = new OSSClient(ossProperties.getEndpoint(), ossProperties.getKeyId(), ossProperties.getKeySecret());
        try {
            //容器不存在,就创建
            if (!ossClient.doesBucketExist(ossProperties.getBucketName())) {
                ossClient.createBucket(ossProperties.getBucketName());
                CreateBucketRequest createBucketRequest = new CreateBucketRequest(ossProperties.getBucketName());
                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
                ossClient.createBucket(createBucketRequest);
            }
            //上传文件
            PutObjectResult result = ossClient.putObject(new PutObjectRequest(ossProperties.getBucketName(), filePath, file));
            //设置权限 这里是公开读
            ossClient.setBucketAcl(ossProperties.getBucketName(), CannedAccessControlList.PublicRead);

            if(result != null) {
                return ossProperties.getWebUrl() + "/" + filePath;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            //关闭
            ossClient.shutdown();
            file.delete();
        }

        return "/images/default.gif";
    }

}
  1. 新建FastDfs文件服务实现类,实现文件服务接口,保存文件到FastDfs,并通过@ConditionalOnProperty注解来控制当配置属性pic.save.storage=fastDfs时,该实现类会实例化被Spring容器管理。
package com.java2nb.novel.service.impl;

import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.java2nb.novel.core.utils.Constants;
import com.java2nb.novel.core.utils.FileUtil;
import com.java2nb.novel.service.FileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;

/**
 * @author 11797
 */
@Service
@RequiredArgsConstructor
@Slf4j
@ConditionalOnProperty(prefix = "pic.save", name = "storage", havingValue = "fastDfs")
public class FastDfsFileServiceImpl implements FileService {

    private final FastFileStorageClient storageClient;

    @Value("${fdfs.webUrl}")
    private String webUrl;

    @Override
    public String transFile(String picSrc, String picSavePath) {

        File file;
        String filePath = FileUtil.network2Local(picSrc, picSavePath, Constants.LOCAL_PIC_PREFIX);
        if (filePath.contains(Constants.LOCAL_PIC_PREFIX)) {
            file = new File(picSavePath + filePath);
        } else {
            //默认图片不存储
            return filePath;
        }

        try {
            FileInputStream inputStream = new FileInputStream(file);
            StorePath storePath = storageClient.uploadFile(inputStream, file.length(),
                    FilenameUtils.getExtension(file.getName()), null);
            //这里额外加上LOCAL_PIC_PREFIX路径,表明该图片是个人资源,而不是爬虫爬取的网络资源,不需要再次进行转换,
            // 实际访问时,再通过nginx的rewite指令来重写路径,去掉LOCAL_PIC_PREFIX
            return webUrl+Constants.LOCAL_PIC_PREFIX+storePath.getFullPath();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            //删除
            file.delete();
        }

        return "/images/default.gif";
    }
}
  1. 新建配置pic.save.storage,用来控制真正被Spring容器管理的实现类。
pic:
  save:
    type: 2 #图片保存方式, 1不保存,使用爬取的网络图片 ,2保存在自己的存储介质
    storage: local #存储介质,local:本地,OSS:阿里云对象存储,fastDfs:分布式文件系统
    path: /var/pic  #图片保存路径
  1. 在需要使用文件服务的类中注入文件服务接口FileService,具体实现类只有在运行期才知道,由配置属性pic.save.storage来指定。
    @Autowire
    private FileService fileService;

    @Override
    public void updateBookPicToLocal(String picUrl, Long bookId) {

        picUrl = fileService.transFile(picUrl, picSavePath);

        bookMapper.update(update(book)
                .set(BookDynamicSqlSupport.picUrl)
                .equalTo(picUrl)
                .set(updateTime)
                .equalTo(new Date())
                .where(id, isEqualTo(bookId))
                .build()
                .render(RenderingStrategies.MYBATIS3));

    }

依赖倒置原则

依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。数据服务层的设计正是遵循了依赖倒置原则,实现了模块间的解耦,切换底层数据存储只需要修改一个配置项即可。

项目仓库地址

https://github.com/201206030/novel-plus

免费评分

参与人数 53吾爱币 +62 热心值 +48 收起 理由
-白衬衫- + 1 + 1 谢谢@Thanks!
空白心 + 1 + 1 我很赞同!
夜惊澜 + 1 + 1 用心讨论,共获提升!
银狐狸 + 1 我很赞同!
aixiaodemj + 1 + 1 热心回复!
599283807 + 1 我很赞同!
Y2K孤星 + 1 + 1 我很赞同!
spilt + 1 + 1 我很赞同!
buryjian + 1 用心讨论,共获提升!
liuye + 3 + 1 我很赞同!
fengbolee + 1 + 1 用心讨论,共获提升!
60520 + 1 + 1 我很赞同!
lihui9519 + 1 + 1 我很赞同!
磨刀客 + 1 谢谢@Thanks!
笛墨萧 + 1 + 1 我很赞同!
我本是一介书生 + 1 + 1 很腻害的样子
萎缩的蛋蛋 + 1 + 1 我很赞同!
ccdajj + 1 日常感谢大佬分享
啦啦啦听不见 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
十五彩虹 + 1 + 1 我很赞同!
这个。可以有 + 1 + 1 谢谢@Thanks!
夜行少女 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
adys + 1 我很赞同!
412 + 1 + 1 我很赞同!支持一下
zhanghao0605 + 1 我很赞同!
ggooosting + 1 + 1 热心回复!
Samaelx + 1 + 1 热心回复!
猫咪半仙 + 1 + 1 谢谢@Thanks!
ecust-xwn + 1 + 1 我很赞同!
silent_grief + 2 + 1 成品跳转过来了。鼓励原创吾爱感谢有你
amengx + 1 + 1 鼓励转贴优秀软件安全工具和文档!
fx_tsee + 1 膜拜大神
baileyxia + 1 谢谢@Thanks!
coolmo + 1 + 1 我很赞同!
Absolate + 1 + 1 用心讨论,共获提升!
枫枫少爷 + 1 + 1 谢谢@Thanks!
seapipi + 1 + 1 东西是好东西,可是懒癌的也只能用那边的成品了
moonlight727 + 1 + 1 热心回复!
arklw + 1 + 1 热心回复!
羽翼 + 1 + 1 感谢大佬 搞个官网啥的 也好打赏
A02 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
continueC + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
可乐味的小土豆 + 1 + 1 谢谢@Thanks!
jiao2402 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhangyusheng + 1 用心讨论,共获提升!
轻小狂 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
萧杀 + 2 + 1 慕名而来。给分~
子晗。 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
taichao + 2 + 1 我很赞同!
tanjj + 1 + 1 谢谢@Thanks!
Supermexyh + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
joneqm + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| Java学者 发表于 2020-12-7 09:40
agi学习者 发表于 2020-12-7 09:35
厉害了,word楼主学者,确实要做一个小说app,所涉及的数据是海量的,爬虫去爬取来的数据需要存储,而且尽 ...

嗯嗯,后面有时间,教程也会慢慢完善的
 楼主| Java学者 发表于 2020-12-13 12:58
shao981109 发表于 2020-12-13 12:22
老哥,这边和你讨论个问题,你那个book的分页问题,你那逻辑生成的book_id有95%以上是偶数,也就是落在奇数 ...

用murmur3的hash算法的话,那得再单独开发个ID生成器的服务,又增加了系统的复杂度,还得考虑单点等问题,不然ID生成器挂了,整个系统就没法用了,很没有必要,而且也没听说有人用这个来做分布式ID的生成算法,雪花算法是目前分布式ID生成算法用的比较多的,经过业界验证过的,偶数多,你可以修改分表的策略呀,为什么会想到修改ID的生成算法呢?分表策略是可以直接在外部配置文件中修改,我这个只是用的其中一种算法而已,每个人都可以根据自己的需求定制自己的分库分表策略
agi学习者 发表于 2020-12-7 09:35
厉害了,word楼主学者,确实要做一个小说app,所涉及的数据是海量的,爬虫去爬取来的数据需要存储,而且尽量不要漏掉,有更新的部分要实现增量存储,对数据服务层要求很高,前端用户阅读部分,与用户的交互也要流畅,必然需要引入缓存集群。
不知楼主大大可否在百忙之中录制一部讲解小说精品屋-plus这个项目开发过程的视频教程?就好像那些培训机构那样,从UML讲起,把整个软件前后端的设计思路、技术选型、数据结构、部署运维、持续集成/持续交付……等过程给您的粉丝们讲讲,我非常想知道项目实现的思路以及代码为什么这么写的原因。
agi学习者 发表于 2020-12-7 09:43
Java学者 发表于 2020-12-7 09:40
嗯嗯,后面有时间,教程也会慢慢完善的

太感谢了~就希望看您搭建环境,讲一下架构设计思路,然后复现一下代码,当然最重要的就是爬虫模块,是咱们项目的核心,是生产力的源泉~
大张呀 发表于 2020-12-7 09:44
感觉好厉害,外行人看热闹。
lzy333 发表于 2020-12-7 09:44
挺厉害的样子,支持支持支持
wlaeni 发表于 2020-12-7 09:50
新手!看着很厉害,就是看不懂
easthq 发表于 2020-12-7 09:56
哇,谢谢楼主的分析分享
清淡如风 发表于 2020-12-7 10:06
厉害了,支持!
qihang5518 发表于 2020-12-7 10:07
谢谢分享学习经验
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 23:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表