笔记666

项目启动

每次项目启动都会花费大量的时间,所谓万事开头难。

构建模块

采用的是微服务的形式加上DDD架构(这个架构还不够熟练后续慢慢学慢慢懂)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 差不多是这种形式,我这个是通过原型代码复制过来的,然后再一个个添加模块,而且我还把里面的各个包的class全部注释掉,就是怕会报错,也不知道报错原因是不是因为这个。

**原型代码报错猜测原因:**原型代码如果直接运行是回报错误的,我搞了很久,可能是因为pom文件加上了很多依赖,但是目前项目初始化阶段还没用到导致的报错。,也可能是上述说的java代码的报错。

重新写!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

用了上述方法后面还会!报错!我受不了! 于是我只能自己手敲了,后续就是自己手敲终于不报错了!!!!!

添加最外层的pom依赖:只要基础配置和springboot启动的依赖以及阿里云maven仓库的的相关依赖:

<groupId>com.jingdianjichi</groupId>   
<artifactId>jc-club-subject</artifactId> 
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<description>鸡翅club-题目领域服务</description>
<modelVersion>4.0.0</modelVersion>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.4.2</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
</dependencies>
<!--    阿里云的maven仓库-->
    <repositories>
        <repository>
            <id>central</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

能够启动springboot项目就说明初步构建完成

starter层还要引入启动的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.4.2</version>
</dependency>
@SpringBootApplication
@ComponentScan("com.cjw")
//@MapperScan("com.jingdianjichi.**.mapper")
public class SubjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(SubjectApplication.class);
    }

}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

应用层SpringMVC

测试Tomcat服务器连接SpringMVC是否能够成功

因为微服务拆分了嘛,所以所有的启动器都放在cjw-club-starter里面,它找不到别的

所以在starter的pom.xml文件里面加上如下代码: controller这个微服务就可以被启动

<dependency>
    <groupId>com.cjw</groupId>
    <artifactId>cjw-club-application-controller</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

去做测试

@RestController
public class SubjectController {
    @GetMapping("/test")
    public  String test(){
        return "cjw";
    }
}

postman 发送外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成功返回 cjw!!!!

数据库的集成

包括mysql,druid,mybatis集成

由于druid第一次见就给了它的作用:

Druid 是一个开源的 Java 数据库连接池和监控库。它被设计用于高并发环境下的数据库访问,提供了高性能、可扩展性和可管理性。主要功能包括:

  1. 连接池管理:Druid 提供了高效的数据库连接池管理,能够有效地管理数据库连接的获取和释放,避免了频繁地创建和销毁连接,提高了系统性能。
  2. 数据库连接监控:Druid 支持实时监控数据库连接的状态,包括活跃连接数、空闲连接数、连接等待时间等指标,有助于及时发现和解决数据库连接问题。
  3. SQL 执行性能监控:Druid 可以监控和统计 SQL 语句的执行性能,包括执行时间、执行次数、慢查询等信息,帮助开发人员优化 SQL 查询性能。
  4. 数据库防火墙:Druid 提供了强大的数据库防火墙功能,能够有效地防御 SQL 注入攻击和其他恶意行为。
  5. 数据源配置灵活:Druid 的数据源配置非常灵活,支持各种常见的数据库连接配置,同时也提供了丰富的配置选项,能够满足不同项目的需求。

总的来说,Druid 在 Java 开发中扮演着数据库连接池和监控的重要角色,能够提升系统的稳定性、性能和安全性。

基础设施相关的东西放在cjw-club-infra里面

1.1 在pom.xml文件中添加连接数据库相关的依赖

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

1.2 添加druid连接池相关依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.21</version>
</dependency>

功能强大很好用 分析慢sql 防火墙 安全性啥的

1.3 mysql的相关驱动

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version>
</dependency>

1.4mybatis-plus依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
</dependency>

2.用easycode插件自动生成一个表的增删改查

牛逼真的牛逼啊

