Skip to content

Latest commit

 

History

History
602 lines (443 loc) · 18.6 KB

javasea-volcano.md

File metadata and controls

602 lines (443 loc) · 18.6 KB

Volcano

License

概述

Volcano是一个基于springboot后台开发简易脚手架。能实现当作一个单独项目或者作为微服务的一个节点进行快速开发和部署。

项目结构

javasea-volcano 父项目, 用于springcloud和springboot的版本管控, maven插件, 仓库统一管控等.

|--javasea-volcano-base: 测试类和相关业务配置, 依赖mgr项目中的组件,开发的时候在该项目的基础上添 加添加业务代码即可。

|--javasea-volcano-mgr:常用组件和工具类

项目环境

中间件 版本 备注
JDK 1.8+ JDK1.8及以上
MySQL 5.6+ 5.6及以上
Redis 3.2+

基本功能

参考包com.zhirui.lmwy.wms.demo下的配置

参数校验

JSR校验

spring-boot-starter-web已经默认集成了JSR303校验,只需要直接使用注解校验即可。

参考测试类: com.zhirui.lmwy.wms.demo.web.controller.TestCheckParamController

手动校验

Assert类定义了不满足条件后快速断言的方式,可以在校验参数中使用。

还可以采用Spring的Assert进行校验

//第一个参数为false则抛出IllegalArgumentException异常
Assert.isTrue(concurrentConsumers > 0, "'concurrentConsumers' value must be at least 1 (one)");
Assert.isTrue(!this.exclusive || concurrentConsumers == 1,"When the consumer is exclusive, the concurrency must be 1");

也可以采用Optional进行校验

ZOrder order = this.getOrderByOrderNum(orderNum);
Optional.ofNullable(order).filter(o -> {
    return null != o && (0 == o.getStatus() || 3 == o.getStatus() || 9 == o.getStatus());
}).orElseThrow(() -> new ParamException("获取数据异常,订单号有误或者订单状态异常!"));

还可以通过guava的Preconditions类来进行参数检查。有需要请自行百度。

参数转换

参考测试类:TestDateConverterAndJson

URL方式传值到后端转换

URL传参到后端包括 如下三种方式传参:

@GetMapping("testDateConverter")
public Student testDateConverter(@RequestParam Student student){...}

@GetMapping("testDateConverter")
public Student testDateConverter(Student student){...}

@GetMapping("testDateConverter/{id}")
public Student testDateConverter(@PathVariable Integer id){...}

com.zhirui.lmwy.mgr.converter包下定义了很多转换类:

  • StringToDateConverter

    如果实体属性是Date,通过该转换器将String转Date类型。效果和日期属性上的注解@DatetimeFormat相同。但是启用该转换器的时候,实体属性上的注解@DatetimeFormat不再生效。

  • StringToDoubleConverter

    如果实体属性是Double,通过该转换器将String转Double类型。

  • StringToIntegerConverter

    如果实体属性是Integer,通过该转换器将String转Integer类型。

请求体方式传值到后端转换

请求体方式传参到后端 包括如下方式:

@PostMapping("testDateConverter2")
public Student testDateConverter2(@RequestBody Student student){...}

com.zhirui.lmwy.mgr.json.jackson包下定义了JSON的序列化和反序列化方式:

  • deserializer 包下的序列化类

    在@RequestBody接参时候,会调用该包中的序列化类将JSON转换成实体接参。

  • serializer 包下的序列化类

    用于后端传值给前端。

后端传值给前端

com.zhirui.lmwy.mgr.json.jackson.serializer包下的定义了序列化类 。

在Controller类上添加注解@ResponseBody或者@RestController,那么后端传值给前端是JSON方式。

如果是返回值是对象或者集合,会用序列化类进行参数类型转换。

如下列子中,Student中的两个属性日期会转换成serializer 定义的格式“yyyy-MM-dd HH:mm:ss”。

