dubbo适配旧的SaaS多数据源



import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 数据源切换
 *
 */
public class MultiDataSource implements DataSource,ApplicationContextAware {

	private static final Log log = LogFactory.getLog(MultiDataSource.class);
	@SuppressWarnings("unused")
	private ApplicationContext applicationContext = null;
	private DataSource dataSource = null;
	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getConnection()
	 */
	@Override
	public Connection getConnection() throws SQLException {
		return getDataSource().getConnection();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
	 */
	@Override
	public Connection getConnection(String arg0, String arg1)
			throws SQLException {
		return getDataSource().getConnection(arg0, arg1);
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getLogWriter()
	 */
	@Override
	public PrintWriter getLogWriter() throws SQLException {
		return getDataSource().getLogWriter();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getLoginTimeout()
	 */
	@Override
	public int getLoginTimeout() throws SQLException {
		return getDataSource().getLoginTimeout();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
	 */
	@Override
	public void setLogWriter(PrintWriter arg0) throws SQLException {
		getDataSource().setLogWriter(arg0);
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#setLoginTimeout(int)
	 */
	@Override
	public void setLoginTimeout(int arg0) throws SQLException {
		getDataSource().setLoginTimeout(arg0);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public DataSource getDataSource(String dataSourceName){
		//System.out.println("dataSourceName:"+dataSourceName);
		/*try{
			if(dataSourceName==null||dataSourceName.equals("")){
				return this.dataSource;
			}
			System.out.println("DataSource:"+(DataSource)this.applicationContext.getBean(dataSourceName));
			
			return (DataSource)this.applicationContext.getBean(dataSourceName);*/
		try{
			if(null==dataSourceName){
				dataSourceName="p1_dev";
			}
			Context initCtx = new InitialContext();   
			try {
				Context ctx = (Context) initCtx.lookup("java:comp/env");
				dataSource = (DataSource) ctx.lookup(dataSourceName.toUpperCase());
			}
			catch(NamingException e){
				Context ctx = (Context) initCtx.lookup("java:");
				dataSource = (DataSource) ctx.lookup(dataSourceName.toUpperCase()); 
			}
			return dataSource;
			
		}catch(Exception ex){
			System.out.println("没有 <name:"+dataSourceName+"> 数据源 在系统当中!");
			log.fatal(ex);
			return null;
		}
	}
	
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public DataSource getDataSource(){
		String sp = SpObserver.getSp();
		return getDataSource(sp);
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}
}

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import mellson.multidata.SpObserver;

/**
 * 
 * 过滤器,从传入参数获取数据源名称
 *
 */
public class ServerFilter extends HttpServlet implements Filter {

	private static final long serialVersionUID = 6452049924844786456L;
	private static FilterConfig filterConfig;
	@Override
	public void destroy() {
		// TODO Auto-generated method stub

	}
	@Override
	public void init(FilterConfig filterConfig) throws ServletException
	{
		ServerFilter.filterConfig = filterConfig;
	}

	public static String getKey(){
		String keyCode = ServerFilter.filterConfig.getInitParameter("keyCode");
		return keyCode;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws IOException, ServletException {
		/**
		 * 需要在此处操作数据源,将数据源切换到新定义好的数据源当中。
		 */
		
		//System.out.println("request.getServerName():_"+request.getServerName());
		//String url = Thread.currentThread().getContextClassLoader().getResource("") + "dataSource/_"+request.getServerName()+".xml";
		//File f=new File(url.substring(6, url.length()));
		System.out.println("datasource:"+request.getParameter("datasource"));
		
		if(null!=request.getParameter("datasource")){
			SpObserver.putSp(request.getParameter("datasource"));
		}else{
			SpObserver.putSp("p1");
		}


		
		filterChain.doFilter(request, response);
	}
}


/**
 * 线程控制
 *
 */
public class SpObserver {
	private static ThreadLocal<String> local = new ThreadLocal<String>();

	public static void putSp(String sp) {
		local.set(sp);
	}

	public static String getSp() {
		return (String)local.get();
	}
}

dubbo序列化选择建议

其中,Kryo是一种非常成熟的序列化实现,已经在Twitter、Groupon、Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。而FST是一种较新的序列化实现,目前还缺乏足够多的成熟使用案例,但我认为它还是非常有前途的。

在面向生产环境的应用中,我建议目前更优先选择Kryo。

启用Kryo

使用Kryo非常简单,只需要在dubbo RPC的XML配置中添加一个属性即可:

<dubbo:protocol name="dubbo" serialization="kryo"/>

Dubbo的服务容器

如果dubbo只是提供dubbo协议服务,不用加载tomcat,jetty等好用资源

服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。

服务容器的加载内容可以扩展,内置了 spring, jetty, log4j 等加载,可通过容器扩展点进行扩展。配置配在 java 命令的 -D 参数或者 dubbo.properties 中。

容器类型

Spring Container

  • 自动加载 META-INF/spring 目录下的所有 Spring 配置。
  • 配置 spring 配置加载位置:dubbo.spring.config=classpath*:META-INF/spring/*.xml

Jetty Container

  • 启动一个内嵌 Jetty,用于汇报状态。
  • 配置:
    • dubbo.jetty.port=8080:配置 jetty 启动端口
    • dubbo.jetty.directory=/foo/bar:配置可通过 jetty 直接访问的目录,用于存放静态文件
    • dubbo.jetty.page=log,status,system:配置显示的页面,缺省加载所有页面

Log4j Container

  • 自动配置 log4j 的配置,在多进程启动时,自动给日志文件按进程分目录。
  • 配置:
    • dubbo.log4j.file=/foo/bar.log:配置日志文件路径
    • dubbo.log4j.level=WARN:配置日志级别
    • dubbo.log4j.subdirectory=20880:配置日志子目录,用于多进程启动,避免冲突

容器启动

缺省只加载 spring

java org.apache.dubbo.container.Main

通过 main 函数参数传入要加载的容器

java org.apache.dubbo.container.Main spring jetty log4j

通过 JVM 启动参数传入要加载的容器

java org.apache.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j

通过 classpath 下的 dubbo.properties 配置传入要加载的容器

dubbo.container=spring,jetty,log4j

dubbo多版本

在 Dubbo 中为同一个服务配置多个版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将所有消费者升级为新版本
  3. 然后将剩下的一半提供者升级为新版本

老版本服务提供者配置:

<dubbo:service interface="com.foo.BarService" version="1.0.0" />

新版本服务提供者配置:

<dubbo:service interface="com.foo.BarService" version="2.0.0" />

老版本服务消费者配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />

新版本服务消费者配置:

<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />

如果不需要区分版本,可以按照以下的方式配置 [^1]:

<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />

dubbo服务分组

使用服务分组区分服务接口的不同实现

当一个接口有多种实现时,可以用 group 区分。

服务

<dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />

引用

<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />

任意组:

<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />

dubbo配置由配置中心管理

外部化配置目的之一是实现配置的集中式管理,这部分业界已经有很多成熟的专业配置系统如 Apollo, Nacos 等,Dubbo 所做的主要是保证能配合这些系统正常工作。

外部化配置和其他本地配置在内容和格式上并无区别,可以简单理解为 dubbo.properties 的外部化存储,配置中心更适合将一些公共配置如注册中心、元数据中心配置等抽取以便做集中管理。

zk-configcenter.jpg
  • namespace,用于不同配置的环境隔离。
  • config,Dubbo约定的固定节点,不可更改,所有配置和服务治理规则都存储在此节点下。
  • dubbo/application,分别用来隔离全局配置、应用级别配置:dubbo是默认group值,application对应应用名
  • dubbo.properties,此节点的node value存储具体配置内容

Nacos

配置中心(配置文件也在Nacos上,注解中填写Nacos地址和配置文件名)


@EnableDubbo
@EnableNacosConfig // 激活 Nacos 配置
@NacosPropertySource(dataId = "nacos-consumer-2.properties")
public class EchoServiceConsumerConfigBootstrap {

    static {
        System.setProperty("nacos.server-addr", "127.0.0.1:8848");
    }

    @Reference(version = "${echo.service.version}")
    private EchoService echoService;

    @NacosConfigListener(dataId = "nacos-consumer-2.properties")
    public void onChange(String properties) {
        System.out.println("onChange(String) : " + properties);
    }

    @PostConstruct
    public void init() {
        for (int i = 0; i < 10; i++) {
            System.err.println(echoService.echo("hello "));
        }
    }

    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(EchoServiceConsumerConfigBootstrap.class);
        context.refresh();
        System.out.println("服务消费者已启动...");
        System.in.read();
        context.close();
    }

}
@EnableDubbo(scanBasePackages = "provider")
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) // 激活 Nacos 配置
@NacosPropertySource(dataId = "nacos-provider-2.properties")
public class EchoServiceProviderConfigBootstrap {

    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(EchoServiceProviderConfigBootstrap.class);
        context.refresh();
        System.out.println("EchoService 启动...");
        System.in.read();
    }
}

Sentinel Dubbo的整合

Sentinel 是什么?

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 提供了与 Dubbo 整合的模块 - Sentinel Dubbo Adapter,主要包括针对 Service Provider 和 Service Consumer 实现的 Filter。使用时用户只需引入以下模块(以 Maven 为例):

```xml
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-dubbo-adapter</artifactId>
    <version>x.y.z</version>
</dependency>
```

引入此依赖后,Dubbo 的服务接口和方法(包括调用端和服务端)就会成为 Sentinel 中的资源,在配置了规则后就可以自动享受到 Sentinel 的防护能力。

> **注:若希望接入 Dashboard,请参考后面接入控制台的步骤。只引入 Sentinel Dubbo Adapter 无法接入控制台!**

若不希望开启 Sentinel Dubbo Adapter 中的某个 Filter,可以手动关闭对应的 Filter,比如:

```java
@Bean
public ConsumerConfig consumerConfig() {
    ConsumerConfig consumerConfig = new ConsumerConfig();
    consumerConfig.setFilter("-sentinel.dubbo.consumer.filter");
    return consumerConfig;
}
```

我们提供了几个具体的 Demo 来分别演示 Provider 和 Consumer 的限流场景。

## Service Provider

Service Provider 用于向外界提供服务,处理各个消费者的调用请求。为了保护 Provider 不被激增的流量拖垮影响稳定性,可以给 Provider 配置 **QPS 模式**的限流,这样当每秒的请求量超过设定的阈值时会自动拒绝多的请求。限流粒度可以是服务接口和服务方法两种粒度。若希望整个服务接口的 QPS 不超过一定数值,则可以为对应服务接口资源(resourceName 为**接口全限定名**)配置 QPS 阈值;若希望服务的某个方法的 QPS 不超过一定数值,则可以为对应服务方法资源(resourceName 为**接口全限定名:方法签名**)配置 QPS 阈值。有关配置详情请参考 [流量控制 | Sentinel](https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6)。

Demo 1 演示了此限流场景,我们看一下这种模式的限流产生的效果。假设我们已经定义了某个服务接口 `com.alibaba.csp.sentinel.demo.dubbo.FooService`,其中有一个方法 `sayHello(java.lang.String)`,Provider 端该方法设定 QPS 阈值为 10。在 Consumer 端在 1s 之内连续发起 15 次调用,可以通过日志文件看到 Provider 端被限流。拦截日志统一记录在 `~/logs/csp/sentinel-block.log` 中:

```
2018-07-24 17:13:43|1|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String),FlowException,default,|5,0
```

在 Provider 对应的 metrics 日志中也有记录:

```
1532423623000|2018-07-24 17:13:43|com.alibaba.csp.sentinel.demo.dubbo.FooService|15|0|15|0|3
1532423623000|2018-07-24 17:13:43|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)|10|5|10|0|0
```

很多场景下,根据**调用方**来限流也是非常重要的。比如有两个服务 A 和 B 都向 Service Provider 发起调用请求,我们希望只对来自服务 B 的请求进行限流,则可以设置限流规则的 `limitApp` 为服务 B 的名称。Sentinel Dubbo Adapter 会自动解析 Dubbo 消费者(调用方)的 application name 作为调用方名称(`origin`),在进行资源保护的时候都会带上调用方名称。若限流规则未配置调用方(`default`),则该限流规则对所有调用方生效。若限流规则配置了调用方则限流规则将仅对指定调用方生效。

> 注:Dubbo 默认通信不携带对端 application name 信息,因此需要开发者在调用端手动将 application name 置入 attachment 中,provider 端进行相应的解析。Sentinel Dubbo Adapter 实现了一个 Filter 用于自动从 consumer 端向 provider 端透传 application name。若调用端未引入 Sentinel Dubbo Adapter,又希望根据调用端限流,可以在调用端手动将 application name 置入 attachment 中,key 为 `dubboApplication`。

在限流日志中会也会记录调用方的名称,如:

```
2018-07-25 16:26:48|1|com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String),FlowException,default,demo-consumer|5,0
```

其中日志中的 `demo-consumer` 即为调用方名称。

## Service Consumer

> 对服务消费方的流量控制可分为**控制并发线程数**和**服务降级**两个维度。

### 并发线程数限流

Service Consumer 作为客户端去调用远程服务。每一个服务都可能会依赖几个下游服务,若某个服务 A 依赖的下游服务 B 出现了不稳定的情况,服务 A 请求服务 B 的响应时间变长,从而服务 A 调用服务 B 的线程就会产生堆积,最终可能耗尽服务 A 的线程数。我们通过用并发线程数来控制对下游服务 B 的访问,来保证下游服务不可靠的时候,不会拖垮服务自身。基于这种场景,推荐给 Consumer 配置**线程数模式**的限流,来保证自身不被不稳定服务所影响。限流粒度同样可以是服务接口和服务方法两种粒度。

采用基于线程数的限流模式后,我们不需要再显式地去进行线程池隔离,Sentinel 会控制资源的线程数,超出的请求直接拒绝,直到堆积的线程处理完成。

Demo 2 演示了此限流场景,我们看一下这种模式的效果。假设当前服务 A 依赖两个远程服务方法 `sayHello(java.lang.String)` 和 `doAnother()`。前者远程调用的响应时间 为 1s-1.5s之间,后者 RT 非常小(30 ms 左右)。服务 A 端设两个远程方法 thread count 为 5。然后每隔 50 ms 左右向线程池投入两个任务,作为消费者分别远程调用对应方法,持续 10 次。可以看到 `sayHello` 方法被限流 5 次,因为后面调用的时候前面的远程调用还未返回(RT 高);而 `doAnother()` 调用则不受影响。线程数目超出时快速失败能够有效地防止自己被慢调用所影响。

### 服务降级

当服务依赖于多个下游服务,而某个下游服务调用非常慢时,会严重影响当前服务的调用。这里我们可以利用 Sentinel 熔断降级的功能,为调用端配置基于平均 RT 的[降级规则](https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7)。这样当调用链路中某个服务调用的平均 RT 升高,在一定的次数内超过配置的 RT 阈值,Sentinel 就会对此调用资源进行降级操作,接下来的调用都会立刻拒绝,直到过了一段设定的时间后才恢复,从而保护服务不被调用端短板所影响。同时可以配合 fallback 功能使用,在被降级的时候提供相应的处理逻辑。

## Fallback

从 0.1.1 版本开始,Sentinel Dubbo Adapter 还支持配置全局的 fallback 函数,可以在 Dubbo 服务被限流/降级/负载保护的时候进行相应的 fallback 处理。用户只需要实现自定义的 [`DubboFallback`](https://github.com/alibaba/Sentinel/blob/master/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java) 接口,并通过 `DubboFallbackRegistry` 注册即可。默认情况会直接将 `BlockException` 包装后抛出。同时,我们还可以配合 [Dubbo 的 fallback 机制](http://dubbo.apache.org/#!/docs/user/demos/local-mock.md?lang=zh-cn) 来为降级的服务提供替代的实现。

Demo 2 的 Consumer 端提供了一个简单的 fallback 示例。

## Sentinel Dashboard

Sentinel 还提供 API 用于获取实时的监控信息,对应文档见[此处](https://github.com/alibaba/Sentinel/wiki/%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7)。为了便于使用,Sentinel 还提供了一个控制台(Dashboard)用于配置规则、查看监控、机器发现等功能。

接入 Dashboard 的步骤(**缺一不可**):

1. 按照 [Sentinel 控制台文档](https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0) 启动控制台
2. 应用引入 `sentinel-transport-simple-http` 依赖,以便控制台可以拉取对应应用的相关信息
3. 给应用添加相关的启动参数,启动应用。需要配置的参数有:
   - `-Dcsp.sentinel.api.port`:客户端的 port,用于上报相关信息
   - `-Dcsp.sentinel.dashboard.server`:控制台的地址
   - `-Dproject.name`:应用名称,会在控制台中显示

注意某些环境下本地运行 Dubbo 服务还需要加上 `-Djava.net.preferIPv4Stack=true` 参数。比如 Service Provider 示例的启动参数:

```bash
-Djava.net.preferIPv4Stack=true -Dcsp.sentinel.api.port=8720 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=dubbo-provider-demo
```

Service Consumer 示例的启动参数:

```bash
-Djava.net.preferIPv4Stack=true -Dcsp.sentinel.api.port=8721 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=dubbo-consumer-demo
```

这样在启动 Service Provider 和 Service Consumer 示例以后,就可以在 Sentinel 控制台中找到我们的服务了。可以很方便地在控制台中配置限流规则:

![规则配置](http://dubbo.incubator.apache.org/img/blog/sentinel-dashboard-view-rules.png)

或者查看实时监控数据:

![秒级实时监控](http://dubbo.incubator.apache.org/img/blog/sentinel-dashboard-metrics.png)

dubbo使用nacos作为注册中心

如果是使用springboot主要修改配置文件application.properties即可

# Spring boot application
spring.application.name=currencyservice

# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
dubbo.scan.base-packages=com.alibabacloud.hipstershop.currencyservice
# Dubbo Application
## The default value of dubbo.application.name is ${spring.application.name}
## dubbo.application.name=${spring.application.name}
# Dubbo Protocol
dubbo.protocol.name=dubbo
dubbo.protocol.port=20003
## Dubbo Registry
dubbo.registry.address=nacos://nacos-server:8848
dubbo.cloud.subscribed-services=${spring.application.name}
dubbo.consumer.check=false
dubbo.registry.check=false
dubbo.application.qos-enable=true
dubbo.application.qos-accept-foreign-ip=false
server.port=8084

dubbo官方网关介绍

Dubbo Proxy是一个Dubbo网关,可以将Http请求转换成Dubbo的协议,调用Dubbo服务并且返回结果,后续还会集成熔断,限流,api管理等功能。

用法

http请求格式如下:

POST {应用名称}/​{接口名称}?version={可选}&group={可选}

http POST body如下:

{
     "methodName" : "sayHello",
     "paramTypes" : ["org.apache.dubbo.demo.model.User"],
     "paramValues": [
         { 
            "id": 23,
             "username": "testUser"
         }
     ]
 }

参考示例

接口

package com.kdniao;

import com.kdniao.domain.LogisticResponse;

public interface KdniaoTrackQueryComp {
/**
* Json方式 查询订单物流轨迹
* @throws Exception
*/
public LogisticResponse getOrderTracesByJson(String expCode, String expNo, String customerName) throws Exception;
}

请求地址

http://192.168.3.22:8000/com-service/com.kdniao.KdniaoTrackQueryComp

请求参数

{    “methodName” : “getOrderTracesByJson”,  

  “paramTypes” : [“java.lang.String”,”java.lang.String”,”java.lang.String”],    

“paramValues”: [        

{“expCode”: “SF”},       

 {“expNo”: “123”},       

 {“customerName”: “123”}    ]

}

fastjson返回jsonp

<bean id="fastjsonConverter"
            class="com.alibaba.fastjson.support.spring.FastJsonpHttpMessageConverter4">
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json;charset=UTF-8</value>
                    <value>text/plain;charset=UTF-8</value>
                    <value>text/html;charset=UTF-8</value>
                    <value>text/json;charset=UTF-8</value>
                </list>
            </property>
            <property name="features"> 
				  <array value-type="com.alibaba.fastjson.serializer.SerializerFeature"> 
				   	<value>DisableCircularReferenceDetect</value> 
				  </array> 
			</property> 
        </bean>

	<bean id="fastJsonpResponseBodyAdvice" class="com.alibaba.fastjson.support.spring.FastJsonpResponseBodyAdvice">
	    <constructor-arg>
	        <list>
	            <value>callback</value>
	            <value>jsonp</value>
	        </list>
	    </constructor-arg>
	</bean>