003-maven開發Java腳手架archrtype-技術點說明

1、概述java

2、技術點說明

2.一、項目結構

凡auto包或文件件,均會被代碼生成器再次生成二修改mysql

一、model層  git

  

 

 

  po:BasePO基礎類,統一了數據庫的基礎字段【數據庫必須添加以下,與mybatis-plus生成器匹配】github

    strategy.setSuperEntityColumns("id","delflag","status","remark","created_datetime","updated_datetime");web

  po:auto數據庫表生成的原始類 【不容許修改】,如需擴展能夠繼承、複合擴展正則表達式

二、dao層spring

  

 

 

  mapper:auto數據庫映射代碼生成器自動生成,不容許修改,如需擴展需自定義在auto外,如 AccountBalanceExtMappersql

  同時修改xml,注意xml內部配置正確的 namespace ,: <mapper namespace="com.aaa.test.mapper.AccountBalanceExtMapper">數據庫

    

 

 

三、service層json

  

 

 

  auto自動生成文件【不容許修改】

  base:IBaseService、BaseServiceImpl 【基礎項目不用繼承,使用IService便可】

  自定義實現須要在auto同級編寫便可。

四、web層

  

 

 

   主要在controller包下,auto自動生成mapper,更具代碼生成器生成,注意權限控制。base 基礎,如後續,從新代碼生成可不生成web層代碼。

  其餘包主要是統一輸出使用。參看下面統一輸出。

2.二、springboot與mybatisplus整合

  【代碼生成器、分頁、邏輯刪除】參看 代碼

  代碼生成:test-demo-generator

    

一、重寫 基於繼承BaseController的模板。框架已提供。

二、代碼生成器文件MybatisPlusGenerator

package com.aaa.test.generator;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;

import java.util.ArrayList;
import java.util.List;

public class MybatisPlusGenerator {

    // region 待修改配置
    
    // 數據庫信息 jdbc:mysql://localhost:3358/test?useUnicode=true&useSSL=false&characterEncoding=utf8
    private static final String DATABASE_DRIVER_NAME = "com.mysql.cj.jdbc.Driver"; //com.mysql.jdbc.Driver
    private static final String DATABASE_HOST = "localhost";
    private static final String DATABASE_PORT = "3358";
    private static final String DATABASE = "test";
    private static final String DATABASE_USERNAME = "root";
    private static final String DATABASE_PASSWORD = "123456";
    private static final String DATABASE_URL = "jdbc:mysql://" + DATABASE_HOST + ":" + DATABASE_PORT + "/" + DATABASE
            + "?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC";
    
    //endregion

    // 全部文件開啓 覆蓋配置
    // 指定表名配置,不指定表明全部
    public static void main(String[] args) {
        String[] tableNames = null;//new String[]{"account_balance"};
        generator(
                //new LayerType[]{LayerType.entity,LayerType.mapperxml},
                null,
                tableNames);
        System.out.println("==========ok=========");
    }
    
    //包名 com.github.bjlhx15
    private static final String packageName = "com.aaa.test";

    //代碼生成路徑 相對當前的路徑
    private static final String baseProjectPath = System.getProperty("user.dir");

    //代碼註釋做者
    private static final String author = "<a>https://www.cnblogs.com/bjlhx/</a>";

    // 數據庫信息 jdbc:mysql://localhost:3358/test?useUnicode=true&useSSL=false&characterEncoding=utf8
    private static final String DATABASE_DRIVER_NAME = "com.mysql.cj.jdbc.Driver"; //com.mysql.jdbc.Driver
    private static final String DATABASE_HOST = "localhost";
    private static final String DATABASE_PORT = "3358";
    private static final String DATABASE = "test";
    private static final String DATABASE_USERNAME = "root";
    private static final String DATABASE_PASSWORD = "123456";
    private static final String DATABASE_URL = "jdbc:mysql://" + DATABASE_HOST + ":" + DATABASE_PORT + "/" + DATABASE
            + "?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC";


    // 生成的分層代碼
    enum LayerType {
        entity("/test-demo-model"),
        mapper("/test-demo-dao"),
        mapperxml("/test-demo-web"),
        service("/test-demo-service"),
        web("/test-demo-web"),
        ;