不知道为啥page相关的会报错,先把它删了就可以

3.在starter服务中添加依赖,才能启动到这个

<dependency>
    <groupId>com.cjw</groupId>
    <artifactId>jc-club-infra</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

4.在启动类上加上注解@MapperScan()

要用到Dao/mapper类就要扫描到对应的mapper/dao,加上就能把要的东西加载进来

@MapperScan("com.cjw.**.mapper")

这里的 .* * 是会跳过所有的包(一个*是跳过一个包)

5.在starter的resources配置相关的application.yml的数据库信息

server:
  port: 8080
spring:
  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/club?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 20
      min-idle: 20
      connectionProperties: config.decrypt=true;config.decrypt.key=${publicKey};
      max-active: 100
      max-wait: 60000
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: admin
        login-password: 123456
      filter:
        stat:
          enabled: true
          slow-sql-millis: 2000
          log-slow-sql: true
        wall:
          enabled: true
        config:
          enabled: true
#publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMJzo9TiSuOGAMR2Zma25lWdtR1oxq6RcZYnWE9vcYLNKxUOkBlvSfMrbS25KtlJi+hIzikfCoyTDB0VI5gB3Q8CAwEAAQ==
#logging:
#  config: classpath:log4j2-spring.xml

drud连接池的信息很多(慢慢学)

6.接下来就是要去controller层做测试

因为controller层需要用到数据库相关的基础设施 所以和starter一样也引!

<dependency>
    <groupId>com.cjw</groupId>
    <artifactId>jc-club-infra</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
@RestController
public class SubjectController {
    @Resource
    private SubjectCategoryService subjectCategoryService;
    @GetMapping("/test")
    public  String test(){
        SubjectCategory subjectCategory = subjectCategoryService.queryById(1L);
        return subjectCategory.getCategoryName();
    }
}

@Resource 注解通常用于注入外部资源,如数据库连接、消息队列等。 (和Autowired作用一样)

通过postman成功返回结果!!!!

数据库加密问题

Druid有提供数据库加密的功能

在infra的basic包下新建一个utils包用来做数据库的加密

(具体加密解密什么的还没搞懂现在不想去理解它等到时候有用的时候再去学习叭叭叭)

代码接口开发!

一·在common层添加一些依赖

1.lombok依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>

Lombok 是一个 Java 库,可以通过注解的方式来简化 Java 代码的编写。它提供了许多注解,用于自动生成常用的 Java 代码,比如 @Getter@Setter 注解可以自动生成属性的 Getter 和 Setter 方法,@AllArgsConstructor@NoArgsConstructor 注解可以自动生成全参和无参构造方法等。通过使用 Lombok,可以显著减少重复的样板代码,提高开发效率。

2.mapstruct依赖

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.4.2.Final</version>
</dependency>

注意mapstruct依赖要写在lombok下面,因为需要lombok写好的代码来进行映射

MapStruct 是一个 Java 实体映射工具,它可以帮助开发人员在不同的 Java 对象之间进行映射转换。通过使用 MapStruct,开发人员可以定义映射规则,然后自动生成映射代码,避免手动编写大量的映射逻辑。MapStruct 支持在编译时生成高效的映射代码,因此映射过程的性能较高。

二. 有了lombok就可以在infra层的entity类把get set 删除掉了(这些代码是easycode插件自动生成的)

1.在infra层添加common依赖

<dependency>
    <groupId>com.cjw</groupId>
    <artifactId>cjw-club-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

2.添加相关依赖

@Data

三.在domain层写东西了

1.还是要引入infra的相关依赖

<dependency>
    <groupId>com.cjw</groupId>
    <artifactId>jc-club-infra</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {
       @Resource
    private SubjectCategoryService subjectCategoryService;
    
}

2.复制infra的entity到domian改名为BO

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为只关心业务,就把这些属性删除

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.新建一个转换器(用到mapstruct)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加的是mapstruct的mapper注解不要搞错了

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

