假如接收参数的实体类中关联了其他对象,那么swagger2的页面中参数应该会多出来这些,dept.id,dept.deptName,或者集合属性,roles[0].id,roles[0].roleName等等。
这些属性有可能是不需要用来接收参数的,出现在文档中会给前端开发人员带来困惑
笔者在swagger2提供的配置中没有找到隐藏此类参数的设置
springfox.documentation.spring.web.readers.parameter.ModelAttributeParameterExpander
package com.zihuiinfo.facesdk.framework.common.swagger;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.members.ResolvedField;
import com.fasterxml.classmate.members.ResolvedMethod;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.Maps;
import springfox.documentation.schema.Types;
import springfox.documentation.schema.property.bean.AccessorsProvider;
import springfox.documentation.schema.property.field.FieldProvider;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.schema.AlternateTypeProvider;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
import springfox.documentation.spring.web.readers.parameter.ExpansionContext;
import springfox.documentation.spring.web.readers.parameter.ModelAttributeField;
import springfox.documentation.spring.web.readers.parameter.ModelAttributeParameterExpander;
import springfox.documentation.spring.web.readers.parameter.ModelAttributeParameterMetadataAccessor;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Predicates.*;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static springfox.documentation.schema.Collections.collectionElementType;
import static springfox.documentation.schema.Collections.isContainerType;
import static springfox.documentation.schema.Types.isVoid;
import static springfox.documentation.schema.Types.typeNameFor;
* 覆盖{@link ModelAttributeParameterExpander}
* @see CustomizeModelAttributeParameterExpander#getBeanPropertyNames(Class)
* @see ModelAttributeParameterExpander#getBeanPropertyNames(Class)
* @see IgnoreSwaggerParameter
@Component
@Primary
public class CustomizeModelAttributeParameterExpander extends ModelAttributeParameterExpander {
private static final Logger LOG = LoggerFactory.getLogger(ModelAttributeParameterExpander.class);
private final FieldProvider fields;
private final AccessorsProvider accessors;
private final EnumTypeDeterminer enumTypeDeterminer;
@Autowired
protected DocumentationPluginsManager pluginsManager;
@Autowired
public CustomizeModelAttributeParameterExpander(FieldProvider fields, AccessorsProvider accessors, EnumTypeDeterminer enumTypeDeterminer) {
super(fields, accessors, enumTypeDeterminer);
this.fields = fields;
this.accessors = accessors;
this.enumTypeDeterminer = enumTypeDeterminer;
public List<Parameter> expand(ExpansionContext context) {
List<Parameter> parameters = newArrayList();
Set<PropertyDescriptor> propertyDescriptors = propertyDescriptors(context.getParamType().getErasedType());
Map<Method, PropertyDescriptor> propertyLookupByGetter
= propertyDescriptorsByMethod(context.getParamType().getErasedType(), propertyDescriptors);
Iterable<ResolvedMethod> getters = FluentIterable.from(accessors.in(context.getParamType()))
.filter(onlyValidGetters(propertyLookupByGetter.keySet()));
Map<String, ResolvedField> fieldsByName = FluentIterable.from(this.fields.in(context.getParamType()))
.uniqueIndex(new Function<ResolvedField, String>() {
@Override
public String apply(ResolvedField input) {
return input.getName();
LOG.debug("Expanding parameter type: {}", context.getParamType());
final AlternateTypeProvider alternateTypeProvider = context.getDocumentationContext().getAlternateTypeProvider();
FluentIterable<ModelAttributeField> attributes =
allModelAttributes(
propertyLookupByGetter,
getters,
fieldsByName,
alternateTypeProvider);
FluentIterable<ModelAttributeField> expendables = attributes
.filter(not(simpleType()))
.filter(not(recursiveType(context)));
for (ModelAttributeField each : expendables) {
LOG.debug("Attempting to expand expandable property: {}", each.getName());
parameters.addAll(
expand(
context.childContext(
nestedParentName(context.getParentName(), each),
each.getFieldType(),
context.getOperationContext())));
FluentIterable<ModelAttributeField> collectionTypes = attributes
.filter(and(isCollection(), not(recursiveCollectionItemType(context.getParamType()))));
for (ModelAttributeField each : collectionTypes) {
LOG.debug("Attempting to expand collection/array field: {}", each.getName());
ResolvedType itemType = collectionElementType(each.getFieldType());
if (Types.isBaseType(itemType) || enumTypeDeterminer.isEnum(itemType.getErasedType())) {
parameters.add(simpleFields(context.getParentName(), context, each));
} else {
ExpansionContext childContext = context.childContext(
nestedParentName(context.getParentName(), each),
itemType,
context.getOperationContext());
if (!context.hasSeenType(itemType)) {
parameters.addAll(expand(childContext));
FluentIterable<ModelAttributeField> simpleFields = attributes.filter(simpleType());
for (ModelAttributeField each : simpleFields) {
parameters.add(simpleFields(context.getParentName(), context, each));
return FluentIterable.from(parameters)
.filter(not(hiddenParameters()))
.filter(not(voidParameters()))
.toList();
private FluentIterable<ModelAttributeField> allModelAttributes(
Map<Method, PropertyDescriptor> propertyLookupByGetter,
Iterable<ResolvedMethod> getters,
Map<String, ResolvedField> fieldsByName,
AlternateTypeProvider alternateTypeProvider) {
FluentIterable<ModelAttributeField> modelAttributesFromGetters = from(getters)
.transform(toModelAttributeField(fieldsByName, propertyLookupByGetter, alternateTypeProvider));
FluentIterable<ModelAttributeField> modelAttributesFromFields = from(fieldsByName.values())
.filter(publicFields())
.transform(toModelAttributeField(alternateTypeProvider));
return FluentIterable.from(Sets.union(
modelAttributesFromFields.toSet(),
modelAttributesFromGetters.toSet()));
private Function<ResolvedField, ModelAttributeField> toModelAttributeField(
final AlternateTypeProvider alternateTypeProvider) {
return new Function<ResolvedField, ModelAttributeField>() {
@Override
public ModelAttributeField apply(ResolvedField input) {
return new ModelAttributeField(
alternateTypeProvider.alternateFor(input.getType()),
input.getName(),
input,
input);
private Predicate<ResolvedField> publicFields() {
return new Predicate<ResolvedField>() {
@Override
public boolean apply(ResolvedField input) {
return input.isPublic();
private Predicate<Parameter> voidParameters() {
return new Predicate<Parameter>() {
@Override
public boolean apply(Parameter input) {
return isVoid(input.getType().orNull());
private Predicate<ModelAttributeField> recursiveCollectionItemType(final ResolvedType paramType) {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return equal(collectionElementType(input.getFieldType()), paramType);
private Predicate<Parameter> hiddenParameters() {
return new Predicate<Parameter>() {
@Override
public boolean apply(Parameter input) {
return input.isHidden();
private Parameter simpleFields(
String parentName,
ExpansionContext context,
ModelAttributeField each) {
LOG.debug("Attempting to expand field: {}", each);
String dataTypeName = Optional.fromNullable(typeNameFor(each.getFieldType().getErasedType()))
.or(each.getFieldType().getErasedType().getSimpleName());
LOG.debug("Building parameter for field: {}, with type: ", each, each.getFieldType());
ParameterExpansionContext parameterExpansionContext = new ParameterExpansionContext(
dataTypeName,
parentName,
determineScalarParameterType(
context.getOperationContext().consumes(),
context.getOperationContext().httpMethod()),
new ModelAttributeParameterMetadataAccessor(
each.annotatedElements(),
each.getFieldType(),
each.getName()),
context.getDocumentationContext().getDocumentationType(),
new ParameterBuilder());
return pluginsManager.expandParameter(parameterExpansionContext);
private Predicate<ModelAttributeField> recursiveType(final ExpansionContext context) {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return context.hasSeenType(input.getFieldType());
private Predicate<ModelAttributeField> simpleType() {
return and(not(isCollection()), not(isMap()),
belongsToJavaPackage(),
isBaseType(),
isEnum()));
private Predicate<ModelAttributeField> isCollection() {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return isContainerType(input.getFieldType());
private Predicate<ModelAttributeField> isMap() {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return Maps.isMapType(input.getFieldType());
private Predicate<ModelAttributeField> isEnum() {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return enumTypeDeterminer.isEnum(input.getFieldType().getErasedType());
private Predicate<ModelAttributeField> belongsToJavaPackage() {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return ClassUtils.getPackageName(input.getFieldType().getErasedType()).startsWith("java.lang");
private Predicate<ModelAttributeField> isBaseType() {
return new Predicate<ModelAttributeField>() {
@Override
public boolean apply(ModelAttributeField input) {
return Types.isBaseType(input.getFieldType())
|| input.getFieldType().isPrimitive();
private Function<ResolvedMethod, ModelAttributeField> toModelAttributeField(
final Map<String, ResolvedField> fieldsByName,
final Map<Method, PropertyDescriptor> propertyLookupByGetter,
final AlternateTypeProvider alternateTypeProvider) {
return new Function<ResolvedMethod, ModelAttributeField>() {
@Override
public ModelAttributeField apply(ResolvedMethod input) {
String name = propertyLookupByGetter.get(input.getRawMember()).getName();
return new ModelAttributeField(
fieldType(alternateTypeProvider, input),
name,
input,
fieldsByName.get(name));
private Predicate<ResolvedMethod> onlyValidGetters(final Set<Method> methods) {
return new Predicate<ResolvedMethod>() {
@Override
public boolean apply(ResolvedMethod input) {
return methods.contains(input.getRawMember());
private String nestedParentName(String parentName, ModelAttributeField attribute) {
String name = attribute.getName();
ResolvedType fieldType = attribute.getFieldType();
if (isContainerType(fieldType) && !Types.isBaseType(collectionElementType(fieldType))) {
name += "[0]";
if (isNullOrEmpty(parentName)) {
return name;
return String.format("%s.%s", parentName, name);
private ResolvedType fieldType(AlternateTypeProvider alternateTypeProvider, ResolvedMethod method) {
return alternateTypeProvider.alternateFor(method.getType());
private Set<PropertyDescriptor> propertyDescriptors(final Class<?> clazz) {
try {
Set<PropertyDescriptor> beanProps = new HashSet<>();
PropertyDescriptor[] descriptors = getBeanInfo(clazz).getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
// 增加逻辑,忽略@IgnoreSwaggerParameter注解的字段
Field field = null;
try {
field = clazz.getDeclaredField(descriptor.getName());
} catch (Exception e) {
LOG.debug(String.format("Failed to get bean properties on (%s)", clazz), e);
try {
if (Objects.isNull(field)) {
field = clazz.getSuperclass().getDeclaredField(descriptor.getName());
} catch (Exception e) {
LOG.debug(String.format("Failed to get super bean properties on (%s)", clazz), e);
if (field != null) {
field.setAccessible(true);
IgnoreSwaggerParameter ignoreSwaggerParameter = field.getDeclaredAnnotation(IgnoreSwaggerParameter.class);
if (ignoreSwaggerParameter != null) {
continue;
// 增加结束
if (descriptor.getReadMethod() != null) {
beanProps.add(descriptor);
return beanProps;
} catch (Exception e) {
LOG.warn(String.format("Failed to get bean properties on (%s)", clazz), e);
return newHashSet();
private Map<Method, PropertyDescriptor> propertyDescriptorsByMethod(
final Class<?> clazz,
Set<PropertyDescriptor> propertyDescriptors) {
return FluentIterable.from(propertyDescriptors)
.filter(new Predicate<PropertyDescriptor>() {
@Override
public boolean apply(PropertyDescriptor input) {
return input.getReadMethod() != null
&& !clazz.isAssignableFrom(Collection.class)
&& !"isEmpty".equals(input.getReadMethod().getName());
.uniqueIndex(new Function<PropertyDescriptor, Method>() {
@Override
public Method apply(PropertyDescriptor input) {
return input.getReadMethod();
@VisibleForTesting
BeanInfo getBeanInfo(Class<?> clazz) throws IntrospectionException {
return Introspector.getBeanInfo(clazz);
public static String determineScalarParameterType(Set<? extends MediaType> consumes, HttpMethod method) {
String parameterType = "query";
if (consumes.contains(MediaType.APPLICATION_FORM_URLENCODED)
&& method == HttpMethod.POST) {
parameterType = "form";
} else if (consumes.contains(MediaType.MULTIPART_FORM_DATA)
&& method == HttpMethod.POST) {
parameterType = "formData";
return parameterType;
用到了自定义的IgnoreSwaggerParamer注解
package com.example.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// swagger忽略的参数
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreSwaggerParameter {
使用方式,在不需要递归展开的属性上加上IgnoreSwaggerParameter注解
package com.example.model.po;
import com.example.annotation.IgnoreSwaggerParameter;
import com.example.model.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.List;
@Data
@ApiModel(value = "用户")
public class User extends BaseModel {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户id")
private Integer id;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "生日")
private Date birth;
@ApiModelProperty(value="登录时间")
private Timestamp logintime;
@ApiModelProperty(value = "部门id")
private Integer deptId;
@ApiModelProperty(value = "部门信息")
@IgnoreSwaggerParameter // 在不需要递归展开的属性上加上IgnoreSwaggerParameter注解
private Dept dept;
@ApiModelProperty(value = "角色信息")
@IgnoreSwaggerParameter
private List<Role> roles;
这样就可以自定义隐藏swagger2页面中的参数了。
springboot整合Swagger的方式就不说了,百度上面有很多资源,主要就是Swagger在忽略属性时不生效的解决方法:内容转载来自:https://blog.csdn.net/weixin_44980618/article/details/102883844原内容中已经解决了忽略属性的问题,我增加了忽略父级属性的小操作,希望不断丰富。我用的是2.9.问题:假如接收参数的实...
提问的艺术for CHATGPT prompt 技术工程高质量答案完全指南
《向 ChatGPT 提问获取高质量答案的艺术:Prompt 工程技术完全指南》。
介绍了各种 Prompt 技术的理解和利用,用于从 ChatGPT miki sharing中生成高质量的答案。
我们将探讨如何使用不同的 Prompt 工程技术来实现不同的目标。ChatGPT 是一
种先进的语言模型,能够生成类似人类的文本。然而,了解正确的提问方式以获
取我们所需的高质量输出非常重要。
无论您是普通人、研究人员、开发人员,还是想将 ChatGPT作为您领域的个人助手的人,都是为您编写的。
使用简单的语言和实用的解释,结合每种Prompt技术的示例和Prompt公式。
您将学习如何使用 Prompt 工程技术控制 ChatGPT 的输出,生成符合您特定需求的文本。
还提供了如何结合不同的 Prompt 技术来实现更具体目标的示例。
造成这种错误的原因是父类的pom中<dependecies></dependecies>有springfox的依赖,在这里面的依赖是的所有工程都必须继承这个,因此会导致不需要这个或说没有用到swagger注解的工程,在执行导致上面的类找不到的错误。
解决方法, 是将这个springfox移除<dependecies></dependecies> 放...
@ApiOperation(value = "分页查询登录日志")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "分页参数:当前页", defaultValue = "1", required = false),
@Api...
//方法一:在ApiModelProperty中添加hidden=true
@ApiModelProperty(value = "所属机构", required = false,hidden=true)
private String createUserOrgNo;
//方法二:@JsonIgnore增加@JsonIgnore注解
@ApiModelProperty(value = "自增序列", required = false)
@JsonIgnore
private .
解决swagger显示实体类 不显示mybatisPlus Page类实体
属性的问题 多层泛型自定义返回工具类自定义分页实体类MyPage使用方法显示效果
自定义返回工具类
@ApiModel(descript
ion = "接口出参对象")
@Data
@Accessors(chain = true)
public class ResultDTO<T> implements Serializable {
@ApiModel
Property(value = "是否请求成功")
这里写自定义目录标题记录SpringBoot关于Swagger启动报错
记录SpringBoot关于Swagger启动报错
java.lang.NullPointerException: null
at springfox.documentation.schema.Example.equals(Example.java:131)
at java.util.Objects.equals(Objects.java:59)
at springfox.documentation.service.Request
Error_msg:
AttributeError: Got AttributeError when attempting to get a value for field `password2` on serializer ` UserSerializer`.
The serializer field might be named incorrectly a...
<groupId>io.
springfox</groupId>
<artifactId>
springfox-
swagger2</artifactId>
<vers
ion>2.9.2</vers
ion>
</dependency>
<dependency>
<groupId>io.
springfox</groupId>
<artifactId>
springfox-
swagger-ui</artifactId>
<vers
ion>2.9.2</vers
ion>
</dependency>
2. 配置
Swagger2
在Spring Boot的启动类中添加
Swagger2的配置:
@Configurat
ion
@Enable
Swagger2
public class
SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(
Documentat
ionType.
SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
.paths(PathSelectors.any())
.build();
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot集成
Swagger2")
.descript
ion("Spring Boot集成
Swagger2的示例项目")
.vers
ion("1.")
.build();
其中,`createRestApi()`方法用于创建Docket对象,`apiInfo()`方法用于设置
Swagger2文档的基本信息,`RequestHandlerSelectors.basePackage()`用于指定扫描的包路径,`PathSelectors.any()`用于指定扫描的路径。
3. 测试
Swagger2
启动Spring Boot应用程序后,在浏览器中访问`http://localhost:808/
swagger-ui.html`,即可看到
Swagger2的UI界面,可以在此界面中查看API文档、测试API等。