        LayerType(String path) {
            this.path = path;
        }

        /**
         * 路徑
         */
        private String path;
        public String getPath() {
            return path;
        }
        public void setPath(String path) {
            this.path = path;
        }
    }

    /**
     * 逆向工程 生成分層代碼,默認覆蓋模式
     *
     * @param layerTypes 要生成的分層名 null 所有,指定的話只生成指定的包
     * @param tableNames 要生成的表名
     */
    private static void generator(LayerType[] layerTypes, String[] tableNames) {
        if (layerTypes == null) {
            layerTypes = LayerType.values();
        }
        for (LayerType layerType : layerTypes) {
            String projectPath = baseProjectPath + layerType.getPath();
            // 全局配置
            GlobalConfig gc = getGlobalConfig(projectPath);
            // 數據源配置
            DataSourceConfig dsc = getDataSourceConfig();
            // 包配置
            PackageConfig pc = getPackageConfig();
            // xml配置
            InjectionConfig cfg = new InjectionConfig() {
                @Override
                public void initMap() {

                }
            };
            if (layerType.equals(LayerType.mapperxml)) {
                // 自定義配置
                cfg = getInjectionConfig(projectPath);
            }
            // 策略配置**(個性化定製)**
            StrategyConfig strategy = getStrategyConfig(tableNames, pc);

            //代碼生成器
            new AutoGenerator()
                    .setGlobalConfig(gc)//全局
                    .setDataSource(dsc)//數據源
                    .setPackageInfo(pc)//包配置
                    .setCfg(cfg)//自定義 xml
                    //.setTemplate(new TemplateConfig().setXml(null))//自定義 xml 此處設置爲null,就不會再java下建立xml的文件夾了
                    .setTemplate(getTemplateConfig(layerType))
                    .setStrategy(strategy) //個性化策略
                    .setTemplateEngine(new FreemarkerTemplateEngine())//模板引擎
                    .execute();//執行
        }
    }

    // 策略配置**(個性化定製)**
    private static StrategyConfig getStrategyConfig(String[] tableNames, PackageConfig pc) {
        StrategyConfig strategy = new StrategyConfig();
        //數據庫表映射到實體的命名策略,該處下劃線轉駝峯命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //數據庫表映射到實體的命名策略,該處下劃線轉駝峯命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);

        if (tableNames != null && tableNames.length > 0) {
            strategy.setInclude(tableNames); //被掃描的表名 須要包含的表名,容許正則表達式(與exclude二選一配置)
        }

        //實體類自動繼承Entity,不須要也能夠
        strategy.setSuperEntityClass(String.format("%s.po.BasePO", packageName));
        //【實體】是否爲lombok模型(默認 false)
        strategy.setEntityLombokModel(false);
        // 寫於父類中的公共字段
        strategy.setSuperEntityColumns("id","delflag","status","remark","created_datetime","updated_datetime");

        // 自定義 service 父類
        //strategy.setSuperServiceClass(String.format("%s.service.BaseService", packageName));
        // 自定義 service 實現類父類
        //strategy.setSuperServiceImplClass(String.format("%s.service.impl.BaseServiceImpl", packageName));

        // 控制層是否使用Rest風格  生成 @RestController 控制器
        strategy.setRestControllerStyle(true);
        //控制層自動繼承父類BaseController,不須要也能夠 BaseController
        strategy.setSuperControllerClass(String.format("%s.controller.base.BaseController", packageName));
        strategy.setControllerMappingHyphenStyle(true);

        //根據表名來建對應的類名,若是你的表名沒有什麼下劃線,好比test,那麼你就能夠取消這一步
        //strategy.setTablePrefix(pc.getModuleName() + "_");
        return strategy;
    }

    //xml 定製化
    private static InjectionConfig getInjectionConfig(String projectPath) {
        // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        //xml 配置
        List<FileOutConfig> focList = new ArrayList();
        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸入文件名稱================================模塊名(本身設置)
                return projectPath + "/src/main/resources/mapper/"
                        + "/autoxml/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        return cfg;
    }