@Mapper
public interface SubjectCategoryConverter {

    SubjectCategoryConverter INSTANCE = Mappers.getMapper(SubjectCategoryConverter.class);

    SubjectCategory convertBoToCategory(SubjectCategoryBO subjectCategoryBO);

    List<SubjectCategoryBO> convertBoToCategory(List<SubjectCategory> categoryList);

}

4.service层完整写法

public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {
    @Resource
    private SubjectCategoryService subjectCategoryService;


    @Override
    public void add(SubjectCategoryBO subjectCategoryBO) {
//        if (log.isInfoEnabled()) {
//            log.info("SubjectCategoryController.add.bo:{}", JSON.toJSONString(subjectCategoryBO));
//        }
        //BO转化为我们想要的category
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE
                .convertBoToCategory(subjectCategoryBO);
    //    subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        //插入信息
        subjectCategoryService.insert(subjectCategory);
    }
}

四.到common层写一些需要的东西

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(直接复制代码了)

五.到controller层 -> 和domain层作交互

1.一样引domain的依赖

<dependency>
    <groupId>com.cjw</groupId>
    <artifactId>jc-club-domain</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

2.一样复制一些需要的代码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码

@RestController
@RequestMapping("/subject/category")
@Slf4j
public class SubjectCategoryController {

    @Resource
    private SubjectCategoryDomainService subjectCategoryDomainService;

    /**
     * 新增分类
     */
    @PostMapping("/add")
    public Result<Boolean> add(@RequestBody SubjectCategoryDTO subjectCategoryDTO) {
        try {
//            if (log.isInfoEnabled()) {
//                log.info("SubjectCategoryController.add.dto:{}", JSON.toJSONString(subjectCategoryDTO));
//            }
//            Preconditions.checkNotNull(subjectCategoryDTO.getCategoryType(), "分类类型不能为空");
//            Preconditions.checkArgument(!StringUtils.isBlank(subjectCategoryDTO.getCategoryName()), "分类名称不能为空");
//            Preconditions.checkNotNull(subjectCategoryDTO.getParentId(), "分类父级id不能为空");
            SubjectCategoryBO subjectCategoryBO = SubjectCategoryDTOConverter.INSTANCE.convertDtoToCategoryBO(subjectCategoryDTO);
            subjectCategoryDomainService.add(subjectCategoryBO);
            return Result.ok(true);
        } catch (Exception e) {
            log.error("SubjectCategoryController.add.error:{}", e.getMessage(), e);
            return Result.fail("新增分类失败");
        }
    }

总结:转换器的知识还不知道什么意思后续去学

日志log4j2集成

1.common模块添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>2.4.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

2.然后在那三个模块下面添加log4j

3.starter的resources下添加相关注解.xml

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.application.yml 加上这个

logging:
  config: classpath:log4j2-spring.xml

precondions的参数校验

common层添加依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>
public class SubjectCategoryController {

    @Resource
    private SubjectCategoryDomainService subjectCategoryDomainService;