序列化类``JacksonDateSerializer的作用相当于在属性上添加了@JsonFormat(pattern="yyyy-MM-dd"),实现将Date类型转换成String,但是添加JacksonDateSerializer@JsonFormat(pattern="yyyy-MM-dd")失效。

    @PostMapping("testDateConverter2")
    public Student testDateConverter2(@RequestBody Student student){
        System.out.println(student);
        Student s = new Student();
        s.setBirth(new Date());
        s.setCreateTime(LocalDateTime.now());
        return s;
    }

异常处理

com.zhirui.lmwy.mgr.exception.impl中定义了三大类异常:

BusinessException: 通用业务异常
ParamException:参数校验异常
AuthenticationException:认证失败异常

Exceptions类定义了快捷抛出异常的一些通用方法,在需要抛出异常时请不要去throw new XXXException(),而是用Exceptions类的方法进行操作。

已经定义了全局异常处理器GlobalExceptionHandler对各种异常可以进行处理,请不要在controller和service中try..catch

返回值处理

controller的返回前端使用ResultModel类进行封装,里面有codemsgdata等字段。

各种场景下的ResultModel返回:

    //插入后返回方式, msg:插入成功;插入失败
    public ResultModel resultInsert(){
        boolean flag = false;
        return ResultModel.resultInsert(flag);
    }

    //更新后返回方式, msg:更新成功;更新失败
    public ResultModel resultUpdate(){
        boolean flag = false;
        return ResultModel.resultUpdate(flag);
    }

    //删除后返回方式, msg:删除成功;删除失败
    public ResultModel resultDelete(){
        boolean flag = false;
        return ResultModel.resultDelete(flag);
    }

    //删除后返回方式, msg:操作成功;操作失败
    public ResultModel result(){
        boolean flag = false;
        return ResultModel.result(flag);
    }

    //认证失败返回方式, msg:认证信息异常
    public ResultModel errorTokenMsg(){
        boolean flag = false;
        //如果msg参数为null,那么是默认的msg:认证信息异常
        return ResultModel.errorTokenMsg(null);
    }

推荐优先上面的放回方式,如果不能满足,还可以使用如下通用的失败成功的返回方式:

ResultModel.error();  //失败返回方式
ResultModel.ok();       //成功返回方式

AOP 日志输出

  1. 添加坐标
<!-- AOP -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- console彩色日志 -->
<dependency>
    <groupId>org.fusesource.jansi</groupId>
    <artifactId>jansi</artifactId>
    <version>1.18</version>
</dependency>
  1. 定义AOP类实现彩色日志输出

参考:com.zhirui.lmwy.mgr.aop.LogAop

集成 丝袜哥

在common项目中集成了swagger

  1. pom中添加注解
        <!-- swagger start -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!-- swagger end -->
  1. 添加配置

    两个配置类:

    com.zhirui.lmwy.mgr.swagger.SwaggerConfiguration

    com.zhirui.lmwy.mgr.swagger.SwaggerProperties

  2. 设置自定义显示参数

    在base项目的application.yaml中添加如下配置进行设置:

