javassist 解決Swagger2一個DTO 適用於多個接口

背景: 

    最近接到老大的一個任務,用一個DTO適用於多個接口,通過某個接口上註解解決把不需要得屬性再swagger上隱藏掉。

結果:

一個DTO適用於2個接口

 

代碼

   步驟一

1.引入pom
      <!-- swagger2 -->
        <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>

2.springBoot  開啓
    @SpringBootApplication
    @EnableAsync
    @EnableSwagger2 //開啓swagger
    public class RenrenApplication   {
	    public static void main(String[] args) {
		    SpringApplication.run(RenrenApplication.class, args);
	    }

    }

步驟二

1.自定義註解 

@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIgp {
        String[] value(); //對象屬性值
}


2. 實現parameterBuilderPlugin 
ps: 注意這裏用的是alibaba 的javassist 的包最好



@Component
@Order   //plugin加載順序,默認是最後加載
public class MapApiReader implements ParameterBuilderPlugin {
    @Autowired
    private TypeResolver typeResolver;

    @Override
    public void apply(ParameterContext parameterContext) {
        ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
        Class originClass = parameterContext.resolvedMethodParameter().getParameterType().getErasedType();


            Optional<ApiIgp> optional = methodParameter.findAnnotation(ApiIgp.class);
            if (optional.isPresent()) {
                Random random = new Random();
                String name = originClass.getSimpleName() + "Model" + random.nextInt(100);  //model 名稱
                String[] properties = optional.get().value();
                try {
                    parameterContext.getDocumentationContext()
                            .getAdditionalModels()
                            .add(typeResolver.resolve(createRefModelIgp(properties, originClass.getPackage()+"."+name, originClass)));  //像documentContext的Models中添加我們新生成的Class
                } catch (Exception e) {
                    e.printStackTrace();
                }
                parameterContext.parameterBuilder()  //修改Map參數的ModelRef爲我們動態生成的class
                        .parameterType("body")
                        .modelRef(new ModelRef(name))
                        .name(name);
        }

    }

    private Class createRefModelIgp(String[] propertys, String name, Class origin) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass( name);
        try {
            Field[] fields = origin.getDeclaredFields();
            List<Field> fieldList = Arrays.asList(fields);
            List<String> ignorePropertys = Arrays.asList(propertys);
            List<Field> dealFileds = fieldList.stream().filter(s -> !ignorePropertys.contains(s.getName())).collect(Collectors.toList());
            for (Field field : dealFileds) {
                CtField ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()), field.getName(), ctClass);
                ctField.setModifiers(Modifier.PUBLIC);
                ApiModelProperty ampAnno = origin.getDeclaredField(field.getName()).getAnnotation(ApiModelProperty.class);
                String attributes = java.util.Optional.ofNullable(ampAnno).map(s->s.value()).orElse("");
                if (StringUtils.isNotBlank(attributes) ){ //添加model屬性說明
                    ConstPool constPool = ctClass.getClassFile().getConstPool();
                    AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
                    Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool);
                    ann.addMemberValue("value", new StringMemberValue(attributes, constPool));
                    attr.addAnnotation(ann);
                    ctField.getFieldInfo().addAttribute(attr);
                }
                ctClass.addField(ctField);
            }
            return ctClass.toClass();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }




    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }
}

步驟三: 

 

public class AppCustomController {
    @Autowired
    private UserService userService;


    @PostMapping("test1")
    @ApiOperation("form測試1")
    public R custom(@RequestBody RegisterForm form){
        return R.ok();
    }

    @PostMapping("test2")
    @ApiOperation("form測試2")
    public R test2(@ApiIgp ({"name","addr"})  // 這裏把你不需要的DTO屬性中的值填寫上
                       @RequestBody RegisterForm form){
        return R.ok();
    }
}

總結

  我這邊通過javassist這個類結合swagger 可拓展的插件去解決這個問題,通過自定義註解把不需要的屬性值在swagger頁面去掉,這裏只會影響頁面的顯示,不會影響到接收的參數。

拓展

  當然這裏你也可以通過自定義MapApiReader的類去 "正着" 處理這個問題, 你可以 把需要的屬性留着,或者說  在每個請求統一加上某個屬性說明都是可以做到的。

ps(這裏javassist 最好使用alibaba的 異常不會影響整個swagger)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章