    /**
     * 新增分类
     */
    @PostMapping("/add")
    public Result<Boolean> add(@RequestBody SubjectCategoryDTO subjectCategoryDTO) {
        try {
            if (log.isInfoEnabled()) {
                log.info("SubjectCategoryController.add.dto:{}", JSON.toJSONString(subjectCategoryDTO));
            }
            Preconditions.checkNotNull(subjectCategoryDTO.getCategoryType(), "分类类型不能为空");
            Preconditions.checkArgument(!StringUtils.isBlank(subjectCategoryDTO.getCategoryName()), "分类名称不能为空");
            Preconditions.checkNotNull(subjectCategoryDTO.getParentId(), "分类父级id不能为空");
            SubjectCategoryBO subjectCategoryBO = SubjectCategoryDTOConverter.INSTANCE.convertDtoToCategoryBO(subjectCategoryDTO);
            subjectCategoryDomainService.add(subjectCategoryBO);
            return Result.ok(true);
        } catch (Exception e) {
            log.error("SubjectCategoryController.add.error:{}", e.getMessage(), e);
            return Result.fail("新增分类失败");
        }
    }

添加校验信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接口开发

后端开发人员还要去开发接口文档,这里粗略地看了一下。

查询分类大类

业务大概就是Controller(xxDTO/xxDTOList) --> DomainService(xxBO/xxBOList) --> Service(xx xx) --> Dao(xx List)

难啊难!!!

标签类的CRUD

没什么好讲的

这里鸡哥细讲了subject_mapping 这个表的作用:

得到一个分类的id,就可以查看这个分类下有哪些题目和标签的id,然后通过id去题目表和标签表查相应的数据

题目类型的开发

一.easycode导入多个表

二.写新增题目的controller

三.写新增题目的domain(重点好好听一下)

用到设计模式 工厂+策略

根据题目的类型去调用相应的Dao层

新建一个handler

分页的开发

common新增了pageinfo分页的信息 和pageresult 分页后的结果。

就是请求参数需要标签的id和分类的id 可以直接在subjectinfdto增加相应的字段。

Sql拦截器自动翻译

为了日志好看清楚执行的sql语句

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在数据库层添加这三个类

cjw-club-oss的开发

专门放在一个模块下面,方便文件访问各个模块,随时切换什么阿里云,京东云什么的。

一.需要启动依赖

    <!--        启动依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.4.2</version>
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-logging</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.2</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

AUTHEN模块(用户权限)

项目初始化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一样要注意maven的配置,自己练习了一遍,感觉对项目的初始配置有了一定的理解。

Satoken案例:

1.maven引入相关依赖

2.starter配置application.yml

3.编写测试用例:

@RestController
@RequestMapping("/user/")
public class UserController {

    // 测试登录,浏览器访问: http://localhost:8081/user/doLogin?username=zhang&password=123456
    @RequestMapping("doLogin")
    public String doLogin(String username, String password) {
        // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
        if("zhang".equals(username) && "123456".equals(password)) {
            StpUtil.login(10001);
            return "登录成功";
        }
        return "登录失败";
    }