    // 數據源配置
    private static DataSourceConfig getDataSourceConfig() {
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(DATABASE_URL)
                // dsc.setSchemaName("public");
                .setDriverName(DATABASE_DRIVER_NAME)
                .setUsername(DATABASE_USERNAME)
                .setPassword(DATABASE_PASSWORD)
                .setTypeConvert(new MySqlTypeConvert() {
                    @Override
                    public DbColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                        //將數據庫中timestamp轉換成date
                        if (fieldType.toLowerCase().contains("timestamp")) {
                            return DbColumnType.DATE;
                        }
                        return (DbColumnType) super.processTypeConvert(globalConfig, fieldType);
                    }
                });
        return dsc;
    }

    // 包配置
    private static PackageConfig getPackageConfig() {
        PackageConfig pc = new PackageConfig();
        //pc.setModuleName(moduleName)//自定義模塊名 account
        pc.setParent(packageName)//《====包名(本身手動設置)com.lihongxu.test 與 module組合
                .setEntity("po.auto")//Entity包名
                .setMapper("mapper.auto")//mapper包名
                .setService("service.auto")
                .setController("controller.auto");
        return pc;
    }

    // 全局配置
    private static GlobalConfig getGlobalConfig(String projectPath) {
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(projectPath + "/src/main/java");//生成文件的輸出目錄
        gc.setOpen(false);//是否打開輸出目錄 默認true
        gc.setFileOverride(true);// 是否覆蓋已有文件 默認false
        gc.setActiveRecord(false);//開啓 ActiveRecord 模式 默認false

        gc.setAuthor(author);// 做者名
        gc.setEnableCache(false);// XML 二級緩存
        gc.setBaseResultMap(true);//開啓 BaseResultMap 默認false
        gc.setBaseColumnList(true);//開啓 baseColumnList 默認false
        gc.setServiceName("I%sService"); //service接口 命名方式 例如:%sBusiness 生成 UserBusiness
        gc.setSwagger2(true); //實體屬性 Swagger2 註解

        // 自定義文件命名,注意 %s 會自動填充表實體屬性! 使用定製化生成了
//        gc.setMapperName("%sMapper");
//        gc.setXmlName("%sMapper");
//        gc.setServiceName("%sService");
//        gc.setServiceImplName("%sServiceImpl");
//        gc.setControllerName("%sController");
        return gc;
    }

    //生成分層代碼
    private static TemplateConfig getTemplateConfig(LayerType layerType) {
        TemplateConfig templateConfig = new TemplateConfig();
        switch (layerType) {
            case entity:
                templateConfig.setEntity(new TemplateConfig().getEntity(false))
                        .setMapper(null)
                        .setXml(null)
                        .setService(null)
                        .setServiceImpl(null)
                        .setController(null);
                break;
            case mapper:
                templateConfig.setEntity(null)
                        .setMapper(new TemplateConfig().getMapper())
                        .setXml(null)
                        .setService(null)
                        .setServiceImpl(null)
                        .setController(null);
                break;
            case mapperxml:
                templateConfig.setEntity(null)
                        .setMapper(null)
                        .setXml(null)
                        .setService(null)
                        .setServiceImpl(null)
                        .setController(null);
                break;
            case service:
                templateConfig.setEntity(null)
                        .setMapper(null)
                        .setXml(null)
                        .setService(new TemplateConfig().getService())
                        .setServiceImpl(new TemplateConfig().getServiceImpl())
                        .setController(null);
                break;
            case web:
                templateConfig.setEntity(null)
                        .setMapper(null)
                        .setXml(null)
                        .setService(null)
                        .setServiceImpl(null)
//                        .setController(new TemplateConfig().getController());
                        .setController("/templates/basecontroller.java");
                break;
            default:
                throw new IllegalArgumentException("參數匹配錯誤,請檢查");
        }
        return templateConfig;
    }
}
View Code

 

  說明一、packageName 包名,無需修改,已經在使用上述命令是生成

2.三、log4j2整合

  參看 代碼

2.四、統一響應【統一異常處理】

