1

我们需要使用 spring 远程调用一个 Bean 类,并在调用中设置动态标头。我们可以HttpInvokerRequestExecutorHttpInvokerProxyFactoryBean和添加标头中设置自定义,但是如何为请求设置动态生成的标头?

In the Config class, declaring the HttpInvokerProxyFactoryBean
@Bean
@Qualifier("service")
public HttpInvokerProxyFactoryBean invoker() {
    HttpInvokerProxyFactoryBean invoker = new HttpInvokerProxyFactoryBean();
    invoker.setServiceUrl(url);
    invoker.setServiceInterface(Service.class);
    return invoker;
}

In the invoker class
@Autowired
Service service;

public void invoke(Bean bean) {
    service.process(bean);
}
4

1 回答 1

0

我使用 spring 远程处理已经很长时间了,但据我所知,我通过子类化 SimpleHttpInvokerRequestExecutor 找到了解决方案,当您没有将任何自定义请求执行器设置为 HttpInvokerProxyFactoryBean 时,这是默认设置。

恕我直言,您可以编写一个自定义请求执行器,您可以设置自定义标头值和一个简单的帮助器组件,该组件在下一个请求之前将动态提供的值设置给执行器。

CustomHttpInvokerRequestExecutor:

public class CustomHttpInvokerRequestExecutor extends SimpleHttpInvokerRequestExecutor {

private Map<String, String> headers;

public void setHeaders(Map<String, String> headers) {
    this.headers = headers;
}

@Override
protected void prepareConnection(HttpURLConnection connection, int contentLength) throws IOException {
    super.prepareConnection(connection, contentLength);

    if (headers != null) {
        // adding our custom headers
        for (String headerName : headers.keySet()) {
            connection.setRequestProperty(headerName, headers.get(headerName));
        }
        // do not want to persist headers for another request!
        headers.clear();
    }
}
}

自定义远程执行器:

@Component
public class CustomRemoteExecutor {

@Autowired
private HttpInvokerProxyFactoryBean factoryBean;

/*
 * May be you should need a synchronized modifier here if there is possibility
 * of multiple threads access here at the same time
 */
public void executeInTemplate(Map<String, String> headers, Runnable task) {
    CustomHttpInvokerRequestExecutor executor = (CustomHttpInvokerRequestExecutor) factoryBean.getHttpInvokerRequestExecutor();
    executor.setHeaders(headers);
    task.run();
}

}

然后您可以通过以下方式使用它:

@Bean
@Qualifier("service")
public HttpInvokerProxyFactoryBean invoker() {
    HttpInvokerProxyFactoryBean invoker = new HttpInvokerProxyFactoryBean();
    invoker.setServiceUrl(testUrl);
    invoker.setServiceInterface(Service.class);
    // set our custom request executor
    CustomHttpInvokerRequestExecutor executor = new CustomHttpInvokerRequestExecutor();
    invoker.setHttpInvokerRequestExecutor(executor);

    return invoker;
}

@Autowired
CustomRemoteExecutor executor;

@Autowired
Service service;

public void invoke(Bean bean) {

    // when you need custom headers
    Map<String, String> headers = new HashMap<>();
    headers.put("CUSTOM_HEADER", "CUSTOM_VALUE");
    headers.put("CUSTOM_HEADER2", "CUSTOM_VALUE2");
    executor.executeInTemplate(headers, () -> service.process(bean));

}

正如我在评论中所说的那样,这里有一个缺点,如果您在多线程环境中执行代理服务客户端(服务器到服务器请求可能是),您应该考虑制作executeInTemplate方法synchronized

如果您的服务方法需要返回某个对象,那么您可以添加另一个帮助方法CustomRemoteExecutor并在需要返回某些内容时使用它,这是对我的回答的补充。该方法可以在这里具有相同的名称,因此它可以重载前一个,我认为这要好得多。

public <T> T executeInTemplate(Map<String, String> headers, Callable<T> task) {
    CustomHttpInvokerRequestExecutor executor = (CustomHttpInvokerRequestExecutor) factoryBean.getHttpInvokerRequestExecutor();
    executor.setHeaders(headers);

    try {
        return task.call();
    } catch (Exception e) {
        // it is better to log this exception by your preferred logger (log4j, logback
        // etc.)
        e.printStackTrace();
    }

    return null;
}

你可以再次使用如下:

@Autowired
CustomRemoteExecutor executor;

@Autowired
ISampleService service;

public void invoke(Bean bean) {

    // when you need custom headers
    Map<String, String> headers = new HashMap<>();
    headers.put("CUSTOM_HEADER", "CUSTOM_VALUE");
    headers.put("CUSTOM_HEADER2", "CUSTOM_VALUE2");

    // assume that service.returnSomething() method returns String
    String value = executor.executeInTemplate(headers, () -> service.returnSomething(bean));

}

希望能帮助到你。

于 2019-01-08T22:03:59.413 回答