swagger:
  #  open: true              #是否开启swagger,在生产环境下需要关闭
  protocol: http          #协议http或https
  base-package: com.zhirui.lmwy.wms   #一定要写对会在这个路径下扫描controller定义
  title: volcano-base项目
  version: 1.0
  description: volcano-base项目丝袜哥测试
  1. 在controller和model中使用swagger

    controller类中使用参考:com.zhirui.lmwy.wms.demo.web.controller.TestCurdController

    model类中使用参考:com.zhirui.lmwy.wms.demo.web.entity.User

  2. 通过swagger进行http请求

    • http://localhost:8080/swagger-ui.html#/

      丝袜哥默认的访问方式

    • http://localhost:8080/docs

      通过controller 重定向后的访问方式

      swagger在2.9.2可能会报错如下:

      Illegal DefaultValue null for parameter type integer

      根据上面这句报错信息,点进去AbstractSerializableParameter.java:412可以看到

      if(BaseIntegerProperty.TYPE.equals(type)){
          return Long.valueOf(example);
      }

      就是说如果实体属性类型是Integer,就把example转为Long类型,而example默认为"",导致转换错误。

      解决办法

      方法一: 实体类中,Integer类型的属性加@ApiModelProperty时,必须要给example参数赋值,且值必须为数字类型。

      `@ApiModelProperty(value = ``"试卷ID"``,example = ``"1"``)``private` `int` `pageId;`

      方法二:

      将版本改成2.8.0

      `<!--swagger依赖-->` `<dependency>` `    ``<groupId>io.springfox</groupId>` `    ``<artifactId>springfox-swagger2</artifactId>` `    ``<version>2.9.2</version>` `  ``<exclusions>` `      ``<exclusion>` `        ``<groupId>io.swagger</groupId>` `        ``<artifactId>swagger-annotations</artifactId>` `      ``</exclusion>` `      ``<exclusion>` `        ``<groupId>io.swagger</groupId>` `        ``<artifactId>swagger-models</artifactId>` `      ``</exclusion>` `  ``</exclusions>` `</dependency>` `<!--解决进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->` `<dependency>` `  ``<groupId>io.swagger</groupId>` `  ``<artifactId>swagger-annotations</artifactId>` `  ``<version>1.5.21</version>` `</dependency>` `<dependency>` `  ``<groupId>io.swagger</groupId>` `  ``<artifactId>swagger-models</artifactId>` `  ``<version>1.5.21</version>` `</dependency>`
      

集成 mybatis-plus(下文中称为MP)

ORM框架使用mybatis-plus,简便了CURD操作

  1. 添加pom

            <!-- mybatis-plus -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.2.0</version>
            </dependency>
            <!-- 代码生成器  start -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.2.0</version>
            </dependency>
            <!-- 代码生成器需要的引擎模板 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.0</version>
            </dependency>
            <!-- 代码生成器  end -->
  2. 在yml中进行配置

    mybatis-plus:
      mapper-locations: classpath:mapper/**/*.xml
      global-config:
        db-config:
          id-type: AUTO  #主键自增长
  3. 添加配置类配置

    下列代码中使用了租户模式,如果只是单纯需要添加分页插件,只需要如下方式即可:

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        return paginationInterceptor;
    }
    /** 分页插件: 实现物理分页 */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        ArrayList<ISqlParser> iSqlParsers = new ArrayList<>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId(boolean where) {
                return new StringValue(tenantId);
            }
    
            @Override
            public String getTenantIdColumn() {
                //指定表中的租户列
                return "tenant_id";
            }
            @Override
            public boolean doTableFilter(String tableName) {
                return false;
            }
        });
        iSqlParsers.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(iSqlParsers);
        return paginationInterceptor;
    }

通过MP进行CRUD操作

参考测试类和官网com.zhirui.lmwy.wms.demo.web.controller.TestCurdController

代码生成器

采用了MP的代码生成器,实现了两个代码生成器。

javasea-volcano-base项目的src/test/java目录的 com.zhirui.lmwy.wms包下,按照注释修改为自己需要的配置运行即可。看个人习惯,推荐用PrimaryCodeGenerator

  • SencondCodeGenerator

    通过数据库表生成基本的entity,mapper,controller和service类等基本类。

  • PrimaryCodeGenerator

    SencondCodeGenerator 功能的基础上,controller,entity中生成swagger的注解。controller、service生成常用的crud方法。

集成redis

  1. 添加pom坐标
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. yml中进行配置
spring:
    redis:
      host: 127.0.0.1
      port: 6379
      password: zhirui888
      timeout: 2000
      database: 6
      lettuce:
        pool:
          max-active: 8
          max-wait: 1
          max-idle: 8
          min-idle: 0
  1. 配置类中进行配置

    • 定义redis的RedisTemplate

      详见:com.zhirui.lmwy.mgr.redis.RedisTemplateConfig

    • 开启springcache

      详见:com.zhirui.lmwy.mgr.redis.RedisCacheConfig

    • redis工具类

      详见:com.zhirui.lmwy.mgr.redis.RedisUtils

集成 JWT

添加jwt坐标

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
    <scope>compile</scope>
</dependency>

yml中配置jwt

