Apache CXF+SpringによるWebサービス開発(JAX-RS編)


Apache CXFとSpring Frameworkを用いたJAX-RS仕様に準拠したWebサービス開発について紹介します。

なお、ここで紹介するソースコード例は、筆者環境の都合上、次の環境を前提とします。

  • Java SE6
  • Apache CXF 2.3.3
  • Spring Framework 3.0.5
  • Apache maven 3.0(jetty plugin等を利用)

また、JAX-RS仕様やApache CXFについては、wikiにまとめてありますので、詳細についてはそちらを参照下さい。

Webサービス(プロバイダ)を作成する

それでは、早速Webサービスを作成します。
JAX-RS仕様に基づき作成するため、基本的なポイントは大体同じですが、Aapche CXFとSpringで開発を進める場合の手順を説明します。

  1. リソースとして公開するインタフェースを作成し、JAX-RSのアノテーションを付与する(リソースクラスに直接アノテーションを付与すれば、インタフェースを作成しなくてもよい)
  2. 公開インタフェースを実装したクラスを作成する
  3. 公開するメソッドの引数、戻り値の型を作成する
  4. CXFのJAX-RS フロントエンドとリソースクラスを Spring Bean として定義する
  5. CXFとSpring を統合するための web.xml を作成する

リソースとして公開するインタフェースを作成し、JAX-RSのアノテーションを付与する

手順にも書きましたが、必ずインタフェースを作成する必要はありません。 しかし、インタフェースを作成することで、公開するリソースのサービスの仕様と実装をすっきりと分離することができるため。筆者はこの方法を好みます。

src/main/java/jp/opensquare/sandbox/cxf/jaxrs/provider/CustomerResource.java

package jp.opensquare.sandbox.cxf.jaxrs.provider;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/customer")
public interface CustomerResource {

	@GET
	@Path("/get/{id}")
	@Produces({"application/json", "application/xml"})
	Customer getCustomer(@PathParam("id") String id);

	@GET
	@Path("/all")
	@Produces({"application/json", "application/xml"})
	Customers getCustomers();

	@POST
	@Path("/create")
	@Produces({"application/json", "application/xml"})
	Customer createCustomer(Customer customer);

	@PUT
	@Path("/update")
	void updateCustomer(Customer customer);

	@DELETE
	@Path("/delete/{id}")
	void removeCustomer(@PathParam("id") String id);
}

ここで注意すべき点は、updateCustomer()メソッドやdeleteCustomer()メソッドのように戻り値を返さないメソッドの場合は、@Producesアノテーションは付与してはいけません。(付与すると実行時に例外が発生します。)

公開インタフェースを実装したクラスを作成する

公開インタフェースに対する実装を行います。

src/main/java/jp/opensquare/sandbox/cxf/jaxrs/provider/CustomerResourceImpl.java

package jp.opensquare.sandbox.cxf.jaxrs.provider;

public class CustomerResourceImpl implements CustomerResource {
	@Override
	public Customer getCustomer(String id) {
		return CustomerRepository.getInstance().getCustomer(id);
	}

	@Override
	public Customers getCustomers() {
		return new Customers(CustomerRepository.getInstance().getCustomers());
	}

	@Override
	public Customer createCustomer(Customer customer) {
		return CustomerRepository.getInstance().createCustomer(customer);
	}

	@Override
	public void updateCustomer(Customer customer) {
		CustomerRepository.getInstance().updateCustomer(customer);
	}
	
	@Override
	public void removeCustomer(String id) {
		CustomerRepository.getInstance().removeCustomer(id);
	}
}

インタフェースにJAX-RSのアノテーションを付与しているので、この実装クラスではJAX-RSのアノテーションを付与する必要はありません。

公開するメソッドの引数、戻り値の型を作成する

プリミティブ型、プリミティブラッパ型以外の型を公開するメソッドの引数、戻り値の型として使用する場合は、その型を作成する必要があります。

src/main/java/jp/opensquare/sandbox/cxf/jaxrs/provider/Customer.java

package jp.opensquare.sandbox.cxf.jaxrs.provider;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "customer")
public class Customer {
	private String id;
	private String name;

	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public Customer() {
		super();
	}
	
