knife4j实现自定义Map类型参数接收

快速上手
1:引入依赖
注意事项:版本不能高于2.0.9,因为目前官网knife4j官网上对于高版本得还在迭代。官网地址
   <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.9</version>
        </dependency>
2:自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonProperty {

    String key();  //key

    String example() default "";

    Class type() default String.class;  //支持string 和 int

    String description() default "";

}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonObject {

    ApiJsonProperty[] value(); //对象属性值
    String name();  //对象名称
    
}
3:编写参数构建插件
import com.fasterxml.classmate.TypeResolver;

import com.phm.annotation.ApiJsonProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.ParameterContext;

import java.util.Map;
import java.util.Optional;

//plugin加载顺序,默认是最后加载
@Component
@Order
public class MapApiReader extends ClassLoader implements ParameterBuilderPlugin {
    @Autowired
    private TypeResolver typeResolver;


    public void apply(ParameterContext parameterContext) {
        Map<String, Object> maps = SwaggerMapContext.getMap();

        ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
        OperationContext operationContext = parameterContext.getOperationContext();
        String requestMappingPatternName = operationContext.requestMappingPattern();

        Optional<String> parameterNameOptional = methodParameter.defaultName();
        String parameterName = parameterNameOptional.get();

        if (methodParameter.getParameterType().canCreateSubtype(Map.class)) {
            System.out.println(requestMappingPatternName);
            String name = "H" + parameterName;
            name = SwaggerASMUtil.returnClassName(requestMappingPatternName,name);

            ApiJsonProperty[] properties = (ApiJsonProperty[]) maps.get(requestMappingPatternName);

            byte[] cs = SwaggerASMUtil.createRefModel(properties,name);
            Class hw = this.defineClass(name, cs, 0, cs.length);

            parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(hw));

            parameterContext.parameterBuilder().parameterType("body").modelRef(new ModelRef(name)).name(name);
        }
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
注意事项:一定要配置获取项目得名称进行拼接,否则后续map得key值无法精准匹配
import com.phm.annotation.ApiJsonObject;
import com.phm.annotation.ApiJsonProperty;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;


@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Autowired
    private Environment env;

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class clazz =  bean.getClass();
        Package packageStr = clazz.getPackage();
        String packAgeName = packageStr.getName();

        if(!packAgeName.contains(SwaggerMapContext.filterPackage)){
            return bean;
        }
        if(clazz.getAnnotation(RestController.class) == null && clazz.getAnnotation(Controller.class) == null){
            return bean;
        }
        RequestMapping controllerRequestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
        String classRequestUrl =  Arrays.toString(controllerRequestMapping.value());

        List methods = Arrays.asList(clazz.getDeclaredMethods());
        Iterator<Method> iterator = methods.iterator();
        //获取项目名称
        String property = env.getProperty("server.servlet.context-path");
        while(iterator.hasNext()){
            Method method = iterator.next();
            String methodRequest = getRequestUrl(method);
            String key = classRequestUrl + methodRequest ;
            key = key.replaceAll("\\[","").replaceAll("\\]","");
            ApiJsonObject annotation = method.getAnnotation(ApiJsonObject.class);
            if(annotation != null){
                ApiJsonProperty[] values = annotation.value();

                SwaggerMapContext.getMap().put(property+key,values);
            }
        }
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 这边只做简单打印   原样返回bean
        //if(AutoConfigurationPackages.class.getName().equals(beanName)){
        //    System.out.println("postProcessBeforeInitialization===="+beanName);
        //}
        return bean;
    }

    private String getRequestUrl(Method method){
        String methodRequest = "";
        if(method.getAnnotation(RequestMapping.class) != null) {
            methodRequest = Arrays.toString(method.getAnnotation(RequestMapping.class).value());
        }
        if(method.getAnnotation(PutMapping.class) != null) {
            methodRequest = Arrays.toString(method.getAnnotation(PutMapping.class).value());
        }
        if(method.getAnnotation(DeleteMapping.class) != null) {
            methodRequest = Arrays.toString(method.getAnnotation(DeleteMapping.class).value());
        }
        if(method.getAnnotation(GetMapping.class) != null) {
            methodRequest = Arrays.toString(method.getAnnotation(GetMapping.class).value());
        }
        if(method.getAnnotation(PatchMapping.class) != null) {
            methodRequest = Arrays.toString(method.getAnnotation(PatchMapping.class).value());
        }
        if(method.getAnnotation(PostMapping.class) != null) {
            methodRequest = Arrays.toString(method.getAnnotation(PostMapping.class).value());
        }
        return methodRequest;
    }
}

注意事项:filterPackage 需要配置自定义注解得目录或者controller目录都可以
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class SwaggerMapContext {

    public static final String filterPackage = "com.phm.controller";

    private static Map<String, Object> map = new ConcurrentHashMap<>();

    public static Map<String, Object> getMap(){
        return map;
    }

}
示例代码:
   @PostMapping("/mark")
    @ApiJsonObject(name = "params", value = {
            @ApiJsonProperty(type = Integer.class,key = "mobile", example = "18614242538", description = "user mobile"),
            @ApiJsonProperty(type = Integer.class,key = "rows", example = "15", description = "行数")
    })
    @ApiOperation(value = "健康评估")
    public Result mark(@RequestBody  Map<String, Object> params)  {
        System.out.println(params.get("rows"));
        return Result.success();
    }

在这里插入图片描述

项目搭建集成步骤省略,参考别的博客。到这里map类型参数就可以正常接收了
总结思路:

1:在spring初始化的时候 通过 MyBeanPostProcessor 收集 注解信息 放到 SwaggerMapContext里
2:MapApiReader 实现了 ParameterBuilderPlugin ,在 apply 里 ,循环遍历 收集到的注解信息 使用 ASM ,动态生成类。也就是 将Map类型参数里的 key 转换成一个类中的 字段。
3:生成类之后,加载这个类,替换 modelRef

参考博客地址
如有其他问题可私信或评论,相互学习。欢迎加微信请添加图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值