You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/08/01 09:53:39 UTC

[incubator-servicecomb-java-chassis] 03/06: [SCB-777] refactor ProducerArgumentsMapperFactory and add BeanParamMapper

This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 3590a03b34aae667c00fd12419d6c0667f1c4ebc
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Sat Jul 28 18:28:37 2018 +0800

    [SCB-777] refactor ProducerArgumentsMapperFactory and add BeanParamMapper
---
 swagger/swagger-invocation/invocation-core/pom.xml |   1 -
 .../swagger/engine/SwaggerEnvironment.java         |  26 ++-
 .../arguments/ArgumentsMapperFactory.java          |   4 +-
 .../invocation/arguments/ProviderParameter.java    |  14 ++
 .../JaxRSProducerArgumentsMapperFactory.java       | 147 +++++++++++++
 .../producer/ProducerArgumentsMapperFactory.java   |  96 ++++-----
 .../producer/ProducerBeanParamMapper.java          |  62 ++++++
 .../SpringMVCProducerArgumentsMapperFactory.java   |  94 +++++++++
 .../servicecomb/engine/TestSwaggerEnvironment.java |  77 -------
 .../swagger/engine/TestSwaggerEnvironment.java     | 121 +++++++++++
 .../JaxRSProducerArgumentsMapperFactoryTest.java   | 229 +++++++++++++++++++++
 11 files changed, 730 insertions(+), 141 deletions(-)

diff --git a/swagger/swagger-invocation/invocation-core/pom.xml b/swagger/swagger-invocation/invocation-core/pom.xml
index b1b2673..864ce08 100644
--- a/swagger/swagger-invocation/invocation-core/pom.xml
+++ b/swagger/swagger-invocation/invocation-core/pom.xml
@@ -35,7 +35,6 @@
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>swagger-generator-jaxrs</artifactId>
-      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java
index e0ba98f..42852c1 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java
@@ -17,7 +17,9 @@
 package org.apache.servicecomb.swagger.engine;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import javax.inject.Inject;
@@ -39,6 +41,7 @@ import org.apache.servicecomb.swagger.invocation.response.producer.ProducerRespo
 import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import io.swagger.annotations.ApiOperation;