	public Customer(String id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "id: " + this.id +" , name:" + this.name;
	}
}

src/main/java/jp/opensquare/sandbox/cxf/jaxrs/provider/Customers.java

package jp.opensquare.sandbox.cxf.jaxrs.provider;

import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "customers")
public class Customers {
	private List<Customer> customer;

	public List<Customer> getCustomer() {
		return customer;
	}

	public void setCustomer(List<Customer> data) {
		this.customer = data;
	}

	public Customers() {
		super();
	}
	public Customers(List<Customer> customer) {
		super();
		this.customer = customer;
	}
	
}

CXFのJAX-RS フロントエンドとリソースクラスを Spring Bean として定義する

ここからは、Webサービスの配備に関連する設定の説明に移ります。CXFで用意されているフロントエンドを使用してWebサービスを定義します。見てわかるようにフロントエンドには、サービスの実装クラスのFQCNとサービスをマッピングするアドレスを指定するだけです。

src/main/resources/provider-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jaxrs="http://cxf.apache.org/jaxrs"
	xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

	<jaxrs:server id="customerResource" address="/">
		<jaxrs:serviceBeans>
			<ref bean="customerResourceImpl" />
		</jaxrs:serviceBeans>
	</jaxrs:server>

	<bean id="customerResourceImpl" class="jp.opensquare.sandbox.cxf.jaxrs.provider.CustomerResourceImpl" />	
</beans>

CXFとSpring を統合するための web.xml を作成する

web.xmlには、次のようにSpringのBean定義のロード定義とCXFServletの定義を行うだけです。

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:/provider-beans.xml</param-value>
	</context-param>

	<listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	</listener>

	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<display-name>CXF Servlet</display-name>
		<servlet-class>
			org.apache.cxf.transport.servlet.CXFServlet
		</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/resources/*</url-pattern>
	</servlet-mapping>
</web-app>

以上で、サービス開発は完了です。

リクエスタの開発

JAX-RSの場合は、アプリケーションサーバにサービスのデプロイが完了すれば、ブラウザからhttp://localhost:8080/cxf-jaxrs/resources/customer/get/001のようにアクセスするだけで簡単に動作確認を行うことができますが、Javaのリクエスタのサンプルを用意しました。

src/main/resources/requestor-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jaxrs="http://cxf.apache.org/jaxrs"
	xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

	<jaxrs:client id="customerResource"
		address="http://localhost:8080/cxf-jaxrs/resources/"
		serviceClass="jp.opensquare.sandbox.cxf.jaxrs.provider.CustomerResource"
		inheritHeaders="true">
	</jaxrs:client>	
</beans>

src/main/java/jp/opensquare/sandbox/cxf/jaxrs/requestor/ServiceRequestor.java

package jp.opensquare.sandbox.cxf.jaxrs.requestor;

import jp.opensquare.sandbox.cxf.jaxrs.provider.Customer;
import jp.opensquare.sandbox.cxf.jaxrs.provider.CustomerResource;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ServiceRequestor {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("requestor-beans.xml");

		CustomerResource customerResource = (CustomerResource) context.getBean("customerResource");
		
		System.out.println("=== @GET RESULT");
		System.out.println(customerResource.getCustomer("001"));

		System.out.println("=== @POST RESULT");
		Customer customer = customerResource.createCustomer(new Customer("999", "create customer"));
		for(Customer item : customerResource.getCustomers().getCustomer()) {
			System.out.println(item);
		}

		System.out.println("=== @PUT RESULT");
		customer.setName("update customer");
		customerResource.updateCustomer(customer);
		for(Customer item : customerResource.getCustomers().getCustomer()) {
			System.out.println(item);
		}
		
		System.out.println("=== @DELETE RESULT");
		customerResource.removeCustomer(customer.getId());
		for(Customer item : customerResource.getCustomers().getCustomer()) {
			System.out.println(item);
		}
		System.exit(0);
	}
}

ソースコードの取得

紹介したソースコードは、https://github.com/rising3/cxf-jaxrsから取得できます。


あわせてお読みください