    // 查询登录状态,浏览器访问: http://localhost:8081/user/isLogin
    @RequestMapping("isLogin")
    public String isLogin() {
        return "当前会话是否登录:" + StpUtil.isLogin();
    }

}

用户模块开发

1.easycode生成基础代码块

2.从subejct模块copy要使用到的包

角色模块开发

1.easycode生成基础代码块

Module选择infra层 包选择到basic 第一第二行的第一个不要勾选。

删除一些不用的爆红的地方。

2.新建RoleController

copy模板编写一些基础内容

3.copy DTO和convert包里面的东西,修改一下加上@data注解。

4.domain层:

copy模板编写

Gateway模块(路由和鉴权)

路由

1.引入相关依赖,新增的依赖

<!--        网关配置-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
<!--        负载均衡相关配置-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

记得gateway相关依赖不能和web依赖共存,要删除web的相关依赖

2.配置配置文件

gateway相关配置文件

server:
  port: 5000
spring:
  cloud:
    gateway:
      routes:
        - id: oss
#          lb是loadbalance负载均衡的意思,cjw-club-oss就是nacos注册服务上面的名称
          uri: lb://cjw-club-oss
#          断言(触发路由的规则):只要前端请求的地址是带oss的地址,就转发到
          predicates:
            - Path=/oss/**
#          正常前端需要的规则:oss/cjw-club-oss/... 加上filters之后:oss/...即可 隐藏了路径
          filters:
            - StripPrefix=1
        - id: auth
          uri: lb://cjw-club-auth
          predicates:
            - Path=/auth/**
          filters:
            - StripPrefix=1
        - id: subject
          uri: lb://cjw-club-subject
          predicates:
            - Path=/subject/**
          filters:
            - StripPrefix=1

3.测试网关是否可用

测试网址:http://localhost:5000/authen/user/doLogin?username=zhang&password=123456

鉴权(gateway网关基于redis实现分布式会话)

1.gateway引入redis相关依赖(Sa-token) auth层也要引入这个依赖(注意auth不需要reactor这个依赖)

<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-reactor-spring-boot-starter</artifactId>
    <version>1.38.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis-jackson</artifactId>
    <version>1.38.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

2.配置相关的配置文件

redis:
  # Redis数据库索引(默认为0)
  database: 1
  # Redis服务器地址
  host: 192.168.133.3
  # Redis服务器连接端口
  port: 6379
  # Redis服务器连接密码(默认为空)
  password: 123456
  # 连接超时时间
  timeout: 2s
  lettuce:
    pool:
      # 连接池最大连接数
      max-active: 200
      # 连接池最大阻塞等待时间(使用负值表示没有限制)
      max-wait: -1ms
      # 连接池中的最大空闲连接
      max-idle: 10
      # 连接池中的最小空闲连接
      min-idle: 0

3.编写测试代码 新建authen包,新建SaTokenConfigure配置类,里面配置了权限认证的配置器,用于处理权限,跳转到StpInterfaceimpl找对应的方法去执行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

public class SaTokenConfigure {
    // 注册 Sa-Token全局过滤器 
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截地址
                .addInclude("/**")
                // 鉴权方法:每次访问进入
                .setAuth(obj -> {
                    System.out.println("-------- 前端访问path:" + SaHolder.getRequest().getRequestPath());
                    // 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
                    SaRouter.match("/auth/**", "/auth/user/doLogin", r -> StpUtil.checkRole("admin"));
                    SaRouter.match("/oss/**", r -> StpUtil.checkLogin());
                    SaRouter.match("/subject/subject/add", r -> StpUtil.checkPermission("subject:add"));
                    SaRouter.match("/subject/**", r -> StpUtil.checkLogin());
                });
                
    }
}

4.新建redis包,新建redisconfig配置类,里面写入redis序列化的方式,redis默认的序列化方式是JDK的序列化方式,出来的是乱码,需要用redisTemplate替换成json的序列化方式

5.新建redisutil类(里面就用到步骤4里面的序列化方式),用于编写对应的方法去使用(放入IOC容器中)然后StpInterfaceimpl就会用到redisutils类中的方法去获取redis缓存的信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

gateway全局异常处理

使用spring框架提供的ErrorWebExceptionHandler,程序发生异常就会执行这段代码(放在ioc容器中)。

@Component
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Mono<Void> handle(ServerWebExchange serverWebExchange, Throwable throwable) {
        ServerHttpRequest request = serverWebExchange.getRequest();
        ServerHttpResponse response = serverWebExchange.getResponse();
        Integer code = 200;
        String message = "";
        if (throwable instanceof SaTokenException) {
            code = 401;
            message = "用户无权限";
        } else {
            code = 500;
            message = "系统繁忙";
        }
        Result result = Result.fail(code, message);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            byte[] bytes = null;
            try {
                bytes = objectMapper.writeValueAsBytes(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return dataBufferFactory.wrap(bytes);
        }));
    }

}

鸡翅CLUB二期开发!!!

futuretask多线程优化分类查询

一下子就跳到二期了,其实也没有关系,我只要把这块的核心业务整理清楚,以及如何去操作去实现的处理好就可以,

一.核心业务逻辑

首先我们这块的前端展示效果如下图所示,我们要将根据一个个分类根据数据库一个个查询每个分类下的标签集合,这样的效率显然太慢了,于是我有个想法就是可以用到java多线程并行处理事务的能力,进行并发编程,由于是只读的情况,所以不需要考虑线程安全的问题,我就采用了线程池的这种处理模式,这种处理模式可以不需要频繁创建和销毁bean,对性能友好一些,而且相对好编码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二.开发流程第一种方式

1.建线程池(配置类)
@Configuration
public class ThreadPoolConfig {

    @Bean(name = "labelThreadPool")
    public ThreadPoolExecutor getLabelThreadPool() {
        return new ThreadPoolExecutor(
                20,
                100,
                5,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(40),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

}

参数依次是 1.核心线程参数 20 2.最大线程参数 100 3.线程的空闲时间 5s 4.空闲时间单位 5.队列的长度 40 6.队列的线程工厂

7.拒绝策略 差不多有4种拒绝策略 如何选择??

CallerRunsPolicy: 线程池不足以承担的时候

根据上图 由于这个根据第二大类查询标签大概有9个 所以核心线程数设置为20完全够用

2.建集合存放返回值
List<FutureTask<Map<Long,List<SubjectLabelBO>>>> futureTaskList =new LinkedList<>();

这个集合比较复杂需要我们一步步去拆解:

1.大体上是一个LinkedList对吧,它的参数最外层是FutureTask<>这个泛型类,是一个返回值。

2.而返回值 返回给我的是什么呢,也就是最底层就是标签集合嘛就是List这个,但是每个标签集合集合都对应一个分类id,所以用Map<Long,List>来封装,把这个Map集合放到FutureTask<>里面,再把FutureTask<>放到List<>里面就是我们最终的返回值吧。

3.编写线程池调用
//线程池并发调用
Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
//参数是根据大分类查询到的子分类集合中的每一个
categoryBOList.forEach(categoryBO -> {
    //新建任务并把查询到的子分类标签放这里(map集合)
    FutureTask<Map<Long, List<SubjectLabelBO>>> futureTask = new FutureTask<>(() ->
            getSubjectLabelBOList(categoryBO));
    futureTaskList.add(futureTask);
    labelThreadPool.submit(futureTask);
});

循环上面查到的子分类,每一个子分类去数据库查标签集合的这个动作都用futuretask装起来,最后submit一个个任务上去,让线程池并发得去执行这个查询标签的动作

//调用完之后从futureTaskList拿到返回结果
for (FutureTask<Map<Long, List<SubjectLabelBO>>> futureTask : futureTaskList) {
    Map<Long, List<SubjectLabelBO>> resultMap = futureTask.get();
    if (CollectionUtils.isEmpty(resultMap)){
        continue;
    }
    map.putAll(resultMap);
}
categoryBOList.forEach(categoryBO->{
    categoryBO.setLabelBOList(map.get(categoryBO.getId()));
});

后续就是去运行完后的FutureTaskList里面一个个去取FutureTask,去get(),得到map集合的标签(键是子分类id),然后子分类集合遍历.

三.开发流程第二种方式

吧。

3.编写线程池调用
//线程池并发调用
Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
//参数是根据大分类查询到的子分类集合中的每一个
categoryBOList.forEach(categoryBO -> {
    //新建任务并把查询到的子分类标签放这里(map集合)
    FutureTask<Map<Long, List<SubjectLabelBO>>> futureTask = new FutureTask<>(() ->
            getSubjectLabelBOList(categoryBO));
    futureTaskList.add(futureTask);
    labelThreadPool.submit(futureTask);
});

循环上面查到的子分类,每一个子分类去数据库查标签集合的这个动作都用futuretask装起来,最后submit一个个任务上去,让线程池并发得去执行这个查询标签的动作

//调用完之后从futureTaskList拿到返回结果
for (FutureTask<Map<Long, List<SubjectLabelBO>>> futureTask : futureTaskList) {
    Map<Long, List<SubjectLabelBO>> resultMap = futureTask.get();
    if (CollectionUtils.isEmpty(resultMap)){
        continue;
    }
    map.putAll(resultMap);
}
categoryBOList.forEach(categoryBO->{
    categoryBO.setLabelBOList(map.get(categoryBO.getId()));
});

后续就是去运行完后的FutureTaskList里面一个个去取FutureTask,去get(),得到map集合的标签(键是子分类id),然后子分类集合遍历.

三.开发流程第二种方式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值