@@ -51,9 +54,15 @@ public class SwaggerEnvironment {
   @Inject
   protected CompositeSwaggerGeneratorContext compositeSwaggerGeneratorContext;
 
+  /**
+   * default producerArgumentsFactory
+   */
   @Inject
   private ProducerArgumentsMapperFactory producerArgumentsFactory;
 
+  @Autowired
+  private List<ProducerArgumentsMapperFactory> producerArgumentsMapperFactoryList = new ArrayList<>(0);
+
   private ResponseMapperFactorys<ProducerResponseMapper> producerResponseMapperFactorys =
       new ResponseMapperFactorys<>(ProducerResponseMapperFactory.class);
 
@@ -174,7 +183,8 @@ public class SwaggerEnvironment {
       config.setSwaggerOperation(swaggerOperationMap.get(methodName));
       config.setSwaggerGeneratorContext(compositeSwaggerGeneratorContext.selectContext(producerCls));
 
-      ProducerArgumentsMapper argsMapper = producerArgumentsFactory.createArgumentsMapper(config);
+      ProducerArgumentsMapperFactory argumentsMapperFactory = selectProducerArgumentsMapperFactory(config);
+      ProducerArgumentsMapper argsMapper = argumentsMapperFactory.createArgumentsMapper(config);
       ProducerResponseMapper responseMapper = producerResponseMapperFactorys.createResponseMapper(
           swaggerMethod.getGenericReturnType(),
           producerMethod.getGenericReturnType());
@@ -194,6 +204,20 @@ public class SwaggerEnvironment {
     return producer;
   }
 
+  ProducerArgumentsMapperFactory selectProducerArgumentsMapperFactory(ArgumentsMapperConfig config) {
+    ProducerArgumentsMapperFactory argumentsMapperFactory = null;
+    for (ProducerArgumentsMapperFactory producerArgumentsMapperFactory : this.producerArgumentsMapperFactoryList) {
+      if (producerArgumentsMapperFactory.canProcess(config)) {
+        argumentsMapperFactory = producerArgumentsMapperFactory;
+        break;
+      }
+    }
+    if (null == argumentsMapperFactory) {
+      argumentsMapperFactory = this.producerArgumentsFactory;
+    }
+    return argumentsMapperFactory;
+  }
+
   private Map<String, Method> retrieveVisibleMethods(Class<?> clazz) {
     Map<String, Method> visibleMethods = new HashMap<>();
     for (Method method : clazz.getMethods()) {
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java
index 3d86a49..001f808 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java
@@ -125,6 +125,7 @@ public abstract class ArgumentsMapperFactory<T> {
   protected List<ProviderParameter> collectContextArgumentsMapper(ArgumentsMapperConfig config) {
     List<ProviderParameter> providerNormalParams = new ArrayList<>();
 
+    final Annotation[][] parameterAnnotations = config.getProviderMethod().getParameterAnnotations();
     Type[] providerParameterTypes = config.getProviderMethod().getGenericParameterTypes();
     for (int providerIdx = 0; providerIdx < providerParameterTypes.length; providerIdx++) {
       Type parameterType = providerParameterTypes[providerIdx];
@@ -136,7 +137,8 @@ public abstract class ArgumentsMapperFactory<T> {
       }
 
       ProviderParameter pp = new ProviderParameter(providerIdx, parameterType,
-          retrieveVisibleParamName(config.getProviderMethod(), providerIdx));
+          retrieveVisibleParamName(config.getProviderMethod(), providerIdx))
+          .setAnnotations(parameterAnnotations[providerIdx]);
       providerNormalParams.add(pp);
     }
 
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ProviderParameter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ProviderParameter.java
index 299c02f..988efc1 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ProviderParameter.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ProviderParameter.java
@@ -16,7 +16,9 @@
  */
 package org.apache.servicecomb.swagger.invocation.arguments;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.Arrays;
 
 public class ProviderParameter {
   private int index;
@@ -28,6 +30,8 @@ public class ProviderParameter {
    */
   private String name;
 
+  private Annotation[] annotations;
+
   public ProviderParameter(int index, Type type, String name) {
     this.index = index;
     this.type = type;
@@ -61,12 +65,22 @@ public class ProviderParameter {
     return this;
   }
 
+  public Annotation[] getAnnotations() {
+    return annotations;
+  }
+
+  public ProviderParameter setAnnotations(Annotation[] annotations) {
+    this.annotations = annotations;
+    return this;
+  }
+
   @Override
   public String toString() {
     final StringBuilder sb = new StringBuilder("ProviderParameter{");
     sb.append("index=").append(index);
     sb.append(", type=").append(type);
     sb.append(", name='").append(name).append('\'');
+    sb.append(", annotations=").append(Arrays.toString(annotations));
     sb.append('}');
     return sb.toString();
   }
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/JaxRSProducerArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/JaxRSProducerArgumentsMapperFactory.java
new file mode 100644
index 0000000..d0b84cd
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/JaxRSProducerArgumentsMapperFactory.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.swagger.invocation.arguments.producer;
+
+import static org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.BeanParamAnnotationProcessor.SETTER_METHOD_PREFIX;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.ws.rs.BeanParam;
+
+import org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.BeanParamAnnotationProcessor;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig;
+import org.apache.servicecomb.swagger.invocation.arguments.ProviderParameter;
+import org.springframework.stereotype.Component;
+
+import io.swagger.models.parameters.Parameter;
+
+@Component
+public class JaxRSProducerArgumentsMapperFactory extends ProducerArgumentsMapperFactory {
+  @Override
+  public boolean canProcess(ArgumentsMapperConfig config) {
+    return config.getSwaggerGeneratorContext().getClass().getCanonicalName().equals(
+        "org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorContext");
+  }
+
+  @Override
+  protected Set<String> findAggregatedParamNames(Map<String, ProviderParameter> providerParamMap,
+      Map<String, ParamWrapper<Parameter>> swaggerParamMap) {
+    Set<String> aggregatedParamNames = new HashSet<>();
+    for (Entry<String, ProviderParameter> providerParameterEntry : providerParamMap.entrySet()) {
+      if (null == providerParameterEntry.getValue().getAnnotations()) {
+        // request body may have no annotation
+        continue;
+      }
+      for (Annotation annotation : providerParameterEntry.getValue().getAnnotations()) {
+        if (annotation instanceof BeanParam) {
+          aggregatedParamNames.add(providerParameterEntry.getKey());
+        }
+      }
+    }
+
+    return aggregatedParamNames;
+  }
+
+  @Override
+  protected void generateAggregatedParamMapper(ArgumentsMapperConfig config,
+      Map<String, ProviderParameter> providerParamMap, Map<String, ParamWrapper<Parameter>> swaggerParamMap,
+      Set<String> aggregatedParamNames) {
+    for (String aggregatedProducerParamName : aggregatedParamNames) {
+      ProviderParameter aggregatedParam = providerParamMap.get(aggregatedProducerParamName);
+      // producer param name -> swagger param name
+      Map<String, String> producerToSwaggerParamNameMapper = getProducerToSwaggerParamNameMap(aggregatedParam);
+      // producer param name -> swagger param index
+      Map<String, Integer> producerNameToSwaggerIndexMap = new HashMap<>(producerToSwaggerParamNameMapper.size());
+      for (Entry<String, String> producerSwaggerNameMapEntry : producerToSwaggerParamNameMapper.entrySet()) {
+        producerNameToSwaggerIndexMap.put(
+            producerSwaggerNameMapEntry.getKey(),
+            swaggerParamMap.get(producerSwaggerNameMapEntry.getValue()).getIndex());
+      }
+
+      // create&add aggregated param mapper
+      ArgumentMapper mapper = new ProducerBeanParamMapper(producerNameToSwaggerIndexMap,
+          aggregatedParam.getIndex(),
+          aggregatedParam.getType());
+      config.addArgumentMapper(mapper);
+    }
+  }
+
+  /**
+   * <pre>
+   * public class AggregatedParam {
+   *   \@PathParam("pathSwaggerParam")
+   *   private String pathProducerParam;
+   *
+   *   private String queryProducerParam;
+   *
+   *   \@QueryParam(value = "querySwaggerParam")
+   *   public void setQueryProducerParam(String queryParam) {
+   *     this.queryProducerParam = queryParam;
+   *   }
+   * }
+   * </pre>
+   * Given a BeanParam like above, will return a map like below:
+   * {
+   *   "pathProducerParam" -> "pathSwaggerParam",
+   *   "queryProducerParam" -> "querySwaggerParam"
+   * }
+   */
+  private Map<String, String> getProducerToSwaggerParamNameMap(ProviderParameter aggregatedParam) {
+    Map<String, String> producerToSwaggerParamNameMapper = new HashMap<>();
+    Class<?> aggregatedParamClazz = (Class<?>) aggregatedParam.getType();
+    // map those params defined by BeanParam fields
+    for (Field field : aggregatedParamClazz.getDeclaredFields()) {
+      for (Annotation fieldAnnotation : field.getAnnotations()) {
+        if (BeanParamAnnotationProcessor.SUPPORTED_PARAM_ANNOTATIONS.contains(fieldAnnotation.annotationType())) {
+          producerToSwaggerParamNameMapper.put(
+              field.getName(),
+              retrieveVisibleParamName(fieldAnnotation));
+          break;
+        }
+      }
+    }
+    // map those params defined by setter methods
+    for (Method method : aggregatedParamClazz.getDeclaredMethods()) {
+      final String methodName = method.getName();
+      if (!methodName.startsWith(SETTER_METHOD_PREFIX)) {
+        // only process setter methods
+        continue;
+      }
+      // There should be one and only one param in a setter method
+      for (Annotation setterAnnotation : method.getAnnotations()) {
+        if (BeanParamAnnotationProcessor.SUPPORTED_PARAM_ANNOTATIONS.contains(setterAnnotation.annotationType())) {
+          producerToSwaggerParamNameMapper.put(
+              methodName.substring( // setParamName() -> "paramName"
+                  SETTER_METHOD_PREFIX.length(), SETTER_METHOD_PREFIX.length() + 1).toLowerCase()
+                  + methodName.substring(SETTER_METHOD_PREFIX.length() + 1),
+              retrieveVisibleParamName(setterAnnotation));
+          break;
+        }
+      }
+    }
+    return producerToSwaggerParamNameMapper;
+  }
+}
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java
index 204dea9..0a59aa6 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java
@@ -18,8 +18,8 @@
 package org.apache.servicecomb.swagger.invocation.arguments.producer;
 
 import java.lang.reflect.Type;
+import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -27,7 +27,7 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
-import org.apache.servicecomb.swagger.generator.core.utils.ParamUtils;
+import org.apache.servicecomb.swagger.generator.rest.RestSwaggerGeneratorContext;
 import org.apache.servicecomb.swagger.invocation.InvocationType;
 import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper;
 import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig;
@@ -39,16 +39,15 @@ import org.apache.servicecomb.swagger.invocation.converter.Converter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
 import org.springframework.stereotype.Component;
 
-import io.swagger.converter.ModelConverters;
 import io.swagger.models.parameters.Parameter;
-import io.swagger.models.parameters.QueryParameter;
-import io.swagger.models.properties.Property;
-import io.swagger.models.properties.RefProperty;
 
 @Component
+@Primary
 public class ProducerArgumentsMapperFactory extends ArgumentsMapperFactory<ProducerArgumentsMapper> {
+
   private static final Logger LOGGER = LoggerFactory.getLogger(ProducerArgumentsMapperFactory.class);
 
   public ProducerArgumentsMapperFactory() {
@@ -69,46 +68,50 @@ public class ProducerArgumentsMapperFactory extends ArgumentsMapperFactory<Produ
         config.getProviderMethod().getParameterCount());
   }
 
+  public boolean canProcess(ArgumentsMapperConfig config) {
+    return false;
+  }
+
   @Override
   protected void collectSwaggerArgumentsMapper(ArgumentsMapperConfig config,
       List<ProviderParameter> providerNormalParams) {
-    if (!config.getSwaggerGeneratorContext().getClass().getCanonicalName().equals(
-        "org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGeneratorContext")) {
-      // if this is not a SpringMVC style provider operation, there is no need to consider query object param
+    if (!RestSwaggerGeneratorContext.class.isInstance(config.getSwaggerGeneratorContext())) {
+      // POJO style provider does not support aggregated param
       super.collectSwaggerArgumentsMapper(config, providerNormalParams);
       return;
     }
 
+    // JAX-RS and SpringMVC style provider support aggregated param, i.e. @BeanParam and query object, respectively
     Map<String, ProviderParameter> providerParamMap = getProviderParamMap(providerNormalParams);
     Map<String, ParamWrapper<Parameter>> swaggerParamMap = getSwaggerParamMap(config);
 
-    Set<String> queryObjectNames = findSpringMvcQueryObject(providerParamMap, swaggerParamMap);
-    if (queryObjectNames.isEmpty()) {
-      // there is no query object param, run as 1-to-1 param mapping mode
+    Set<String> aggregatedParamNames = findAggregatedParamNames(providerParamMap, swaggerParamMap);
+    if (aggregatedParamNames.isEmpty()) {
+      // there is no aggregated param, run as 1-to-1 param mapping mode
       super.collectSwaggerArgumentsMapper(config, providerNormalParams);
       return;
     }
 
-    // There is at lease one query object param, so the param mapping mode becomes to M-to-N
+    // There is at lease one aggregated param, so the param mapping mode becomes to M-to-N
     // try to map params by name
-    generateParamMapperByName(config, providerParamMap, swaggerParamMap, queryObjectNames);
+    generateParamMapperByName(config, providerParamMap, swaggerParamMap, aggregatedParamNames);
   }
 
   private void generateParamMapperByName(ArgumentsMapperConfig config, Map<String, ProviderParameter> providerParamMap,
-      Map<String, ParamWrapper<Parameter>> swaggerParamMap, Set<String> queryObjectNames) {
-    LOGGER.info("mapping query object params: [{}]", queryObjectNames);
-    generateObjectQueryParamMapper(config, providerParamMap, swaggerParamMap, queryObjectNames);
-    generateDefaultParamMapper(config, providerParamMap, swaggerParamMap, queryObjectNames);
+      Map<String, ParamWrapper<Parameter>> swaggerParamMap, Set<String> aggregatedParamNames) {
+    LOGGER.info("mapping aggregated params: [{}]", aggregatedParamNames);
+    generateAggregatedParamMapper(config, providerParamMap, swaggerParamMap, aggregatedParamNames);
+    generateDefaultParamMapper(config, providerParamMap, swaggerParamMap, aggregatedParamNames);
   }
 
   /**
    * Generate default argument mappers. One swagger argument is mapped to one producer argument.
    */
   private void generateDefaultParamMapper(ArgumentsMapperConfig config, Map<String, ProviderParameter> providerParamMap,
-      Map<String, ParamWrapper<Parameter>> swaggerParamMap, Set<String> queryObjectNames) {
+      Map<String, ParamWrapper<Parameter>> swaggerParamMap, Set<String> aggregatedParamNames) {
     Type[] swaggerParamTypes = config.getSwaggerMethod().getGenericParameterTypes();
     for (Entry<String, ProviderParameter> providerParamEntry : providerParamMap.entrySet()) {
-      if (queryObjectNames.contains(providerParamEntry.getKey())) {
+      if (aggregatedParamNames.contains(providerParamEntry.getKey())) {
         continue;
       }
 
@@ -122,29 +125,17 @@ public class ProducerArgumentsMapperFactory extends ArgumentsMapperFactory<Produ
   }
 
   /**
-   * Generate argument mappers for query object params. Collect all query params as json and map them to object param.
+   * Generate argument mappers for aggregated params.
+   * Collect related swagger params and map them to an aggregated param.
+   * It's implemented by SpringMVC and JAX-RS.
    */
-  private void generateObjectQueryParamMapper(ArgumentsMapperConfig config,
+  protected void generateAggregatedParamMapper(ArgumentsMapperConfig config,
       Map<String, ProviderParameter> providerParamMap, Map<String, ParamWrapper<Parameter>> swaggerParamMap,
-      Set<String> queryObjectNames) {
-    // collect all query params
-    Map<String, Integer> querySwaggerParamsIndex = new HashMap<>();
-    for (Entry<String, ParamWrapper<Parameter>> wrapperEntry : swaggerParamMap.entrySet()) {
-      if (wrapperEntry.getValue().getParam() instanceof QueryParameter) {
-        querySwaggerParamsIndex.put(wrapperEntry.getKey(), wrapperEntry.getValue().getIndex());
-      }
-    }
-    // create mapper for each query objects
-    for (String queryObjectName : queryObjectNames) {
-      final ProviderParameter providerParameter = providerParamMap.get(queryObjectName);
-      ArgumentMapper mapper = new ProducerSpringMVCQueryObjectMapper(querySwaggerParamsIndex,
-          providerParameter.getIndex(),
-          providerParameter.getType());
-      config.addArgumentMapper(mapper);
-    }
+      Set<String> aggregatedParamNames) {
+    // do nothing, not supported by default
   }
 
-  private Map<String, ParamWrapper<Parameter>> getSwaggerParamMap(ArgumentsMapperConfig config) {
+  protected Map<String, ParamWrapper<Parameter>> getSwaggerParamMap(ArgumentsMapperConfig config) {
     Map<String, ParamWrapper<Parameter>> swaggerParamMap =
         new HashMap<>(config.getSwaggerOperation().getParameters().size());
     List<Parameter> parameters = config.getSwaggerOperation().getParameters();
@@ -155,7 +146,7 @@ public class ProducerArgumentsMapperFactory extends ArgumentsMapperFactory<Produ
     return swaggerParamMap;
   }
 
-  private Map<String, ProviderParameter> getProviderParamMap(List<ProviderParameter> providerNormalParams) {
+  protected Map<String, ProviderParameter> getProviderParamMap(List<ProviderParameter> providerNormalParams) {
     Map<String, ProviderParameter> providerParamMap = new HashMap<>(providerNormalParams.size());
     providerNormalParams.forEach(
         providerParameter -> providerParamMap.put(providerParameter.getName(), providerParameter));
@@ -163,29 +154,12 @@ public class ProducerArgumentsMapperFactory extends ArgumentsMapperFactory<Produ
   }
 
   /**
-   * Find all query object params
-   * @return the names of the query object params
+   * Find all aggregated params
+   * @return the names of the aggregated params
    */
-  private Set<String> findSpringMvcQueryObject(Map<String, ProviderParameter> providerParamMap,
+  protected Set<String> findAggregatedParamNames(Map<String, ProviderParameter> providerParamMap,
       Map<String, ParamWrapper<Parameter>> swaggerParamMap) {
-    // find all reference type producer params, and exclude body param
-    Set<String> queryObjectSet = new HashSet<>();
-
-    for (Entry<String, ProviderParameter> paramEntry : providerParamMap.entrySet()) {
-      Type paramType = paramEntry.getValue().getType();
-      Property property = ModelConverters.getInstance().readAsProperty(paramType);
-      if (RefProperty.class.isInstance(property)) {
-        queryObjectSet.add(paramEntry.getKey());
-      }
-    }
-
-    for (Entry<String, ParamWrapper<Parameter>> paramEntry : swaggerParamMap.entrySet()) {
-      if (ParamUtils.isRealBodyParameter(paramEntry.getValue().getParam())) {
-        queryObjectSet.remove(paramEntry.getKey());
-      }
-    }
-
-    return queryObjectSet;
+    return Collections.emptySet();
   }
 
   @Override
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.java
new file mode 100644
index 0000000..b72b7c9
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.swagger.invocation.arguments.producer;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.servicecomb.swagger.invocation.SwaggerInvocation;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper;
+import org.apache.servicecomb.swagger.invocation.converter.Converter;
+import org.apache.servicecomb.swagger.invocation.converter.impl.ConverterCommon;
+
+public class ProducerBeanParamMapper implements ArgumentMapper {
+
+  private int producerIdx;
+
+  private Map<String, Integer> swaggerParamIndexMap;
+
+  private Converter converter;
+
+  /**
+   * @param producerNameToSwaggerIndexMap name of the fields & setters defined in @BeanParam parameter -> swagger param index
+   * @param producerIdx index of producer param
+   * @param producerParamType type of producer param
+   */
+  public ProducerBeanParamMapper(Map<String, Integer> producerNameToSwaggerIndexMap, int producerIdx,
+      Type producerParamType) {
+    this.producerIdx = producerIdx;
+    this.swaggerParamIndexMap = new HashMap<>();
+    this.swaggerParamIndexMap.putAll(producerNameToSwaggerIndexMap);
+    converter = new ConverterCommon(producerParamType);
+  }
+
+  @Override
+  public void mapArgument(SwaggerInvocation invocation, Object[] producerArguments) {
+    Map<String, Object> jsonMap = new HashMap<>(swaggerParamIndexMap.size());
+
+    for (Entry<String, Integer> swaggerIndexEntry : swaggerParamIndexMap.entrySet()) {
+      jsonMap.put(swaggerIndexEntry.getKey(), invocation.getSwaggerArgument(swaggerIndexEntry.getValue()));
+    }
+
+    final Object producerParam = converter.convert(jsonMap);
+    producerArguments[producerIdx] = producerParam;
+  }
+}
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SpringMVCProducerArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SpringMVCProducerArgumentsMapperFactory.java
new file mode 100644
index 0000000..f7d1eda
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SpringMVCProducerArgumentsMapperFactory.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.swagger.invocation.arguments.producer;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.servicecomb.swagger.generator.core.utils.ParamUtils;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig;
+import org.apache.servicecomb.swagger.invocation.arguments.ProviderParameter;
+import org.springframework.stereotype.Component;
+
+import io.swagger.converter.ModelConverters;
+import io.swagger.models.parameters.Parameter;
+import io.swagger.models.parameters.QueryParameter;
+import io.swagger.models.properties.Property;
+import io.swagger.models.properties.RefProperty;
+
+@Component
+public class SpringMVCProducerArgumentsMapperFactory extends ProducerArgumentsMapperFactory {
+
+  @Override
+  public boolean canProcess(ArgumentsMapperConfig config) {
+    return config.getSwaggerGeneratorContext().getClass().getCanonicalName().equals(
+        "org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGeneratorContext");
+  }
+
+  /**
+   * Find all query object params
+   * @return the names of the query object params
+   */
+  @Override
+  protected Set<String> findAggregatedParamNames(Map<String, ProviderParameter> providerParamMap,
+      Map<String, ParamWrapper<Parameter>> swaggerParamMap) {
+    // find all reference type producer params, and exclude body param
+    Set<String> queryObjectSet = new HashSet<>();
+
+    for (Entry<String, ProviderParameter> paramEntry : providerParamMap.entrySet()) {
+      Type paramType = paramEntry.getValue().getType();
+      Property property = ModelConverters.getInstance().readAsProperty(paramType);
+      if (RefProperty.class.isInstance(property)) {
+        queryObjectSet.add(paramEntry.getKey());
+      }
+    }
+
+    for (Entry<String, ParamWrapper<Parameter>> paramEntry : swaggerParamMap.entrySet()) {
+      if (ParamUtils.isRealBodyParameter(paramEntry.getValue().getParam())) {
+        queryObjectSet.remove(paramEntry.getKey());
+      }
+    }
+
+    return queryObjectSet;
+  }
+
+  protected void generateAggregatedParamMapper(ArgumentsMapperConfig config,
+      Map<String, ProviderParameter> providerParamMap, Map<String, ParamWrapper<Parameter>> swaggerParamMap,
+      Set<String> aggregatedParamNames) {
+    // collect all query params
+    Map<String, Integer> querySwaggerParamsIndex = new HashMap<>();
+    for (Entry<String, ParamWrapper<Parameter>> wrapperEntry : swaggerParamMap.entrySet()) {
+      if (wrapperEntry.getValue().getParam() instanceof QueryParameter) {
+        querySwaggerParamsIndex.put(wrapperEntry.getKey(), wrapperEntry.getValue().getIndex());
+      }
+    }
+    // create mapper for each query objects
+    for (String queryObjectName : aggregatedParamNames) {
+      final ProviderParameter providerParameter = providerParamMap.get(queryObjectName);
+      ArgumentMapper mapper = new ProducerSpringMVCQueryObjectMapper(querySwaggerParamsIndex,
+          providerParameter.getIndex(),
+          providerParameter.getType());
+      config.addArgumentMapper(mapper);
+    }
+  }
+}
diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/engine/TestSwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/engine/TestSwaggerEnvironment.java
deleted file mode 100644
index 82c6ad4..0000000
--- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/engine/TestSwaggerEnvironment.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.engine;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.hamcrest.core.IsNull.nullValue;
-import static org.junit.Assert.assertThat;
-
-import org.apache.servicecomb.common.javassist.JavassistUtils;
-import org.apache.servicecomb.swagger.engine.SwaggerConsumer;
-import org.apache.servicecomb.swagger.engine.SwaggerProducer;
-import org.apache.servicecomb.swagger.invocation.models.ProducerImpl;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class TestSwaggerEnvironment {
-  private static SwaggerEnvironmentForTest env = new SwaggerEnvironmentForTest();
-
-  private static SwaggerProducer producer;
-
-  @BeforeClass
-  public static void init() {
-    producer = env.createProducer(new ProducerImpl());
-  }
-
-  @AfterClass
-  public static void tearDown() {
-    JavassistUtils.clearByClassLoader(env.getClassLoader());
-  }
-
-  @Test
-  public void ableToFindVisibleMethod() {
-    assertThat(producer.findOperation("visibleMethod"), is(notNullValue()));
-  }
-
-  @Test
-  public void unableToFindHiddenMethod() {
-    assertThat(producer.findOperation("hiddenMethod"), is(nullValue()));
-  }
-
-  interface ConsumerIntf {
-    void exist();
-
-    void notExist();
-  }
-
-  interface ContractIntf {
-    void exist();
-  }
-
-  @Test
-  public void createConsumer_consumerMethodSetBigger() {
-    SwaggerConsumer swaggerConsumer = env.getSwaggerEnvironment()
-        .createConsumer(ConsumerIntf.class, ContractIntf.class);
-
-    Assert.assertNotNull(swaggerConsumer.findOperation("exist"));
-    Assert.assertNull(swaggerConsumer.findOperation("notExist"));
-  }
-}
diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.java
new file mode 100644
index 0000000..0a888cc
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.swagger.engine;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.servicecomb.common.javassist.JavassistUtils;
+import org.apache.servicecomb.engine.SwaggerEnvironmentForTest;
+import org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorContext;
+import org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig;
+import org.apache.servicecomb.swagger.invocation.arguments.producer.JaxRSProducerArgumentsMapperFactory;
+import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperFactory;
+import org.apache.servicecomb.swagger.invocation.arguments.producer.SpringMVCProducerArgumentsMapperFactory;
+import org.apache.servicecomb.swagger.invocation.models.ProducerImpl;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import mockit.Deencapsulation;
+
+public class TestSwaggerEnvironment {
+  private static SwaggerEnvironmentForTest env = new SwaggerEnvironmentForTest();
+
+  private static SwaggerProducer producer;
+
+  private static ProducerArgumentsMapperFactory defaultProducerArgumentsMapperFactory;
+
+  @BeforeClass
+  public static void init() {
+    producer = env.createProducer(new ProducerImpl());
+    defaultProducerArgumentsMapperFactory = new ProducerArgumentsMapperFactory();
+    List<ProducerArgumentsMapperFactory> producerArgumentsMapperFactoryList = new ArrayList<>();
+    producerArgumentsMapperFactoryList.add(new JaxRSProducerArgumentsMapperFactory());
+    producerArgumentsMapperFactoryList.add(new SpringMVCProducerArgumentsMapperFactory());
+    producerArgumentsMapperFactoryList.add(new ProducerArgumentsMapperFactory());
+
+    Deencapsulation.setField(
+        env.getSwaggerEnvironment(), "producerArgumentsFactory", defaultProducerArgumentsMapperFactory);
+    Deencapsulation.setField(
+        env.getSwaggerEnvironment(), "producerArgumentsMapperFactoryList", producerArgumentsMapperFactoryList);
+  }
+
+  @AfterClass
+  public static void tearDown() {
+    JavassistUtils.clearByClassLoader(env.getClassLoader());
+  }
+
+  @Test
+  public void ableToFindVisibleMethod() {
+    assertThat(producer.findOperation("visibleMethod"), is(notNullValue()));
+  }
+
+  @Test
+  public void unableToFindHiddenMethod() {
+    assertThat(producer.findOperation("hiddenMethod"), is(nullValue()));
+  }
+
+  interface ConsumerIntf {
+    void exist();
+
+    void notExist();
+  }
+
+  interface ContractIntf {
+    void exist();
+  }
+
+  @Test
+  public void createConsumer_consumerMethodSetBigger() {
+    SwaggerConsumer swaggerConsumer = env.getSwaggerEnvironment()
+        .createConsumer(ConsumerIntf.class, ContractIntf.class);
+
+    Assert.assertNotNull(swaggerConsumer.findOperation("exist"));
+    Assert.assertNull(swaggerConsumer.findOperation("notExist"));
+  }
+
+  @Test
+  public void selectProducerArgumentsMapperFactory() {
+    final ArgumentsMapperConfig config = new ArgumentsMapperConfig();
+    config.setSwaggerGeneratorContext(new JaxrsSwaggerGeneratorContext());
+
+    final ProducerArgumentsMapperFactory producerArgumentsMapperFactory = env.getSwaggerEnvironment()
+        .selectProducerArgumentsMapperFactory(config);
+
+    Assert.assertEquals(JaxRSProducerArgumentsMapperFactory.class, producerArgumentsMapperFactory.getClass());
+  }
+
+  @Test
+  public void selectProducerArgumentsMapperFactoryOnReturnDefault() {
+    final ArgumentsMapperConfig config = new ArgumentsMapperConfig();
+    config.setSwaggerGeneratorContext(new PojoSwaggerGeneratorContext());
+
+    final ProducerArgumentsMapperFactory producerArgumentsMapperFactory = env.getSwaggerEnvironment()
+        .selectProducerArgumentsMapperFactory(config);
+
+    Assert.assertSame(defaultProducerArgumentsMapperFactory, producerArgumentsMapperFactory);
+  }
+}
diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/JaxRSProducerArgumentsMapperFactoryTest.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/JaxRSProducerArgumentsMapperFactoryTest.java
new file mode 100644
index 0000000..7519203
--- /dev/null
+++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/JaxRSProducerArgumentsMapperFactoryTest.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.swagger.invocation.arguments.producer;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.Part;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+import org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorContext;
+import org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext;
+import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig;
+import org.apache.servicecomb.swagger.invocation.arguments.ProviderParameter;
+import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperFactory.ParamWrapper;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+import io.swagger.models.parameters.CookieParameter;
+import io.swagger.models.parameters.FormParameter;
+import io.swagger.models.parameters.HeaderParameter;
+import io.swagger.models.parameters.Parameter;
+import io.swagger.models.parameters.PathParameter;
+import io.swagger.models.parameters.QueryParameter;
+import mockit.Deencapsulation;
+
+public class JaxRSProducerArgumentsMapperFactoryTest {
+
+  private final JaxRSProducerArgumentsMapperFactory producerArgumentsMapperFactory = new JaxRSProducerArgumentsMapperFactory();
+
+  @Test
+  public void canProcess() {
+    ArgumentsMapperConfig argumentsMapperConfig = new ArgumentsMapperConfig();
+
+    argumentsMapperConfig.setSwaggerGeneratorContext(new JaxrsSwaggerGeneratorContext());
+    Assert.assertTrue(producerArgumentsMapperFactory.canProcess(argumentsMapperConfig));
+
+    argumentsMapperConfig.setSwaggerGeneratorContext(new PojoSwaggerGeneratorContext());
+    Assert.assertFalse(producerArgumentsMapperFactory.canProcess(argumentsMapperConfig));
+  }
+
+  @Test
+  public void findAggregatedParamNames() throws NoSuchMethodException {
+    Map<String, ProviderParameter> providerParamMap = new HashMap<>();
+    providerParamMap.put("qqq", new ProviderParameter(1, String.class, "qqq"));
+    Method aggregatedTestParamMethod = AggregatedParamProvider.class
+        .getMethod("aggregatedParamTest", AggregatedTestParam.class);
+    Annotation beanParamAnnotation = aggregatedTestParamMethod.getParameterAnnotations()[0][0];
+    providerParamMap.put("aggregatedTestParam",
+        new ProviderParameter(0, AggregatedTestParam.class, "aggregatedTestParam")
+            .setAnnotations(new Annotation[] {beanParamAnnotation}));
+
+    Map<String, ParamWrapper<Parameter>> swaggerParamMap = new HashMap<>();
+    Parameter swaggerParam = new PathParameter().name("pathSwaggerParam");
+    swaggerParamMap.put("pathSwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(0));
+    swaggerParam = new HeaderParameter().name("headerSwaggerParam");
+    swaggerParamMap.put("headerSwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(1));
+    swaggerParam = new CookieParameter().name("intSwaggerVal");
+    swaggerParamMap.put("intSwaggerVal", new ParamWrapper<>(swaggerParam).setIndex(2));
+    swaggerParam = new FormParameter().name("longSwaggerVal");
+    swaggerParamMap.put("longSwaggerVal", new ParamWrapper<>(swaggerParam).setIndex(3));
+    swaggerParam = new QueryParameter().name("querySwaggerParam");
+    swaggerParamMap.put("querySwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(4));
+    swaggerParam = new FormParameter().name("uploadSwaggerParam");
+    swaggerParamMap.put("uploadSwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(5));
+    swaggerParam = new QueryParameter().name("qqq");
+    swaggerParamMap.put("qqq", new ParamWrapper<>(swaggerParam).setIndex(6));
+
+    Set<String> aggregatedParamNames = producerArgumentsMapperFactory
+        .findAggregatedParamNames(providerParamMap, swaggerParamMap);
+    Assert.assertThat(aggregatedParamNames, Matchers.contains("aggregatedTestParam"));
+  }
+
+  @Test
+  public void generateAggregatedParamMapper() throws NoSuchMethodException {
+    Map<String, ProviderParameter> providerParamMap = new HashMap<>();
+    providerParamMap.put("qqq", new ProviderParameter(1, String.class, "qqq"));
+    Method aggregatedTestParamMethod = AggregatedParamProvider.class
+        .getMethod("aggregatedParamTest", AggregatedTestParam.class);
+    Annotation beanParamAnnotation = aggregatedTestParamMethod.getParameterAnnotations()[0][0];
+    providerParamMap.put("aggregatedTestParam",
+        new ProviderParameter(0, AggregatedTestParam.class, "aggregatedTestParam")
+            .setAnnotations(new Annotation[] {beanParamAnnotation}));
+
+    Map<String, ParamWrapper<Parameter>> swaggerParamMap = new HashMap<>();
+    Parameter swaggerParam = new PathParameter().name("pathSwaggerParam");
+    swaggerParamMap.put("pathSwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(0));
+    swaggerParam = new HeaderParameter().name("headerSwaggerParam");
+    swaggerParamMap.put("headerSwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(1));
+    swaggerParam = new CookieParameter().name("intSwaggerVal");
+    swaggerParamMap.put("intSwaggerVal", new ParamWrapper<>(swaggerParam).setIndex(2));
+    swaggerParam = new FormParameter().name("longSwaggerVal");
+    swaggerParamMap.put("longSwaggerVal", new ParamWrapper<>(swaggerParam).setIndex(3));
+    swaggerParam = new QueryParameter().name("querySwaggerParam");
+    swaggerParamMap.put("querySwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(4));
+    swaggerParam = new FormParameter().name("uploadSwaggerParam");
+    swaggerParamMap.put("uploadSwaggerParam", new ParamWrapper<>(swaggerParam).setIndex(5));
+    swaggerParam = new QueryParameter().name("qqq");
+    swaggerParamMap.put("qqq", new ParamWrapper<>(swaggerParam).setIndex(6));
+
+    Set<String> aggregatedParamNames = new HashSet<>();
+    aggregatedParamNames.add("aggregatedTestParam");
+    ArgumentsMapperConfig argumentsMapperConfig = new ArgumentsMapperConfig();
+    producerArgumentsMapperFactory.generateAggregatedParamMapper(
+        argumentsMapperConfig, providerParamMap, swaggerParamMap, aggregatedParamNames);
+
+    Assert.assertEquals(1, argumentsMapperConfig.getArgumentMapperList().size());
+    Assert.assertEquals(ProducerBeanParamMapper.class, argumentsMapperConfig.getArgumentMapperList().get(0).getClass());
+    ProducerBeanParamMapper producerBeanParamMapper =
+        (ProducerBeanParamMapper) argumentsMapperConfig.getArgumentMapperList().get(0);
+    Assert.assertEquals(Integer.valueOf(0), Deencapsulation.getField(producerBeanParamMapper, "producerIdx"));
+    Map<String, Integer> swaggerParamIndexMap =
+        Deencapsulation.getField(producerBeanParamMapper, "swaggerParamIndexMap");
+    Assert.assertEquals(Integer.valueOf(0), swaggerParamIndexMap.get("pathParam"));
+    Assert.assertEquals(Integer.valueOf(1), swaggerParamIndexMap.get("headerParam"));
+    Assert.assertEquals(Integer.valueOf(2), swaggerParamIndexMap.get("intVal"));
+    Assert.assertEquals(Integer.valueOf(3), swaggerParamIndexMap.get("longVal"));
+    Assert.assertEquals(Integer.valueOf(4), swaggerParamIndexMap.get("q"));
+    Assert.assertEquals(Integer.valueOf(5), swaggerParamIndexMap.get("uploaded"));
+  }
+
+  static class AggregatedParamProvider {
+    public String aggregatedParamTest(@BeanParam AggregatedTestParam aggregatedTestParam) {
+      return null;
+    }
+  }
+
+  static class AggregatedTestParam {
+    @PathParam("pathSwaggerParam")
+    private String pathParam;
+
+    private String queryParam;
+
+    @DefaultValue("defaultHeader")
+    @HeaderParam(value = "headerSwaggerParam")
+    private String headerParam;
+
+    @CookieParam("intSwaggerVal")
+    private int intVal;
+
+    @FormParam("longSwaggerVal")
+    private long longVal;
+
+    private Part uploaded;
+
+    public String getPathParam() {
+      return pathParam;
+    }
+
+    public AggregatedTestParam setPathParam(String pathParam) {
+      this.pathParam = pathParam;
+      return this;
+    }
+
+    public String getQ() {
+      return queryParam;
+    }
+
+    @DefaultValue("defaultQuery")
+    @QueryParam(value = "querySwaggerParam")
+    public AggregatedTestParam setQ(String queryParam) {
+      this.queryParam = queryParam;
+      return this;
+    }
+
+    public String getHeaderParam() {
+      return headerParam;
+    }
+
+    public AggregatedTestParam setHeaderParam(String headerParam) {
+      this.headerParam = headerParam;
+      return this;
+    }
+
+    public int getIntVal() {
+      return intVal;
+    }
+
+    public AggregatedTestParam setIntVal(int intVal) {
+      this.intVal = intVal;
+      return this;
+    }
+
+    public long getLongVal() {
+      return longVal;
+    }
+
+    public AggregatedTestParam setLongVal(long longVal) {
+      this.longVal = longVal;
+      return this;
+    }
+
+    public Part getUploaded() {
+      return uploaded;
+    }
+
+    @FormParam("uploadSwaggerParam")
+    public AggregatedTestParam setUploaded(Part uploaded) {
+      this.uploaded = uploaded;
+      return this;
+    }
+  }
+}
\ No newline at end of file