custom:
    jwt:
      header: token
      secret: 666666          #密码,用于生成签名
      issuer: volcano         #签发人
      subject: volcano-jwt    #主题
      audience: web           #签发的目标
      expire-minutes: 20      #过期时间
    interceptor:
      jwt:
        exclude:
          path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/login,/verificationCode,/doc/**,/error/**,/docs,/test/**
      permission:
        exclude:
          path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/adminLogin,/sysLogin,/login.html,/verificationCode,/doc/**,/error/**,/docs
      token-timeout:
        exclude:
          path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/docs

添加登陆接口

登陆后会将token信息保存到redis

详见登陆controller:com.zhirui.lmwy.wms.security.controller.LoginController

添加jwt拦截器

拦截到请求后会进行校验,校验方式如下:

// 验证token是否有效
Jws<Claims> jws = JwtUtil.verify(token);

token未过期且合法的才能校验通过,否则抛出401异常。

详见jwt拦截器:com.zhirui.lmwy.wms.security.interceptor.JwtInterceptor

在swagger进行token测试

  1. 执行登陆操作,token见返回值中

    1568710656833

    返回值为:

    {
      "code": 200,
      "msg": "操作成功",
      "data": {
        "loginSysUser": {
          "id": "1",
          "userName": "admin"
        },
        "token": "eyJjdHkiOiJjdHkiLCJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2b2xjYW5vLWp3dCIsImF1ZCI6IndlYiIsImlzcyI6InZvbGNhbm8iLCJleHAiOjE1Njg3MTI0MDcsImlhdCI6MTU2ODcxMDYwNywianRpIjoiNDM4NjMwMDAwOTlmNDFkNDk1Y2FlOWVkNWUzNDZhOTgifQ.wVZOx-J2knqUxdnRjRXZqr2nf1S-Qwmap2-0nGXJDXM"
      },
      "time": "2019-09-17 16:56:51"
    }
  2. 点击 Authorize按钮

    1568710795850

  3. 将token设置到value中,后面丝袜哥所有的请求都会带一个叫做“Authorization”的请求头。

    在jwt拦截器JwtInterceptor中会获取该请求头进行校验。

    1568710877266

项目启动和部署

单独作为项目使用

javasea-volcano默认是一个独立的springboot项目,可以直接启动javasea-volcano-base项目,在base项目的基础上,直接编写业务代码即可。

集成到现有的springcloud中

默认是独立项目,但是也可以轻松的集成到现有的springcloud环境中,只需要放开部分注释即可。

  1. 启用bootstrap.yml的配置(放开注释)

    #spring:
    #  cloud:
    #    config:
    #      uri: ${WMS_CONFIG_SERVER_URL}
    #      name: zhirui-lmwy2-wms${WMS_DEVELOPER_NAME:}
    #      profile: ${config.profile:dev}
    #
  2. base项目的pom中开启对应eureka client的坐标,启动类WmsApplication通过注解@EnableDiscoveryClient启用eureka client。

    <!-- springboot 1.X -->
    <!--<dependency>-->
       <!--<groupId>org.springframework.cloud</groupId>-->
       <!--<artifactId>spring-cloud-starter-eureka</artifactId>-->
    <!--</dependency>-->
    <!-- springboot 2.X -->
    <!--<dependency>-->
       <!--<groupId>org.springframework.cloud</groupId>-->
       <!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
    <!--</dependency>-->
  3. IDE中配置vm参数,使用已经已经存在的eureka注册中心配置中心

    在IDE中配置VM参数只能是作为测试,如果需要部署后生效,那么需要配置到服务器的环境变量或者在启动的java -jar后添加配置。之后我会添加关于服务部署的文章。

    -Dmultipart-location=D:/wms/temp
    -DDiskLocation=D:/wms/
    -DWMS_CONFIG_SERVER_URL=http://192.168.1.230:8861
    -DWMS_EUREKA_SERVER_URL=http://192.168.1.230:8090/eureka/
    -DWMS_DEVELOPER_NAME=-longxiaonan
    -Dconfig.profile=dev

Maven方式打包

mvn clean package -Dmaven.test.skip=true