一、設置統一響應輸出類 BaseResponse

package com.aaa.test.response;

import com.aaa.test.enums.ErrorCodeEnum;

/**
 * 統一返回結果
 */
public class BaseResponse<T> {
    /**
     * 返回的data
     * */
    private T data;

    /**
     * 錯誤碼
     * */
    private Integer errorCode;

    /**
     * 錯誤信息
     * */
    private String errorMsg;

    /**
     * 是否成功
     * */
    private boolean success = false;

    /**
     * 出現異常的構造函數
     * */
    public BaseResponse(ErrorCodeEnum errorCodeEnum) {
        this.errorCode = errorCodeEnum.getErrorCode();
        this.errorMsg = errorCodeEnum.getErrorMsg();
    }

    /**
     * 成功返回的結果
     * */
    public BaseResponse(T data) {
        success = true;
        this.data = data;
    }

    /**
     * 成功返回的結果
     * */
    public BaseResponse(boolean success,ErrorCodeEnum errorCodeEnum,T data) {
        this.success = success;
        this.data = data;
        this.errorCode = errorCodeEnum.getErrorCode();
        this.errorMsg = errorCodeEnum.getErrorMsg();
    }

    public static <T> BaseResponse success(T data) {
        return new BaseResponse(data);
    }
    public static <T> BaseResponse success(ErrorCodeEnum errorCodeEnum) {
        return new BaseResponse(true,errorCodeEnum,null);
    }

    public static BaseResponse fail(ErrorCodeEnum errorCodeEnum) {
        return new BaseResponse(errorCodeEnum);
    }


