mirror of https://gitee.com/stylefeng/roses
【wrapper】增加wrapper功能
parent
f32847a931
commit
0a6ab12db6
|
@ -0,0 +1,10 @@
|
|||
# wrapper模块,一种开发思路或者开发工具
|
||||
|
||||
将控制器层的返回结果进行进一层包装从而灵活的进行响应参数装配
|
||||
|
||||
例如:
|
||||
select menu_id,menu_name,create_user from sys_menu
|
||||
|
||||
利用Wrapper可以将返回结果的关联字段响应中文名称,例如create_user这种字段,就不用去left join连表查询
|
||||
|
||||
另外wrapper时可以用缓存,加快wrapper速度
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>roses-kernel</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>kernel-d-wrapper</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>wrapper-api</module>
|
||||
<module>wrapper-sdk</module>
|
||||
<module>wrapper-spring-boot-starter</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 开发规则 -->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-a-rule</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
wrapper的api模块
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-wrapper</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>wrapper-api</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,23 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 基础包装接口,
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:18
|
||||
*/
|
||||
public interface BaseWrapper<T> {
|
||||
|
||||
/**
|
||||
* 具体包装的过程
|
||||
*
|
||||
* @param beWrappedModel 被包装的原始对象,可以是obj,list,page,PageResult
|
||||
* @return 包装后增加的增量集合
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:22
|
||||
*/
|
||||
Map<String, Object> doWrap(T beWrappedModel);
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper.api.annotation;
|
||||
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.BaseWrapper;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 结果包装的注解,一般用在Controller层,给最后响应结果做包装
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:10
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface Wrapper {
|
||||
|
||||
/**
|
||||
* 具体包装类
|
||||
*/
|
||||
Class<? extends BaseWrapper<?>>[] value();
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper.api.constants;
|
||||
|
||||
/**
|
||||
* Wrapper的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/19 22:23
|
||||
*/
|
||||
public interface WrapperConstants {
|
||||
|
||||
/**
|
||||
* Wrapper模块的名称
|
||||
*/
|
||||
String WRAPPER_MODULE_NAME = "kernel-d-wrapper";
|
||||
|
||||
/**
|
||||
* 异常枚举的步进值
|
||||
*/
|
||||
String WRAPPER_EXCEPTION_STEP_CODE = "24";
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper.api.exception;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.constants.WrapperConstants;
|
||||
|
||||
/**
|
||||
* Wrapper异常
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/19 22:24
|
||||
*/
|
||||
public class WrapperException extends ServiceException {
|
||||
|
||||
public WrapperException(AbstractExceptionEnum exception, Object... params) {
|
||||
super(WrapperConstants.WRAPPER_MODULE_NAME, exception.getErrorCode(), StrUtil.format(exception.getUserTip(), params));
|
||||
}
|
||||
|
||||
public WrapperException(AbstractExceptionEnum exception) {
|
||||
super(WrapperConstants.WRAPPER_MODULE_NAME, exception);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper.api.exception.enums;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.constants.WrapperConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Wrapper异常的状态码
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/19 22:24
|
||||
*/
|
||||
@Getter
|
||||
public enum WrapperExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 被包装的值不能是基本类型
|
||||
*/
|
||||
BASIC_TYPE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + WrapperConstants.WRAPPER_EXCEPTION_STEP_CODE + "01", "被包装的值不能是基本类型"),
|
||||
|
||||
/**
|
||||
* 字段包装转化异常
|
||||
*/
|
||||
TRANSFER_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + WrapperConstants.WRAPPER_EXCEPTION_STEP_CODE + "02", "字段包装转化异常");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
WrapperExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
wrapper模块的sdk
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-wrapper</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>wrapper-sdk</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--wrapper模块的api-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>wrapper-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--db模块的api-->
|
||||
<!--包装的时候可能包装PageResult对象-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>db-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- aop -->
|
||||
<!-- 包装过程是在aop中进行 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis-plus dao框架 -->
|
||||
<!-- 包装分页的参数可能 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,213 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData;
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.BaseWrapper;
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.annotation.Wrapper;
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.exception.WrapperException;
|
||||
import cn.stylefeng.roses.kernel.wrapper.api.exception.enums.WrapperExceptionEnum;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* controller结果包装的aop
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:42
|
||||
*/
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class WrapperAop {
|
||||
|
||||
/**
|
||||
* 切入点
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:42
|
||||
*/
|
||||
@Pointcut("@annotation(cn.stylefeng.roses.kernel.wrapper.api.annotation.Wrapper)")
|
||||
private void wrapperPointcut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行具体的包装过程
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:44
|
||||
*/
|
||||
@Around("wrapperPointcut()")
|
||||
public Object doWrapper(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
|
||||
|
||||
// 直接执行原有业务逻辑
|
||||
Object proceedResult = proceedingJoinPoint.proceed();
|
||||
|
||||
return processWrapping(proceedingJoinPoint, proceedResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 具体包装过程
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 17:53
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private Object processWrapping(ProceedingJoinPoint proceedingJoinPoint, Object originResult) throws IllegalAccessException, InstantiationException {
|
||||
|
||||
// 获取@Wrapper注解
|
||||
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
|
||||
Method method = methodSignature.getMethod();
|
||||
Wrapper wrapperAnnotation = method.getAnnotation(Wrapper.class);
|
||||
|
||||
// 获取注解上的处理类
|
||||
Class<? extends BaseWrapper<?>>[] baseWrapperClasses = wrapperAnnotation.value();
|
||||
|
||||
// 如果注解上的为空直接返回
|
||||
if (ObjectUtil.isEmpty(baseWrapperClasses)) {
|
||||
return originResult;
|
||||
}
|
||||
|
||||
// 获取原有返回结果,如果不是ResponseData则不进行处理(需要遵守这个约定)
|
||||
if (!(originResult instanceof ResponseData)) {
|
||||
log.warn("当前请求的返回结果不是ResponseData类型,直接返回原值!");
|
||||
return originResult;
|
||||
}
|
||||
|
||||
// 获取ResponseData中的值
|
||||
ResponseData responseData = (ResponseData) originResult;
|
||||
Object beWrapped = responseData.getData();
|
||||
|
||||
// 如果是基本类型,不进行加工处理
|
||||
if (ObjectUtil.isBasicType(beWrapped)) {
|
||||
throw new WrapperException(WrapperExceptionEnum.BASIC_TYPE_ERROR);
|
||||
}
|
||||
|
||||
// 如果是Page类型
|
||||
if (beWrapped instanceof Page) {
|
||||
|
||||
// 获取Page原有对象
|
||||
Page page = (Page) beWrapped;
|
||||
|
||||
// 将page中所有records都包装一遍
|
||||
ArrayList<Map<String, Object>> maps = new ArrayList<>();
|
||||
for (Object wrappedItem : page.getRecords()) {
|
||||
maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
|
||||
}
|
||||
|
||||
page.setRecords(maps);
|
||||
responseData.setData(page);
|
||||
}
|
||||
|
||||
// 如果是PageResult类型
|
||||
else if (beWrapped instanceof PageResult) {
|
||||
|
||||
// 获取PageResult原有对象
|
||||
PageResult pageResult = (PageResult) beWrapped;
|
||||
|
||||
// 将PageResult中所有rows都包装一遍
|
||||
ArrayList<Map<String, Object>> maps = new ArrayList<>();
|
||||
for (Object wrappedItem : pageResult.getRows()) {
|
||||
maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
|
||||
}
|
||||
|
||||
pageResult.setRows(maps);
|
||||
responseData.setData(pageResult);
|
||||
}
|
||||
|
||||
// 如果是List类型
|
||||
else if (beWrapped instanceof Collection) {
|
||||
|
||||
// 获取原有的List
|
||||
Collection collection = (Collection) beWrapped;
|
||||
|
||||
// 将page中所有records都包装一遍
|
||||
ArrayList<Map<String, Object>> maps = new ArrayList<>();
|
||||
for (Object wrappedItem : collection) {
|
||||
maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
|
||||
}
|
||||
|
||||
responseData.setData(maps);
|
||||
}
|
||||
|
||||
// 如果是Array类型
|
||||
else if (ArrayUtil.isArray(beWrapped)) {
|
||||
|
||||
// 获取原有的Array
|
||||
Object[] objects = this.objToArray(beWrapped);
|
||||
|
||||
// 将array中所有records都包装一遍
|
||||
ArrayList<Map<String, Object>> maps = new ArrayList<>();
|
||||
for (Object wrappedItem : objects) {
|
||||
maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
|
||||
}
|
||||
|
||||
responseData.setData(maps);
|
||||
}
|
||||
|
||||
// 如果是Object类型
|
||||
else {
|
||||
responseData.setData(this.wrapPureObject(beWrapped, baseWrapperClasses));
|
||||
}
|
||||
|
||||
|
||||
return responseData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原始对象包装成一个map的过程
|
||||
* <p>
|
||||
* 期间多次根据BaseWrapper接口方法执行包装过程
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 21:40
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private Map<String, Object> wrapPureObject(Object originModel, Class<? extends BaseWrapper<?>>[] baseWrapperClasses) {
|
||||
|
||||
// 首先将原始的对象转化为map
|
||||
Map<String, Object> originMap = BeanUtil.beanToMap(originModel);
|
||||
|
||||
// 经过多个包装类填充属性
|
||||
try {
|
||||
for (Class<? extends BaseWrapper<?>> baseWrapperClass : baseWrapperClasses) {
|
||||
BaseWrapper baseWrapper = baseWrapperClass.newInstance();
|
||||
Map<String, Object> incrementFieldsMap = baseWrapper.doWrap(originModel);
|
||||
originMap.putAll(incrementFieldsMap);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("原始对象包装过程,字段转化异常:{}", e.getMessage());
|
||||
throw new WrapperException(WrapperExceptionEnum.TRANSFER_ERROR);
|
||||
}
|
||||
|
||||
return originMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object转为一个array,确保object为数组类型
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/7/24 22:06
|
||||
*/
|
||||
private Object[] objToArray(Object object) {
|
||||
int length = Array.getLength(object);
|
||||
Object[] result = new Object[length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = Array.get(object, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
wrapper功能的spring boot自动加载模块
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-wrapper</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>wrapper-spring-boot-starter</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--wrapper的sdk-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>wrapper-sdk</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,29 @@
|
|||
package cn.stylefeng.roses.kernel.wrapper.starter;
|
||||
|
||||
import cn.stylefeng.roses.kernel.wrapper.WrapperAop;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Wrapper的自动配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/19 22:42
|
||||
*/
|
||||
@Configuration
|
||||
public class GunsWrapperAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Wrapper的自动配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/19 22:42
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(WrapperAop.class)
|
||||
public WrapperAop wrapperAop() {
|
||||
return new WrapperAop();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.stylefeng.roses.kernel.wrapper.starter.GunsWrapperAutoConfiguration
|
Loading…
Reference in New Issue