28. Remoting and web services using Spring
使用spring的远程和web service
28.1 Introduction
介绍
Spring features integration classes for remoting support using various technologies. The remoting support eases the development of remote-enabled services, implemented by your usual (Spring) POJOs. Currently, Spring supports the following remoting technologies:
spring的特性集成类用于远程支持使用不同的技术。远程支持启用远程支持的服务,通过实现普通的pojo。当前,spring支持下面的远程技术:
Remote Method Invocation (RMI). Through the use of the RmiProxyFactoryBean and the RmiServiceExporter Spring supports both traditional RMI (with java.rmi.Remote interfaces and java.rmi.RemoteException) and transparent remoting via RMI invokers (with any Java interface).
远程方法调用。通过使用RmiProxyFactoryBean和RmiServiceExporter后spring支持传统的RMI(使用java.rml.Remote接口和java.rmi.RemoteException)和透明的远程通过RMI调用(使用任何Java接口)
Spring’s HTTP invoker. Spring provides a special remoting strategy which allows for Java serialization via HTTP, supporting any Java interface (just like the RMI invoker). The corresponding support classes are HttpInvokerProxyFactoryBean and HttpInvokerServiceExporter.
spring的http调用。spring支持一个特殊的远程策略来访问Java序列化通过http,支持任何Java接口(就像RMI调用一样)。相应的支持类是HttpInvokerProxyFactoryBean和HttpInvokerServiceExporter。
Hessian. By using Spring’s HessianProxyFactoryBean and the HessianServiceExporter you can transparently expose your services using the lightweight binary HTTP-based protocol provided by Caucho.
Hession。通过使用spring的HessianProxyFactoryBean和HessianServiceExporter,你可以暴露你的服务使用轻量级的基于http的协议通过Caucho来提供。
Burlap. Burlap is Caucho’s XML-based alternative to Hessian. Spring provides support classes such as BurlapProxyFactoryBean and BurlapServiceExporter.
Burlap。是Caucho基于xml来替代Hessian。spring提供支持类例如BurlapProxyFactoryBean和BurlapServiceExporter。
JAX-WS. Spring provides remoting support for web services via JAX-WS (the successor of JAX-RPC, as introduced in Java EE 5 and Java 6).
JAX-WS。spring提供了远程支持用于web服务通过JAX-WS(JAX-RPC的替代者,在JavaEE5和Java6中被引入使用)。
JMS. Remoting using JMS as the underlying protocol is supported via the JmsInvokerServiceExporter and JmsInvokerProxyFactoryBean classes.
JMS。远程使用JMS作为底层协议支持通过JmsInvokerServiceExporter和JmsInvokerProxyFactoryBean类。
AMQP. Remoting using AMQP as the underlying protocol is supported by the Spring AMQP project.
AMQP。远程使用AMQP作为底层协议通过spring的AMQP项目来支持。
While discussing the remoting capabilities of Spring, we’ll use the following domain model and corresponding services:
当讨论spring的远程能力时,我们使用下面的主体类和相应的服务:
public class Account implements Serializable{
private String name;
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface AccountService {
public void insertAccount(Account account);
public List<Account> getAccounts(String name);
}
// the implementation doing nothing at the moment
public class AccountServiceImpl implements AccountService {
public void insertAccount(Account acc) {
// do something...
}
public List<Account> getAccounts(String name) {
// do something...
}
}
We will start exposing the service to a remote client by using RMI and talk a bit about the drawbacks of using RMI. We’ll then continue to show an example using Hessian as the protocol.
我们将开始暴露服务给远程的客户端通过使用RMI但是使用RMI存在一些缺点。我们将继续展示一个使用Hessian作为协议的例子。
28.2 Exposing services using RMI
使用RMI来暴露服务
Using Spring’s support for RMI, you can transparently expose your services through the RMI infrastructure. After having this set up, you basically have a configuration similar to remote EJBs, except for the fact that there is no standard support for security context propagation or remote transaction propagation. Spring does provide hooks for such additional invocation context when using the RMI invoker, so you can for example plug in security frameworks or custom security credentials here.
使用spring对于RMI的支持,你可以暴露你的服务通过RMI的基础设施。在设置好这些后,你对于远程的EJB会有一个相似的配置,除了不在支持安全上下文传播或远程的事务传播。spring提供了钩子用于额外的调用上下文当使用RMI调用者的时候,因此你可以使用插件框架或自定义安全验证。
28.2.1 Exporting the service using the RmiServiceExporter
使用RmiServiceExporter来暴露服务
Using the RmiServiceExporter, we can expose the interface of our AccountService object as RMI object. The interface can be accessed by using RmiProxyFactoryBean, or via plain RMI in case of a traditional RMI service. The RmiServiceExporter explicitly supports the exposing of any non-RMI services via RMI invokers.
使用RmiServiceExporter,我们可以暴露我们AccountService的object的接口作为RMI的object。这个接口可以被访问通过RmiProxyFactoryBean或通过普通的RMI由于传统的RMI服务。RmiServiceExporter支持通过RMI调用者来暴露任意的非RMI服务。
Of course, we first have to set up our service in the Spring container:
当然,我们首先应该将服务设置在spring的容器中:
<bean id="accountService" class="example.AccountServiceImpl">
<!-- any additional properties, maybe a DAO? -->
</bean>
Next we’ll have to expose our service using the RmiServiceExporter:
下一步我们需要暴露我们的服务通过使用RmiServiceExporter:
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- does not necessarily have to be the same name as the bean to be exported -->
<property name="serviceName" value="AccountService"/>
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
<!-- defaults to 1099 -->
<property name="registryPort" value="1199"/>
</bean>
As you can see, we’re overriding the port for the RMI registry. Often, your application server also maintains an RMI registry and it is wise to not interfere with that one. Furthermore, the service name is used to bind the service under. So right now, the service will be bound at 'rmi://HOST:1199/AccountService'. We’ll use the URL later on to link in the service at the client side.
你看到,我们覆盖的RMI注册中心的端口。你的应用服务器也包含一个RMI注册中心并且里面没有任何接口。此外,服务名被用于绑定服务。因此,服务将绑定为'rmi://HOST:1199/AccountService'。我们将在后续使用URL来连接客户端和服务端。
[Note]
注意
The servicePort property has been omitted (it defaults to 0). This means that an anonymous port will be used to communicate with the service.、
servicePort属性已经被忽略了(默认是0)。这意味着任何匿名端口将被是使用来消费这个服务。
28.2.2 Linking in the service at the client
连接客户端和服务
Our client is a simple object using the AccountService to manage accounts:
我们的客户端是一个简单的object使用AccountService来管理accounts:
public class SimpleObject {
private AccountService accountService;
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
// additional methods using the accountService
}
To link in the service on the client, we’ll create a separate Spring container, containing the simple object and the service linking configuration bits:
为了连接客户端和服务,我们将会创建一个分离的spring容器,包含简单的object和服务连接配置:
<bean class="example.SimpleObject">
<property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
That’s all we need to do to support the remote account service on the client. Spring will transparently create an invoker and remotely enable the account service through the RmiServiceExporter. At the client we’re linking it in using the RmiProxyFactoryBean.
这就是我们需要来支持远程用户服务。spring将创建一个调用者和远程允许account服务通过RmiServiceExporter。在客户端我们连接他通过使用RmiProxyFactoryBean。
28.3 Using Hessian or Burlap to remotely call services via HTTP
使用Hession或Burlap来远程调用服务通过HTTP
Hessian offers a binary HTTP-based remoting protocol. It is developed by Caucho and more information about Hessian itself can be found at http://www.caucho.com.
Hession提供了一个二进制的基于http的远程协议。他是Caucho开发的并且关于Hession的有关信息请参考http://www.caucho.com。
28.3.1 Wiring up the DispatcherServlet for Hessian and co.
使用DispatcherServlet来处理Hessian
Hessian communicates via HTTP and does so using a custom servlet. Using Spring’s DispatcherServlet principles, as known from Spring Web MVC usage, you can easily wire up such a servlet exposing your services. First we’ll have to create a new servlet in your application (this is an excerpt from 'web.xml'):
通过HTTP通信的Hession使用一个自定义的Servlet。使用spring的DispatcherServlet原则,被spring的web的mvc使用,你可以简单的处理这样的Servlet暴露你的服务。首先我们已经创建了一个新的Servlet在你的应用中(除了web.xml):
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/remoting/*</url-pattern>
</servlet-mapping>
You’re probably familiar with Spring’s DispatcherServlet principles and if so, you know that now you’ll have to create a Spring container configuration resource named 'remoting-servlet.xml' (after the name of your servlet) in the 'WEB-INF' directory. The application context will be used in the next section.
你可以使用spring的DispatcherServlet原则并且如果这么做现在你将需要创建一个spring的容器配置资源名字为'remoting-servlet.xml'(在你的Servlet之后)在'WEB-INF'目录中。应用上下文被使用在下一个内容中。
Alternatively, consider the use of Spring’s simpler HttpRequestHandlerServlet. This allows you to embed the remote exporter definitions in your root application context (by default in 'WEB-INF/applicationContext.xml'), with individual servlet definitions pointing to specific exporter beans. Each servlet name needs to match the bean name of its target exporter in this case.
作为替代,考虑使用spring的简单的HttpRequestHandlerServlet。他允许你来集成远程的定义在你的根应用上下文(通过默认的在'WEB-INF/applicationContext.xml'中)。使用独立的Servlet定义指向特定bean。每个Servlet名字需要匹配bean的名字来匹配目标调用。
28.3.2 Exposing your beans by using the HessianServiceExporter
暴露你的bean通过使用HessianServiceExporter
In the newly created application context called remoting-servlet.xml, we’ll create a HessianServiceExporter exporting your services:
在新创建的应用上下文名字为remoting-servlet.xml,我们将创建一个HessianServiceExporter来暴露你的服务:
<bean id="accountService" class="example.AccountServiceImpl">
<!-- any additional properties, maybe a DAO? -->
</bean>
<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
Now we’re ready to link in the service at the client. No explicit handler mapping is specified, mapping request URLs onto services, so BeanNameUrlHandlerMapping will be used: Hence, the service will be exported at the URL indicated through its bean name within the containing DispatcherServlet’s mapping (as defined above): ’http://HOST:8080/remoting/AccountService'.
现在我们需要连接客户端和服务端。不需要明确的指定匹配,匹配URL和服务,因为BeanNameUrlHandlerMapping将被使用:因此,服务将暴露于URL指定bean的名字在容器DispatcherServlet的匹配中(定义如上):’http://HOST:8080/remoting/AccountService'。
Alternatively, create a HessianServiceExporter in your root application context (e.g. in 'WEB-INF/applicationContext.xml'):
作为替代,创建一个HessianServiceExporter在你的根应用上下文中(例如,在'WEB-INF/applicationContext.xml'中);
<bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
In the latter case, define a corresponding servlet for this exporter in 'web.xml', with the same end result: The exporter getting mapped to the request path /remoting/AccountService. Note that the servlet name needs to match the bean name of the target exporter.
在后面的例子中,定义一个相应的Servlet用于暴露在web.xml中,使用相同的结果。将匹配请求路径/remoting/AccountService。注意Servlet的名字需要匹配目标暴露的bean的名字。
<servlet>
<servlet-name>accountExporter</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>accountExporter</servlet-name>
<url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>
28.3.3 Linking in the service on the client
连接客户端和服务
Using the HessianProxyFactoryBean we can link in the service at the client. The same principles apply as with the RMI example. We’ll create a separate bean factory or application context and mention the following beans where the SimpleObject is using the AccountService to manage accounts:
使用HessianProxyFactoryBean我们可以连接客户端和服务。相同的规则可以应用于RMI的案例。我们将创建一个隔离的bean工厂或应用上下文并提到下面的bean其中SimpleObject使用使用AccountService来管理accounts:
<bean class="example.SimpleObject">
<property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
28.3.4 Using Burlap
使用Burlap
We won’t discuss Burlap, the XML-based equivalent of Hessian, in detail here, since it is configured and set up in exactly the same way as the Hessian variant explained above. Just replace the word Hessian with Burlap and you’re all set to go.
我们不会讨论Burlap,基于xml的和Hessian相同,因此他的配置和设置和Hessian在下面的例子相似。指示替换了Hessian为Burlap而已。
28.3.5 Applying HTTP basic authentication to a service exposed through Hessian or Burlap
应用http基本的验证来服务通过Hessian或Burlap暴露的服务
One of the advantages of Hessian and Burlap is that we can easily apply HTTP basic authentication, because both protocols are HTTP-based. Your normal HTTP server security mechanism can easily be applied through using the web.xml security features, for example. Usually, you don’t use per-user security credentials here, but rather shared credentials defined at the Hessian/BurlapProxyFactoryBean level (similar to a JDBC DataSource).
Hessian和Burlap的一个优点是我们可以简单的应用HTTP基本的验证,因为他们都是基于HTTP。普通的HTTP服务器安全策略可以简单的被应用通过使用web.xml的安全特性,例如,通常你不需要使用每个用户的安全验证,但是最好共享验证定义在Hessian/BurlapProxyFactoryBean级别(相似于JDBC的数据源)。
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors" ref="authorizationInterceptor"/>
</bean>
<bean id="authorizationInterceptor"
class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor">
<property name="authorizedRoles" value="administrator,operator"/>
</bean>
This is an example where we explicitly mention the BeanNameUrlHandlerMapping and set an interceptor allowing only administrators and operators to call the beans mentioned in this application context.
这是一个例子我们期望提到BeanNameUrlHandlerMapping并且设置一个拦截器来只允许administrator来操作和调用bean设置在应用上下文中。
[Note]
注意
Of course, this example doesn’t show a flexible kind of security infrastructure. For more options as far as security is concerned, have a look at the Spring Security project at http://projects.spring.io/spring-security/.
当然,这个里没有展示一种友好的安全的风格。有关更多安全方面考虑的选项,请参考spring的Security项目在http://projects.spring.io/spring-security/页面。
28.4 Exposing services using HTTP invokers
使用HTTP调用者来暴露服务
As opposed to Burlap and Hessian, which are both lightweight protocols using their own slim serialization mechanisms, Spring HTTP invokers use the standard Java serialization mechanism to expose services through HTTP. This has a huge advantage if your arguments and return types are complex types that cannot be serialized using the serialization mechanisms Hessian and Burlap use (refer to the next section for more considerations when choosing a remoting technology).
和Burlap和Hessian相对的,轻量级的协议使用他们自身的序列化策略,spring的http调用者使用标准的Java序列化策略来暴露服务通过HTTP。这样就会有一个很大的优势如果你的参数和返回类型是复杂类型不用被序列化通过使用Hessian和Burlap的序列化策略(查看下一个章节来了解更多有关考虑选择一个远程技术)。
Under the hood, Spring uses either the standard facilities provided by the JDK or Apache HttpComponents to perform HTTP calls. Use the latter if you need more advanced and easier-to-use functionality. Refer to hc.apache.org/httpcomponents-client-ga/ for more information.
spring使用了标准的库通过JDK或Apache的HttpComponents来执行HTTP的调用。如果你需要高级的便于使用的功能。参考hc.apache.org/httpcomponents-client-ga/来了解更多的信息。
28.4.1 Exposing the service object
暴露服务object
Setting up the HTTP invoker infrastructure for a service object resembles closely the way you would do the same using Hessian or Burlap. Just as Hessian support provides the HessianServiceExporter, Spring’s HttpInvoker support provides the org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter.
设置http的调用者用于服务object暴露你需要的方式对于Hessian或Burlap。由于Hessian支持提供HessianServiceExporter,spring的HttpInvoker提供了org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter。
To expose the AccountService (mentioned above) within a Spring Web MVC DispatcherServlet, the following configuration needs to be in place in the dispatcher’s application context:
为了暴露AccountService(上面提到的)使用spring的web的mvc的DispatcherServlet,下面的配置需要定义在dispatcher的应用上下文中:
<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
Such an exporter definition will be exposed through the `DispatcherServlet’s standard mapping facilities, as explained in the section on Hessian.
这样的暴露器定义将通过dispatcher的标准匹配策略来使用,和Hessian中解释相同。
Alternatively, create an HttpInvokerServiceExporter in your root application context (e.g. in 'WEB-INF/applicationContext.xml'):
作为替代,创建一个HttpInvokerServiceExporter在你的根应用上下文中(例如,在'WEB-INF/applicationContext.xml'中):
<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
In addition, define a corresponding servlet for this exporter in 'web.xml', with the servlet name matching the bean name of the target exporter:
此外,定义一个相应的Servlet用于这个暴露在web.xml中,使用Servlet的名字匹配目标暴露器的名字:
<servlet>
<servlet-name>accountExporter</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>accountExporter</servlet-name>
<url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>
If you are running outside of a servlet container and are using Oracle’s Java 6, then you can use the built-in HTTP server implementation. You can configure the SimpleHttpServerFactoryBean together with a SimpleHttpInvokerServiceExporter as is shown in this example:
如果你在Servlet容器外执行和使用Oracle的Java6,你可以使用内置的HTTP服务器实现。你可以配置SimpleHttpServerFactoryBean使用SimpleHttpInvokerServiceExporter展示在下面的例子中:
<bean name="accountExporter"
class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
<bean id="httpServer"
class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
<property name="contexts">
<util:map>
<entry key="/remoting/AccountService" value-ref="accountExporter"/>
</util:map>
</property>
<property name="port" value="8080" />
</bean>
28.4.2 Linking in the service at the client
在连接客户端和服务
Again, linking in the service from the client much resembles the way you would do it when using Hessian or Burlap. Using a proxy, Spring will be able to translate your calls to HTTP POST requests to the URL pointing to the exported service.
连接客户端和服务和你使用Hessian或Burlap的方式很相似。使用一个代理,spring将可以转换你的调用为HTTP的POST请求对于URL指向暴露的服务。
<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
<property name="serviceInterface" value="example.AccountService"/>
</bean>
As mentioned before, you can choose what HTTP client you want to use. By default, the HttpInvokerProxy uses the JDK’s HTTP functionality, but you can also use the Apache HttpComponents client by setting the httpInvokerRequestExecutor property:
由于之前提到的,你可以选择你希望使用的HTTP的客户端。默认的,HttpInvokerProxy使用JDK的http功能,但是你可以使用Apache的HttpComponents的客户端通过设置httpInvokerRequestExecutor属性:
<property name="httpInvokerRequestExecutor">
<bean class="org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor"/>
</property>
28.5 Web services
web服务
Spring provides full support for standard Java web services APIs:
spring提供了全面的支持对于标准的Java的web服务API
Exposing web services using JAX-WS
暴露web服务使用JAX-WS
Accessing web services using JAX-WS
访问web服务使用JAX-WS
In addition to stock support for JAX-WS in Spring Core, the Spring portfolio also features Spring Web Services, a solution for contract-first, document-driven web services - highly recommended for building modern, future-proof web services.
此外在spring的核心中支持JAX-WS,spring的portfolio也是spring的web服务,一个解决方案用于首次访问,基于文档的web服务,推荐来构建现代的、不会过时的web服务。
28.5.1 Exposing servlet-based web services using JAX-WS
使用JAX-WS来暴露基于Servlet的web服务
Spring provides a convenient base class for JAX-WS servlet endpoint implementations - SpringBeanAutowiringSupport. To expose our AccountService we extend Spring’s SpringBeanAutowiringSupport class and implement our business logic here, usually delegating the call to the business layer. We’ll simply use Spring’s @Autowired annotation for expressing such dependencies on Spring-managed beans.
spring提供了一个方便的基类用于JAX-WS的Servlet实现————SpringBeanAutowiringSupport。为了暴露我们的AccountService我们继承了spring的SpringBeanAutowiringSupport类并且实现了我们自己的逻辑在这里,通常授权调用业务层。我们将简单的使用spring的@Autowired注解用于表达这样的依赖对于spring管理的bean。
/**
* JAX-WS compliant AccountService implementation that simply delegates
* to the AccountService implementation in the root web application context.
*
* This wrapper class is necessary because JAX-WS requires working with dedicated
* endpoint classes. If an existing service needs to be exported, a wrapper that
* extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through
* the @Autowired annotation) is the simplest JAX-WS compliant way.
*
* This is the class registered with the server-side JAX-WS implementation.
* In the case of a Java EE 5 server, this would simply be defined as a servlet
* in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting
* accordingly. The servlet name usually needs to match the specified WS service name.
*
* The web service engine manages the lifecycle of instances of this class.
* Spring bean references will just be wired in here.
*/
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
@WebService(serviceName="AccountService")
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {
@Autowired
private AccountService biz;
@WebMethod
public void insertAccount(Account acc) {
biz.insertAccount(acc);
}
@WebMethod
public Account[] getAccounts(String name) {
return biz.getAccounts(name);
}
}
Our AccountServiceEndpoint needs to run in the same web application as the Spring context to allow for access to Spring’s facilities. This is the case by default in Java EE 5 environments, using the standard contract for JAX-WS servlet endpoint deployment. See Java EE 5 web service tutorials for details.
我们的AccountServiceEndpoint需要运行在相同的web应用作为spring的上下文呢允许访问spring的组件。默认是在JavaEE5的环境中,使用标准的访问用于JAX-WS的Servlet服务端部署。见JavaEE5的web服务指南来了解更多内容。
28.5.2 Exporting standalone web services using JAX-WS
使用JAX-WS来暴露标准的web服务
The built-in JAX-WS provider that comes with Oracle’s JDK 1.6 supports exposure of web services using the built-in HTTP server that’s included in JDK 1.6 as well. Spring’s SimpleJaxWsServiceExporter detects all @WebService annotated beans in the Spring application context, exporting them through the default JAX-WS server (the JDK 1.6 HTTP server).
内置的JAX-WS提供了来自Oracle的JDK6的支持暴露的web服务使用内置的HTTP服务器暴露JDK1.6及以上的版本。spring的SimpleJaxWsServiceExporter检测到所有@WebService注解的bean在spring的应用上下文,暴露他们通过默认的JAX-WS服务器(JDK1.6的HTTP服务器)。
In this scenario, the endpoint instances are defined and managed as Spring beans themselves; they will be registered with the JAX-WS engine but their lifecycle will be up to the Spring application context. This means that Spring functionality like explicit dependency injection may be applied to the endpoint instances. Of course, annotation-driven injection through @Autowired will work as well.
在这个场景中,端点实例被定义和管理作为spring的bean本身,他们将被注册到JAX-WS引擎中但是他们的生命周期是取决于spring的应用上下文的。这意味着spring的功能类似于依赖注入可以应用于端点实例。当然注解驱动注入通过@Autowired也是可行的。
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
<property name="baseAddress" value="http://localhost:8080/"/>
</bean>
<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
...
</bean>
...
The AccountServiceEndpoint may derive from Spring’s SpringBeanAutowiringSupport but doesn’t have to since the endpoint is a fully Spring-managed bean here. This means that the endpoint implementation may look like as follows, without any superclass declared - and Spring’s @Autowired configuration annotation still being honored:
AccountServiceEndpoint来自spring的SpringBeanAutowiringSupport到那时不是spring管理的bean的端点。这意味着端点实现类似于如下的样子,不需要超类的定义而且spring的@Autowired的配置注解依然可以使用:
@WebService(serviceName="AccountService")
public class AccountServiceEndpoint {
@Autowired
private AccountService biz;
@WebMethod
public void insertAccount(Account acc) {
biz.insertAccount(acc);
}
@WebMethod
public List<Account> getAccounts(String name) {
return biz.getAccounts(name);
}
}
28.5.3 Exporting web services using the JAX-WS RI’s Spring support
使用JAX-WS的RI的spring支持来暴露web服务
Oracle’s JAX-WS RI, developed as part of the GlassFish project, ships Spring support as part of its JAX-WS Commons project. This allows for defining JAX-WS endpoints as Spring-managed beans, similar to the standalone mode discussed in the previous section - but this time in a Servlet environment. Note that this is not portable in a Java EE 5 environment; it is mainly intended for non-EE environments such as Tomcat, embedding the JAX-WS RI as part of the web application.
Oracle的JAX-WS的RI,作为GlassFish项目的一部分,spring支持JAX-WS通用项目的一部分。这允许定义JAX-WS端点作为spring管理的bean,相似于独立的模式讨论过在之前的章节中————但是这次是在Servlet环境中。注意在JavaEE5环境中是不可以用的,主要是用于非EE的环境例如Tomcat,绑定JAX-WS作为web应用的一部分。
The difference to the standard style of exporting servlet-based endpoints is that the lifecycle of the endpoint instances themselves will be managed by Spring here, and that there will be only one JAX-WS servlet defined in web.xml. With the standard Java EE 5 style (as illustrated above), you’ll have one servlet definition per service endpoint, with each endpoint typically delegating to Spring beans (through the use of @Autowired, as shown above).
和标准暴露基于Servlet的端点不同的是端点实例的生命周期在这里是通过spring来管理的,并且将定义一个JAX-WS在web.xml中。对于标准的JavaEE5风格来说(在上面已经声明),你需要一个Servlet定义对于每个服务端点,对于每个服务端点通常定义一个spring的bean(通过上面使用的@Autowired注解)。
Check out https://jax-ws-commons.java.net/spring/ for details on setup and usage style.
参考https://jax-ws-commons.java.net/spring/来了解配置和使用的更多细节。
28.5.4 Accessing web services using JAX-WS
使用JAX-WS来访问web服务
Spring provides two factory beans to create JAX-WS web service proxies, namely LocalJaxWsServiceFactoryBean and JaxWsPortProxyFactoryBean. The former can only return a JAX-WS service class for us to work with. The latter is the full-fledged version that can return a proxy that implements our business service interface. In this example we use the latter to create a proxy for the AccountService endpoint (again):
spring提供了两个工厂bean用于创建JAX-WS的web服务代理,名字为LocalJaxWsServiceFactoryBean和JaxWsPortProxyFactoryBean。前者只能返回一个JAX-WS服务类用于使用。后者是全功能的版本可以返回一个代理实现我们的业务服务接口。在这个例子中我们使用后者来创建一个代理用于AccountService:
<bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="example.AccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
<property name="namespaceUri" value="http://example/"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountServiceEndpointPort"/>
</bean>
Where serviceInterface is our business interface the clients will use. wsdlDocumentUrl is the URL for the WSDL file. Spring needs this a startup time to create the JAX-WS Service. namespaceUri corresponds to the targetNamespace in the .wsdl file. serviceName corresponds to the service name in the .wsdl file. portName corresponds to the port name in the .wsdl file.
这里serviceInterface是我们的业务接口客户端将会使用。wsdlDocumentUrl是一个用于WSDL文件的URL。spring需要他在启动时来创建JAX-WS服务。namespaceUri相关于targetNamespace在wsdl文件中。serviceName相关于服务的名字在wsdl文件中。portName相关于端口在wsdl文件中。
Accessing the web service is now very easy as we have a bean factory for it that will expose it as AccountService interface. We can wire this up in Spring:
访问web服务是十分简单的作为我们有一个bean工厂用于他暴露了作为AccountService接口。我们可以在spring中进行处理:
<bean id="client" class="example.AccountClientImpl">
...
<property name="service" ref="accountWebService"/>
</bean>
From the client code we can access the web service just as if it was a normal class:
来自客户端的代码我们可以访问web服务而把它当成普通的类:
public class AccountClientImpl {
private AccountService service;
public void setService(AccountService service) {
this.service = service;
}
public void foo() {
service.insertAccount(...);
}
}
[Note]
注意
The above is slightly simplified in that JAX-WS requires endpoint interfaces and implementation classes to be annotated with @WebService, @SOAPBinding etc annotations. This means that you cannot (easily) use plain Java interfaces and implementation classes as JAX-WS endpoint artifacts; you need to annotate them accordingly first. Check the JAX-WS documentation for details on those requirements.
上面只是简单的需求用于JAX-WS请求端点接口和实现类使用了@WebService、@SOAPBinding等等注解。这意味着你不能使用普通的Java接口和实现类作为JAX-WS端点,你首先需要声明他们。检查JAX-WS文档有关这些需求的细节。
28.6 JMS
It is also possible to expose services transparently using JMS as the underlying communication protocol. The JMS remoting support in the Spring Framework is pretty basic - it sends and receives on the same thread and in the same non-transactional Session, and as such throughput will be very implementation dependent. Note that these single-threaded and non-transactional constraints apply only to Spring’s JMS remoting support. See Chapter 30, JMS (Java Message Service) for information on Spring’s rich support for JMS-based messaging.
也可以使用JMS作为底层通信技术来暴露服务。JMS远程支持在spring框架中是基本的————他需要收到相同的线程在相同的没有事务的会话中,并且可以独立实现。注意这些单线程和无事务的限制只是应用于spring的JMS远程支持中。见章节30,JMS(Java消息服务)来了解更多有关spring支持JMS消息的内容。
The following interface is used on both the server and the client side.
下面的接口被使用用于服务器和客户端。
package com.foo;
public interface CheckingAccountService {
public void cancelAccount(Long accountId);
}
The following simple implementation of the above interface is used on the server-side.
下面是简单的实现上面的接口用于服务端。
package com.foo;
public class SimpleCheckingAccountService implements CheckingAccountService {
public void cancelAccount(Long accountId) {
System.out.println("Cancelling account [" + accountId + "]");
}
}
This configuration file contains the JMS-infrastructure beans that are shared on both the client and server.
这个配置文件中包含了JMS的基础的bean用于在服务器和客户端之间共享。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://ep-t43:61616"/>
</bean>
<bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="mmm"/>
</bean>
</beans>
28.6.1 Server-side configuration
服务端的配置
On the server, you just need to expose the service object using the JmsInvokerServiceExporter.
在服务端,你只需要暴露服务object通过使用JmsInvokerServiceExporter。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="checkingAccountService"
class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
<property name="serviceInterface" value="com.foo.CheckingAccountService"/>
<property name="service">
<bean class="com.foo.SimpleCheckingAccountService"/>
</property>
</bean>
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="queue"/>
<property name="concurrentConsumers" value="3"/>
<property name="messageListener" ref="checkingAccountService"/>
</bean>
</beans>
package com.foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Server {
public static void main(String[] args) throws Exception {
new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"});
}
}
28.6.2 Client-side configuration
客户端方面的配置
The client merely needs to create a client-side proxy that will implement the agreed upon interface ( CheckingAccountService). The resulting object created off the back of the following bean definition can be injected into other client side objects, and the proxy will take care of forwarding the call to the server-side object via JMS.
客户端几乎不需要创建客户端代理来实现商定的接口( CheckingAccountService)。结果object创建下面的基本定义可以被注入到其他的客户端object并且带来将处理转发调用服务端的object通过JMS。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="checkingAccountService"
class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
<property name="serviceInterface" value="com.foo.CheckingAccountService"/>
<property name="connectionFactory" ref="connectionFactory"/>
<property name="queue" ref="queue"/>
</bean>
</beans>
package com.foo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"com/foo/client.xml", "com/foo/jms.xml"});
CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService");
service.cancelAccount(new Long(10));
}
}
28.7 AMQP
Refer to the Spring AMQP Reference Document 'Spring Remoting with AMQP' section for more information.
参考spring的AMQP的参考文档“使用AMQP实现spring的远程处理”章节来了解更多的内容。
28.8 Auto-detection is not implemented for remote interfaces
自动探测不实现远程接口
The main reason why auto-detection of implemented interfaces does not occur for remote interfaces is to avoid opening too many doors to remote callers. The target object might implement internal callback interfaces like InitializingBean or DisposableBean which one would not want to expose to callers.
自动探测实现接口的主要原因是因为没有远程接口来为调用者提供多个服务入口。目标object可能实现内部的回调接口类似于InitializingBean或DisposableBean其中不需要暴露给调用者。
Offering a proxy with all interfaces implemented by the target usually does not matter in the local case. But when exporting a remote service, you should expose a specific service interface, with specific operations intended for remote usage. Besides internal callback interfaces, the target might implement multiple business interfaces, with just one of them intended for remote exposure. For these reasons, we require such a service interface to be specified.
提供一个代理包含所有的接口实现目标通常不会影响本地。但是当暴露一个远程服务,你应当暴露一个特定的服务接口,使用特定的操作代替远程使用。此外内部的回调接口,目标实现多个业务接口,其中的一个将用于服务暴露。由于一些原因,我们请求一个服务接口是指定的。
This is a trade-off between configuration convenience and the risk of accidental exposure of internal methods. Always specifying a service interface is not too much effort, and puts you on the safe side regarding controlled exposure of specific methods.
这是一个在配置和暴露内部方法的压力之间的平衡。定义一个服务接口是没有太大的影响的,并且放置在安全的位置依赖于对特定方法的控制暴露。
28.9 Considerations when choosing a technology
考虑何时选择一种技术
Each and every technology presented here has its drawbacks. You should carefully consider your needs, the services you are exposing and the objects you’ll be sending over the wire when choosing a technology.
每个技术在这里都有缺点。你应当谨慎考虑你的需求,你需要暴露的服务和你需要通过你选择的技术来发送的object。
When using RMI, it’s not possible to access the objects through the HTTP protocol, unless you’re tunneling the RMI traffic. RMI is a fairly heavy-weight protocol in that it supports full-object serialization which is important when using a complex data model that needs serialization over the wire. However, RMI-JRMP is tied to Java clients: It is a Java-to-Java remoting solution.
当使用RMI,不需要通过HTTP协议来访问object,除非你使用了RMI的隧道。RMI是一个重量级的协议支持全功能的序列化策略在使用一些服务数据模型的时候是很重要的。然而,RMI-JRMP是绑定到Java客户端的:他是一个Java到Java的解决方案。
Spring’s HTTP invoker is a good choice if you need HTTP-based remoting but also rely on Java serialization. It shares the basic infrastructure with RMI invokers, just using HTTP as transport. Note that HTTP invokers are not only limited to Java-to-Java remoting but also to Spring on both the client and server side. (The latter also applies to Spring’s RMI invoker for non-RMI interfaces.)
spring的HTTP调用者是一个好的选择如果你需要基于HTTP的远程调用但是不依赖于Java的序列化。他共享基本的配置和RMI调用者,只是使用HTTP作为传输。注意HTTP调用者不限制于Java到Java的远程调用而是可以用于客户端和服务端的spring。(后者也可以应用于spring的RMI调用者用于非RMI接口)。
Hessian and/or Burlap might provide significant value when operating in a heterogeneous environment, because they explicitly allow for non-Java clients. However, non-Java support is still limited. Known issues include the serialization of Hibernate objects in combination with lazily-initialized collections. If you have such a data model, consider using RMI or HTTP invokers instead of Hessian.
Hessian和Burlap可以提供重要的数据当操作在一个多样的环境中时,因此他们明确允许非Java的客户端。然而,非Java支持确实是一个限制。了解Hibernate序列化的限制配合延迟初始化的集合。如果你有这样一个数据模型,考虑使用RMI或HTTP调用来代替Hessian。
JMS can be useful for providing clusters of services and allowing the JMS broker to take care of load balancing, discovery and auto-failover. By default: Java serialization is used when using JMS remoting but the JMS provider could use a different mechanism for the wire formatting, such as XStream to allow servers to be implemented in other technologies.
JMS可以是很有用的用于提供服务集群和运行JMS消息代理来处理负载均衡、服务发现和自动失效支援。默认的:Java序列化被使用当JMS远程调用但是JMS提供者可以使用一个不同的策略用于格式化,例如XStream允许服务端实现通过其他的技术。
Last but not least, EJB has an advantage over RMI in that it supports standard role-based authentication and authorization and remote transaction propagation. It is possible to get RMI invokers or HTTP invokers to support security context propagation as well, although this is not provided by core Spring: There are just appropriate hooks for plugging in third-party or custom solutions here.
最后,EJB也有一些优于RMI的特性就是他支持标准的基于角色的策略和验证以及远程事务传播。可以获得RMI调用或HTTP调用来支持安全上下文传播,这不是由spring的核心提供的:指示一个第三方的插件提供的解决方案。
28.10 Accessing RESTful services on the Client
访问客户端的RESTful风格的服务
The RestTemplate is the core class for client-side access to RESTful services. It is conceptually similar to other template classes in Spring, such as JdbcTemplate and JmsTemplate and other template classes found in other Spring portfolio projects. RestTemplate’s behavior is customized by providing callback methods and configuring the `HttpMessageConverter used to marshal objects into the HTTP request body and to unmarshal any response back into an object. As it is common to use XML as a message format, Spring provides a MarshallingHttpMessageConverter that uses the Object-to-XML framework that is part of the org.springframework.oxm package. This gives you a wide range of choices of XML to Object mapping technologies to choose from.
RestTemplate是一个核心类用于客户端来访问RESTful服务。他和spring中的其他模板类相似,例如JdbcTemplate和JmsTemplate以及其他模板类在spring的项目中。RestTemplate的行为自定义通过提供的回调方法和配置HttpMessageConverter用于处理object到HTTP请求体重并且输出响应给object。由于通常使用xml作为消息的格式,spring提供了MarshallingHttpMessageConverter使用object-to-xml的框架来自org.springframework.oxm包中。给你一个广泛的选择有关xml到object的映射技术可供选择。
This section describes how to use the RestTemplate and its associated HttpMessageConverters.
这一节描述了如何使用RestTemplate以及他相关的HttpMessageConverters。
28.10.1 RestTemplate
Invoking RESTful services in Java is typically done using a helper class such as Apache HttpComponents HttpClient. For common REST operations this approach is too low level as shown below.
调用RESTful服务在Java中通常使用一些帮助类例如Apache HttpComponents HttpClient。对于普通的http操作这种方式是比较低级的。
String uri = "http://example.com/hotels/1/bookings";
PostMethod post = new PostMethod(uri);
String request = // create booking request content
post.setRequestEntity(new StringRequestEntity(request));
httpClient.executeMethod(post);
if (HttpStatus.SC_CREATED == post.getStatusCode()) {
Header location = post.getRequestHeader("Location");
if (location != null) {
System.out.println("Created new booking at :" + location.getValue());
}
}
RestTemplate provides higher level methods that correspond to each of the six main HTTP methods that make invoking many RESTful services a one-liner and enforce REST best practices.
RestTemplate提供了高级别的方法用于六个主要的HTTP方法使得调用RESTful服务更加方便并且增强了REST服务的体验。
[Note]
注意
RestTemplate has an asynchronous counter-part: see Section 28.10.3, “Async RestTemplate”.
RestTemplate有一个异步的组成部分:见章节28.10.3,“异步的RestTemplate”。
Table 28.1. Overview of RestTemplate methods
RestTemplate方法概览
HTTP Method HTTP方法 | RestTemplate Method RestTemplate方法 |
DELETE | |
GET | getForObject getForEntity |
HEAD | |
OPTIONS | |
POST | postForLocation(String url, Object request, String… urlVariables) postForObject(String url, Object request, Class<T> responseType, String… uriVariables) |
PUT | |
PATCH and others | exchange execute |
The names of RestTemplate methods follow a naming convention, the first part indicates what HTTP method is being invoked and the second part indicates what is returned. For example, the method getForObject() will perform a GET, convert the HTTP response into an object type of your choice and return that object. The method postForLocation() will do a POST, converting the given object into a HTTP request and return the response HTTP Location header where the newly created object can be found. In case of an exception processing the HTTP request, an exception of the type RestClientException will be thrown; this behavior can be changed by plugging in another ResponseErrorHandler implementation into the RestTemplate.
RestTemplate方法的名字参考一个命名规范,首先指示HTTP方法被调用然后指示调用后的返回内容。例如方法getForObject将执行一个GET并且转换HTTP响应为一个object根据你的选择并且返回object。方法postForLocation将执行一个POST转换给定的object为HTTP请求兵器返回HTTP响应的Location的头信息当找到新创建的object。由于http请求的异常处理,RestClientException类型的异常会被抛出;这种行为可以被改变通过插件在另一个ResponseErrorHandler实现到RestTemplate中。
The exchange and execute methods are generalized versions of the more specific methods listed above them and can support additional combinations and methods, like HTTP PATCH. However, note that the underlying HTTP library must also support the desired combination. The JDK HttpURLConnection does not support the PATCH method, but Apache HttpComponents HttpClient version 4.2 or later does. They also enable RestTemplate to read an HTTP response to a generic type (e.g. List<Account>), using a ParameterizedTypeReference, a new class that enables capturing and passing generic type info.
exchange和execute方法是广义的版本有关更多指定方法列出有关他们并且可以支持额外的组合和方法,例如HTTP PATCH。然而注意到底层的HTTP库也可以支持目标组合。JDK的HttpURLConnection不支持PATCH方法,但是Apache HttpComponents HttpClient版本4.2及以后的版本支持。这使得RestTemplate读取HTTP响应为通用类型(例如List<Account>)使用额一个ParameterizedTypeReference,一个新的类允许手机和传递通用的类型信息。
Objects passed to and returned from these methods are converted to and from HTTP messages by HttpMessageConverter instances. Converters for the main mime types are registered by default, but you can also write your own converter and register it via the messageConverters() bean property. The default converter instances registered with the template are ByteArrayHttpMessageConverter, StringHttpMessageConverter, FormHttpMessageConverter and SourceHttpMessageConverter. You can override these defaults using the messageConverters() bean property as would be required if using the MarshallingHttpMessageConverter or MappingJackson2HttpMessageConverter.
object传递并返回来自这些方法转换HTTP信息通过HttpMessageConverter实例。转换器用于主要的mine类型默认是被注册的,但是你可以实现你自己的转换器并且通过messageConverters的bean属性来注册他们。默认的转换器实例注册在模板上是ByteArrayHttpMessageConverter、StringHttpMessageConverter、FormHttpMessageConverter和SourceHttpMessageConverter。你可以覆盖他们通过使用messageConverters的bean属性要求如果使用MarshallingHttpMessageConverter或MappingJackson2HttpMessageConverter。
Each method takes URI template arguments in two forms, either as a String variable length argument or a Map<String,String>. For example,
每个方法有URI模板参数在两个表单中,字符串变量长度参数或一个Map<String,String>。例如,
String result = restTemplate.getForObject(
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");
using variable length arguments and
使用变量长度参数
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
"http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
using a Map<String,String>.
使用Map<String,String>,
To create an instance of RestTemplate you can simply call the default no-arg constructor. This will use standard Java classes from the java.net package as the underlying implementation to create HTTP requests. This can be overridden by specifying an implementation of ClientHttpRequestFactory. Spring provides the implementation HttpComponentsClientHttpRequestFactory that uses the Apache HttpComponents HttpClient to create requests. HttpComponentsClientHttpRequestFactory is configured using an instance of org.apache.http.client.HttpClient which can in turn be configured with credentials information or connection pooling functionality.
为了创建RestTemplate的实例你可以简单的调用默认的无参数构造方法。这将会使用标准的Java类来自java.net包作为底层实现来创建HTTP请求。这可以被覆盖通过定义一个ClientHttpRequestFactory的实现。spring提供了实现HttpComponentsClientHttpRequestFactory使用Apache HttpComponents HttpClient来创建请求。HttpComponentsClientHttpRequestFactory被配置使用一个org.apache.http.client.HttpClient实例并且可以配置使用连接池功能的内容。
[Tip]
提示
Note that the java.net implementation for HTTP requests may raise an exception when accessing the status of a response that represents an error (e.g. 401). If this is an issue, switch to HttpComponentsClientHttpRequestFactory instead.
注意用于HTTP请求的java.net实现可能抛出一个异常当访问一个响应结果为错误的状态(例如401)。如果这是一个问题,请使用HttpComponentsClientHttpRequestFactory作为替代。
The previous example using Apache HttpComponents HttpClient directly rewritten to use the RestTemplate is shown below
前面的例子直接使用Apache HttpComponents HttpClient,下面使用RestTemplate来重新他们。
uri = "http://example.com/hotels/{id}/bookings";
RestTemplate template = new RestTemplate();
Booking booking = // create booking object
URI location = template.postForLocation(uri, booking, "1");
To use Apache HttpComponents instead of the native java.net functionality, construct the RestTemplate as follows:
为了使用Apache的组件来代替本地的java.net中的功能,构建一个RestTemplate如下:
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
[Tip]
提示
Apache HttpClient supports gzip encoding. To use it, construct a HttpComponentsClientHttpRequestFactory like so:
Apache的HttpClient支持gzip编码。为了使用他,构建一个HttpComponentsClientHttpRequestFactory如下:
HttpClient httpClient = HttpClientBuilder.create().build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
The general callback interface is RequestCallback and is called when the execute method is invoked.
通常的回调接口是RequestCallback并且被调用当执行方法被调用的时候。
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, String... urlVariables)
// also has an overload with urlVariables as a Map<String, String>.
The RequestCallback interface is defined as
public interface RequestCallback {
void doWithRequest(ClientHttpRequest request) throws IOException;
}
and allows you to manipulate the request headers and write to the request body. When using the execute method you do not have to worry about any resource management, the template will always close the request and handle any errors. Refer to the API documentation for more information on using the execute method and the meaning of its other method arguments.
并且允许你来处理请求头信息并且写入请求体。当使用执行方法你不需要考虑任何资源的管理,模板将会关闭请求和处理任何错误。参考API文档来了解执行方法和其他方法参数的更多信息。
Working with the URI
操作URI
For each of the main HTTP methods, the RestTemplate provides variants that either take a String URI or java.net.URI as the first argument.
对于每个主要的HTTP方法,RestTemplate提供了不同的方法对于String的URI或java.net.URI作为第一个参数。
The String URI variants accept template arguments as a String variable length argument or as a Map<String,String>. They also assume the URL String is not encoded and needs to be encoded. For example the following:
String的URI变量接收模板参数作为字符串变量长度参数或作为一个Map<String,String>。他们也假设URI字符串没有被编码和需要被编码。例如:
restTemplate.getForObject("http://example.com/hotel list", String.class);
will perform a GET on http://example.com/hotel%20list. That means if the input URL String is already encoded, it will be encoded twice — i.e. http://example.com/hotel%20list will become http://example.com/hotel%2520list. If this is not the intended effect, use the java.net.URI method variant, which assumes the URL is already encoded is also generally useful if you want to reuse a single (fully expanded) URI multiple times.
将执行GET请求对于http://example.com/hotel%20list。这意味着如果输入的URL字符串已经被编码,他将被编码两次————例如http://example.com/hotel%20list将成为http://example.com/hotel%2520list。如果这不是预期的效果,使用java.net.URI方法变量,假设URL是已经编码通常对你来说是有用的如果你希望重新使用URL多次(高扩展)。
The UriComponentsBuilder class can be used to build and encode the URI including support for URI templates. For example you can start with a URL String:
UriComponentsBuilder类可以被使用来构建并且编码URI编码支持用于URI模板。例如你可以使用一个URL字符串:
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://example.com/hotels/{hotel}/bookings/{booking}").build()
.expand("42", "21")
.encode();
URI uri = uriComponents.toUri();
Or specify each URI component individually:
或独立指定每个URI组件:
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()
.expand("42", "21")
.encode();
URI uri = uriComponents.toUri();
Dealing with request and response headers
处理请求和响应的头信息
Besides the methods described above, the RestTemplate also has the exchange() method, which can be used for arbitrary HTTP method execution based on the HttpEntity class.
在下面描述的方法中,RestTemplate也有一个exchange方法,可以被用于任意的HTTP方法执行基于HttpEntity类。
Perhaps most importantly, the exchange() method can be used to add request headers and read response headers. For example:
或许是最重要的,exchange方法可以被用于添加请求头信息和读取响应头。例如:
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
HttpEntity<String> response = template.exchange(
"http://example.com/hotels/{hotel}",
HttpMethod.GET, requestEntity, String.class, "42");
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
In the above example, we first prepare a request entity that contains the MyRequestHeader header. We then retrieve the response, and read the MyResponseHeader and body.
在上面的例子中,我们首先准备一个请求entity包含MyRequestHeader头信息。我们接收响应并且读取MyResponseHeader和响应体。
Jackson JSON Views support
It is possible to specify a Jackson JSON View to serialize only a subset of the object properties. For example:
可以定义一个Jackson的JSON视图用于序列化一个object属性的子集。例如:
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
HttpEntity<MappingJacksonValue> entity = new HttpEntity<MappingJacksonValue>(value);
String s = template.postForObject("http://example.com/user", entity, String.class);
28.10.2 HTTP Message Conversion
HTTP消息转换
Objects passed to and returned from the methods getForObject(), postForLocation(), and put() are converted to HTTP requests and from HTTP responses by HttpMessageConverters. The HttpMessageConverter interface is shown below to give you a better feel for its functionality
object传递并返回来自方法getForObject、postForLocation和put用于方便的转换HTTP请求和HTTP响应通过HttpMessageConverters。HttpMessageConverter接口展示如下用于显示他的功能。
public interface HttpMessageConverter<T> {
// Indicate whether the given class and media type can be read by this converter.
boolean canRead(Class<?> clazz, MediaType mediaType);
// Indicate whether the given class and media type can be written by this converter.
boolean canWrite(Class<?> clazz, MediaType mediaType);
// Return the list of MediaType objects supported by this converter.
List<MediaType> getSupportedMediaTypes();
// Read an object of the given type from the given input message, and returns it.
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
// Write an given object to the given output message.
void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}
Concrete implementations for the main media (mime) types are provided in the framework and are registered by default with the RestTemplate on the client-side and with AnnotationMethodHandlerAdapter on the server-side.
一致的实现用于主要的媒体类型被提供在框架中并且默认被注册通过使用RestTemplate在客户端方面配合服务端的AnnotationMethodHandlerAdapter。
The implementations of HttpMessageConverters are described in the following sections. For all converters a default media type is used but can be overridden by setting the supportedMediaTypes bean property
HttpMessageConverters的实现被描述在下面的章节中。对于所有的转换器一个默认的媒体类型被使用但是可以被覆盖通过设置supportedMediaTypes的bean属性。
StringHttpMessageConverter
An HttpMessageConverter implementation that can read and write Strings from the HTTP request and response. By default, this converter supports all text media types ( text/*), and writes with a Content-Type of text/plain.
一个HttpMessageConverter实现可以读写字符串来自HTTP请求和响应。默认的,这个转换器支持所有的文本媒体类型并且写入text/plain的Content-Type。
FormHttpMessageConverter
An HttpMessageConverter implementation that can read and write form data from the HTTP request and response. By default, this converter reads and writes the media type application/x-www-form-urlencoded. Form data is read from and written into a MultiValueMap<String, String>.
一个HttpMessageConverter实现可以读写来自HTTP请求和响应的数据。默认的,这个转换器读写媒体类型application/x-www-form-urlencoded。表单数据读写为MultiValueMap<String, String>类型的数据。
ByteArrayHttpMessageConverter
An HttpMessageConverter implementation that can read and write byte arrays from the HTTP request and response. By default, this converter supports all media types ( */*), and writes with a Content-Type of application/octet-stream. This can be overridden by setting the supportedMediaTypes property, and overriding getContentType(byte[]).
一个HttpMessageConverter实现可以读写字节数组来自HTTP请求和响应。默认的这个转换器支持所有的媒体类型并且写入application/octet-stream的Content-Type。这可以被覆盖通过设置supportedMediaTypes属性和覆盖getContentType方法。
MarshallingHttpMessageConverter
An HttpMessageConverter implementation that can read and write XML using Spring’s Marshaller and Unmarshaller abstractions from the org.springframework.oxm package. This converter requires a Marshaller and Unmarshaller before it can be used. These can be injected via constructor or bean properties. By default this converter supports ( text/xml) and ( application/xml).
一个HttpMessageConverter实现可以读写xml使用spring的Marshaller和Unmarshaller抽象来自org.springframework.oxm包中。这个转换器要求一个Marshaller和Unmarshaller在他们被使用之前。可以被注入通过构造器或bean属性。默认的这个转换器支持( text/xml)和( application/xml)。
MappingJackson2HttpMessageConverter
An HttpMessageConverter implementation that can read and write JSON using Jackson’s ObjectMapper. JSON mapping can be customized as needed through the use of Jackson’s provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports ( application/json).
一个HttpMessageConverter实现可以读写JSON使用Jackson的ObjectMapper。JSON匹配可以被自定义作为需要通过使用Jackson提供的注解。当需要进一步的控制时,一个自定义的ObjectMapper可以被注入通过ObjectMapper属性当自定义JSON序列化和反序列化需要提供指定类型。默认的这个转换器支持( application/json)。
MappingJackson2XmlHttpMessageConverter
An HttpMessageConverter implementation that can read and write XML using Jackson XML extension’s XmlMapper. XML mapping can be customized as needed through the use of JAXB or Jackson’s provided annotations. When further control is needed, a custom XmlMapper can be injected through the ObjectMapper property for cases where custom XML serializers/deserializers need to be provided for specific types. By default this converter supports ( application/xml).
一个HttpMessageConverter实现可以读写xml使用Jackson的xml外部的XmlMapper。xml匹配可以自定义作为需要通过使用JAXB或Jackson提供的注解。当需要进一步的控制,一个自定义的XmlMapper可以被注入通过ObjectMapper属性用于自定义xml序列化和反序列化需要提供指定的类型。默认的转换器支持( application/xml)。
SourceHttpMessageConverter
An HttpMessageConverter implementation that can read and write javax.xml.transform.Source from the HTTP request and response. Only DOMSource, SAXSource, and StreamSource are supported. By default, this converter supports ( text/xml) and ( application/xml).
一个HttpMessageConverter实现可以读写java.xml.transform.Source来自HTTP请求和响应。只有DOMSource、SAXSource和StreamSource被支持。默认的转换器支持( text/xml)和( application/xml)。
BufferedImageHttpMessageConverter
An HttpMessageConverter implementation that can read and write java.awt.image.BufferedImage from the HTTP request and response. This converter reads and writes the media type supported by the Java I/O API.
一个HttpMessageConverter实现可以读写java.awt.image.BufferedImage来自HTTP请求和响应。这个转换器读写媒体类型支持Java的IO的API。
28.10.3 Async RestTemplate
异步的RestTemplate
Web applications often need to query external REST services those days. The very nature of HTTP and synchronous calls can lead up to challenges when scaling applications for those needs: multiple threads may be blocked, waiting for remote HTTP responses.
web应用通常需要查询外部的REST服务。现在的HTTP和同步调用面临着改变当分布式应用被需要的时候,多线程可能被阻塞,需要远程的HTTP响应。
AsyncRestTemplate and Section 28.10.1, “RestTemplate”'s APIs are very similar; see Table 28.1, “Overview of RestTemplate methods”. The main difference between those APIs is that AsyncRestTemplate returns ListenableFuture wrappers as opposed to concrete results.
AsyncRestTemplate和章节28.10.1,“RestTemplate”的API是非常相似的;见表格28.1,“RestTemplate方法的概述”。他们的主要不同是AsyncRestTemplate返回ListenableFuture处理包裹相对于同步的结果。
The previous RestTemplate example translates to:
之前的RestTemplate例子转换为:
// async call
Future<ResponseEntity<String>> futureEntity = template.getForEntity(
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
// get the concrete result - synchronous call
ResponseEntity<String> entity = futureEntity.get();
ListenableFuture accepts completion callbacks:
ListenableFuture接受完成的回调:
ListenableFuture<ResponseEntity<String>> futureEntity = template.getForEntity(
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
// register a callback
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onSuccess(ResponseEntity<String> entity) {
//...
}
@Override
public void onFailure(Throwable t) {
//...
}
});
[Note]
注意
The default AsyncRestTemplate constructor registers a SimpleAsyncTaskExecutor for executing HTTP requests. When dealing with a large number of short-lived requests, a thread-pooling TaskExecutor implementation like ThreadPoolTaskExecutor may be a good choice.
默认的AsyncRestTemplate构造器注册一个SimpleAsyncTaskExecutor用于执行HTTP请求。当处理大数量的短时请求、一个线程池TaskExecutor实现类似于ThreadPoolTaskExecutor可能是一个好的选择。
See the ListenableFuture javadocs and AsyncRestTemplate javadocs for more details.
参考ListenableFuture和AsyncRestTemplate的javadocs了解更多内容。