    public static BaseResponse fail() {
        return new BaseResponse(ErrorCodeEnum.result_exception);
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Integer getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(Integer errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    @Override
    public String toString() {
        return "BaseResponse{" +
                "data=" + data +
                ", errorCode='" + errorCode + '\'' +
                ", errorMsg='" + errorMsg + '\'' +
                ", success=" + success +
                '}';
    }
}
View Code

其中用到了錯誤碼 ErrorCodeEnum

package com.aaa.test.enums;

/**
 * 錯誤代碼枚舉類
 */
public enum ErrorCodeEnum {
    // 成功類響應
    success(200000, "成功"),
    no_response_data(200001, "沒有返回數據"),

    //請求類響應碼
    Param_does_not_exist(400001, "查找參數不存在"),
    Param_does_not_correct(400002, "所傳參數格式不正確"),
    HttpMediaTypeNotSupportedException(400003, "不支持的Content-type類型"),

    result_exception(600000, "待處理服務端異常"),

    ;

    ErrorCodeEnum(Integer errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    /**
     * 錯誤碼
     */
    private Integer errorCode;

    /**
     * 錯誤信息
     */
    private String errorMsg;

    public Integer getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(Integer errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}
View Code

技術需求

  一、統一響應類支持靜態的成功失敗方法

  二、錯誤碼,設計應爲6位數值類型,其中200000 類別爲成功類型,400000爲請求類型碼,500000爲服務端異常,其餘大於 600000碼爲客戶自定義碼, 

二、Controller編寫

  支持controller多種響應參數

    常見類型String,int,Integer、Map、Object、單個類等

    支持使用上述:BaseResponse 包裝響應類

    支持使用:ResponseEntity接收類型

  經過統一響應處理返回統一:BaseResponse格式

三、統一響應處理方案 

  編寫RestControllerAdvice,對響應,以及異常統一處理

/**
 * 統一返回結果異常處理類
 */
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {
    private final Logger log = LoggerFactory.getLogger(ResponseResultBodyAdvice.class);
    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseBody.class;

    /**
     * 判斷類或者方法是否使用了 @ResponseResultBody
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
     * 當類或者方法使用了 @ResponseResultBody 就會調用這個方法
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 避免已經返回基礎類型 屢次轉換
        if (body instanceof BaseResponse) {
            return body;
        }
        return BaseResponse.success(body);
    }

    /**
     * 提供對標準Spring MVC異常的處理
     *
     * @param ex      the target exception
     * @param request the current request
     */
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<BaseResponse<?>> exceptionHandler(Exception ex, WebRequest request) {
        log.error("ExceptionHandler: {}", ex.getMessage());
        HttpHeaders headers = new HttpHeaders();
        if (ex instanceof HttpMediaTypeNotSupportedException) {
            // 針對 返回String 類型 須要客戶端設置Content-type 爲application/json
            return this.handleResultException(new ResultException(ErrorCodeEnum.HttpMediaTypeNotSupportedException),
                    headers, request);
        } else if (ex instanceof ResultException) {
            return this.handleResultException((ResultException) ex, headers, request);
        }
        //TODO: 這裏能夠自定義其餘的異常攔截
        return this.handleException(ex, headers, request);
    }

    /**
     * 對ResultException類返回返回結果的處理
     */
    protected ResponseEntity<BaseResponse<?>> handleResultException(ResultException ex, HttpHeaders headers, WebRequest request) {
        BaseResponse<?> body = BaseResponse.fail((ex.getErrorCodeEnum()));
        HttpStatus status = null;
        if (ex.getErrorCodeEnum().getErrorCode().intValue() >= 500000) {
            status = HttpStatus.INTERNAL_SERVER_ERROR;
        } else if (ex.getErrorCodeEnum().getErrorCode().intValue() >= 400000) {
            status = HttpStatus.BAD_REQUEST;
        }
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * 異常類的統一處理
     */
    protected ResponseEntity<BaseResponse<?>> handleException(Exception ex, HttpHeaders headers, WebRequest request) {
        BaseResponse<?> body = BaseResponse.fail();
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
     * handleExceptionInternal(java.lang.Exception, java.lang.Object, org.springframework.http.HttpHeaders,
     * org.springframework.http.HttpStatus, org.springframework.web.context.request.WebRequest)
     * <p>
     * A single place to customize the response body of all exception types.
     * <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
     * request attribute and creates a {@link ResponseEntity} from the given
     * body, headers, and status.
     */
    protected ResponseEntity<BaseResponse<?>> handleExceptionInternal(
            Exception ex, BaseResponse<?> body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        return new ResponseEntity<>(body, headers, status);
    }
}
View Code

  上述在返回String類型,以及Object等類型或者爲空時會出現類型轉換異常。作如下處理:

  一、配置WebMvcConfigurer,將 MappingJackson2HttpMessageConverter

/舊版本 WebMvcConfigurerAdapter  spring5棄用了 WebMvcConfigurerAdapter
@Configuration
public class WebMvcConfig  implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //提到最前面 ,主要爲了處理基礎類型時 XXX not cast String type 相似問題,結合請求類型必須爲application/json
        // 提早使用MappingJackson2HttpMessageConverter 處理 避免使用 StringHttpMessageConverter處理 String類型
        converters.add(0,new MappingJackson2HttpMessageConverter());
    }
}
View Code

    主要做用://提到最前面 ,主要爲了處理基礎類型時 XXX not cast String type 相似問題,結合請求類型必須爲application/json

        // 提早使用MappingJackson2HttpMessageConverter 處理 避免使用 StringHttpMessageConverter處理 String類型

  二、編寫controller級別aop,主要做用處理,對象爲null,以及String爲null,轉換爲BaseResponse時的類型轉換異常

/**
 * 統一結果處理切面, 注意返回時 null 時候 處理
 */
@Aspect
@Component
public class ResponseAspect {
    @Around("execution(* com.aaa.test.controller..*(..))")
    public Object controllerProcess(ProceedingJoinPoint pjd) throws Throwable {
        Object result = pjd.proceed();
        // 是null特殊處理
        if (result == null) {
            try {
                MethodSignature signature = (MethodSignature) pjd.getSignature();
                Class returnType = signature.getReturnType();
                if("java.lang.Object".equals(returnType.getName())){
                    // object 使用此方式
                    return BaseResponse.success(result);
                }else if("java.lang.String".equals(returnType.getName())){
                    // 字符串 初始化一個新的返回
                    return returnType.newInstance();
                }
                //其餘默認 返回
                return result;
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return result;
    }
}
View Code
相關文章
相關標籤/搜索