Um guia para o Apache CXF com Spring
1. Visão geral
Este tutorial se concentra na configuração eusing the Apache CXF framework together with Spring - com configuração Java ou XML.
É o segundo de uma série no Apache CXF; the first one enfocou os fundamentos do CXF como uma implementação das APIs padrão JAX-WS.
2. Dependências do Maven
Semelhante ao tutorial anterior, as duas dependências a seguir precisam ser incluídas:
org.apache.cxf
cxf-rt-frontend-jaxws
3.1.6
org.apache.cxf
cxf-rt-transports-http
3.1.6
Para as versões mais recentes dos artefatos do Apache CXF, verifiqueapache-cxf.
Além disso, são necessárias as seguintes dependências para dar suporte ao Spring:
org.springframework
spring-context
4.3.1.RELEASE
org.springframework
spring-webmvc
4.3.1.RELEASE
As versões mais recentes dos artefatos do Spring podem ser encontradashere.
Finalmente, como configuraremos programaticamente o aplicativo usando a API Java Servlet 3.0+ em vez de um descritor de implantação tradicionalweb.xml, precisaremos do artefato abaixo:
javax.servlet
javax.servlet-api
3.1.0
This é onde podemos encontrar a versão mais recente da API Servlet.
3. Componentes do lado do servidor
Vamos agora dar uma olhada nos componentes que precisam estar presentes no lado do servidor para publicar o endpoint de serviço da web.
3.1. InterfaceWebApplicationInitilizer
A interfaceWebApplicationInitializer é implementada para configurar programaticamente a interfaceServletContext para o aplicativo. Quando presente no caminho de classe, seu métodoonStartup é automaticamente chamado pelo contêiner de servlet e, a partir daí,ServletContext é instanciado e inicializado.
Aqui está como uma classe é definida para implementar a interfaceWebApplicationInitializer:
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
// Method implementation
}
}
O métodoonStartup() é implementado usando trechos de código mostrados abaixo.
Primeiro, um contexto de aplicativo Spring é criado e configurado para registrar uma classe que contém metadados de configuração:
AnnotationConfigWebApplicationContext context
= new AnnotationConfigWebApplicationContext();
context.register(ServiceConfiguration.class);
A classeServiceConfiguration é anotada com a anotação@Configuration para fornecer definições de bean. Esta classe é discutida na próxima subseção.
O seguinte fragmento mostra como o contexto do aplicativo Spring é adicionado ao contexto do servlet:
container.addListener(new ContextLoaderListener(context));
A classeCXFServlet, que é definida pelo Apache CXF, é gerada e registrada para lidar com as solicitações de entrada:
ServletRegistration.Dynamic dispatcher
= container.addServlet("dispatcher", new CXFServlet());
O contexto do aplicativo carrega elementos Spring definidos em um arquivo de configuração. Nesse caso, o nome do servlet écxf, portanto, o contexto procura por esses elementos em um arquivo chamadocxf-servlet.xml por padrão.
Por fim, o servlet CXF é mapeado para uma URL relativa:
dispatcher.addMapping("/services");
3.2. O bom e velhoweb.xml
Alternativamente, se quisermos fazer uso de um descritor de implantação (um tanto antiquado) em vez da interfaceWebApplicationInitilizer, o arquivoweb.xml correspondente deve conter as seguintes definições de servlet:
cxf
org.apache.cxf.transport.servlet.CXFServlet
1
cxf
/services/*
3.3. ClasseServiceConfiguration
Vamos agora dar uma olhada na configuração do serviço - primeiro um esqueleto básico que inclui as definições de bean para o endpoint do serviço da web:
@Configuration
public class ServiceConfiguration {
// Bean definitions
}
O primeiro bean necessário é oSpringBus - que fornece extensões para o Apache CXF funcionar com o Spring Framework:
@Bean
public SpringBus springBus() {
return new SpringBus();
}
Um beanEnpointImpl também precisa ser criado usando o beanSpringBus e um serviço da webimplementor. Este bean é usado para publicar o terminal no endereço HTTP fornecido:
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), new exampleImpl());
endpoint.publish("http://localhost:8080/services/example");
return endpoint;
}
A classeexampleImpl é usada para implementar a interface de serviço da web. Sua definição é dada na próxima subseção.
Como alternativa, também podemos declarar o terminal do servidor em um arquivo de configuração XML. Especificamente, o arquivocxf-servlet.xml abaixo funciona com o descritor de implantaçãoweb.xml conforme foi definido na subseção 3.1 e descreve exatamente o mesmo endpoint:
Observe que o arquivo de configuração XML é nomeado após o nome do servlet definido no descritor de implantação, que écxf.
3.4. Definições de tipo
A seguir - aqui está a definição deimplementor que já foi mencionado na subseção anterior:
@WebService(endpointInterface = "com.example.cxf.spring.example")
public class exampleImpl implements example {
private int counter;
public String hello(String name) {
return "Hello " + name + "!";
}
public String register(Student student) {
counter++;
return student.getName() + " is registered student number " + counter;
}
}
Esta classe fornece uma implementação para a interface de endpointexample que o Apache CXF incluirá nos metadados WSDL publicados:
@WebService
public interface example {
String hello(String name);
String register(Student student);
}
Tanto a interface do endpoint quanto aimplementor usam a classeStudent, que é definida da seguinte maneira:
public class Student {
private String name;
// constructors, getters and setters
}
4. Feijões do lado do cliente
Para aproveitar as vantagens do Spring Framework, declaramos um bean em uma classe anotada@Configuration:
@Configuration
public class ClientConfiguration {
// Bean definitions
}
Um bean com o nome declient é definido:
@Bean(name = "client")
public Object generateProxy() {
return proxyFactoryBean().create();
}
O beanclient representa um proxy para o serviço da webexample. Ele é criado por uma chamada para o métodocreate em um beanJaxWsProxyFactoryBean, uma fábrica para a criação de proxies JAX-WS.
O objetoJaxWsProxyFactoryBean é criado e configurado pelo seguinte método:
@Bean
public JaxWsProxyFactoryBean proxyFactoryBean() {
JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
proxyFactory.setServiceClass(example.class);
proxyFactory.setAddress("http://localhost:8080/services/example");
return proxyFactory;
}
A propriedadeserviceClass da fábrica denota a interface de serviço da web, enquanto a propriedadeaddress indica o endereço URL para o proxy fazer invocações remotas.
Também para os beans Spring no lado do cliente, pode-se reverter para um arquivo de configuração XML. Os seguintes elementos declaram os mesmos beans que acabamos de configurar programaticamente acima:
5. Casos de teste
Esta seção descreve os casos de teste usados para ilustrar o suporte do Apache CXF para Spring. Os casos de teste são definidos em uma classe chamadaStudentTest.
Primeiro, precisamos carregar um contexto de aplicativo Spring da classe de configuraçãoServiceConfiguration mencionada anteriormente e armazená-lo em cache no campocontext:
private ApplicationContext context
= new AnnotationConfigApplicationContext(ClientConfiguration.class);
Em seguida, um proxy para a interface do terminal em serviço é declarado e carregado no contexto do aplicativo:
private example exampleProxy = (example) context.getBean("client");
Este proxyexample será usado nos casos de teste descritos abaixo.
No primeiro caso de teste, provamos que quando o métodohello é chamado localmente no proxy, a resposta é exatamente a mesma que o endpointimplementor retorna do serviço da web remoto:
@Test
public void whenUsingHelloMethod_thenCorrect() {
String response = exampleProxy.hello("John Doe");
assertEquals("Hello John Doe!", response);
}
No segundo caso de teste, os alunos se registram em cursos de exemplo invocando localmente o métodoregister no proxy, que por sua vez chama o serviço da web. Esse serviço remoto calculará os números dos alunos e os devolverá ao chamador. O seguinte snippet de código confirma o que esperamos:
@Test
public void whenUsingRegisterMethod_thenCorrect() {
Student student1 = new Student("Adam");
Student student2 = new Student("Eve");
String student1Response = exampleProxy.register(student1);
String student2Response = exampleProxy.register(student2);
assertEquals("Adam is registered student number 1", student1Response);
assertEquals("Eve is registered student number 2", student2Response);
}
6. Teste de integração
Para ser implementado como um aplicativo da Web em um servidor, os snippets de código neste tutorial precisam ser empacotados em um arquivo WAR primeiro. Isso pode ser obtido declarando a propriedadepackaging no arquivo POM:
war
O trabalho de empacotamento é implementado pelo plug-in Maven WAR:
maven-war-plugin
2.6
false
Este plug-in empacota o código-fonte compilado em um arquivo WAR. Como configuramos o contexto do servlet usando o código Java, o descritor de implantação tradicionalweb.xml não precisa existir. Como resultado, a propriedadefailOnMissingWebXml deve ser definida comofalse para evitar falha quando o plugin é executado.
Podemos seguirthis link para a versão mais recente do plugin Maven WAR.
Para ilustrar as operações do serviço da web, criamos um teste de integração. Esse teste gera primeiro um arquivo WAR e inicia um servidor incorporado, depois faz com que os clientes invoquem o serviço da Web, verifica as respostas subseqüentes e finalmente interrompe o servidor.
Os seguintes plugins precisam ser incluídos no arquivo Maven POM. Para obter mais detalhes, verifiquethis Integration Testing tutorial.
Aqui está o plugin Maven Surefire:
maven-surefire-plugin
2.19.1
StudentTest.java
A última versão deste plugin pode ser encontradahere.
Uma seçãoprofile comid deintegration é declarada para facilitar o teste de integração:
integration
...
O plugin Maven Cargo está incluído no perfilintegration:
org.codehaus.cargo
cargo-maven2-plugin
1.5.0
jetty9x
embedded
localhost
8080
start-server
pre-integration-test
start
stop-server
post-integration-test
stop
Observe que as propriedades de configuraçãocargo.hostnameecargo.servlet.port foram incluídas apenas por uma questão de clareza. Essas propriedades de configuração podem ser deixadas de fora sem nenhum impacto no aplicativo, pois seus valores são os mesmos que os valores padrão. Este plug-in inicia o servidor, aguarda conexões e finalmente interrompe o servidor para liberar recursos do sistema.
This link nos permite verificar a versão mais recente do plugin Maven Cargo.
O plugin Maven Surefire é declarado novamente, dentro do perfilintegration, para substituir sua configuração na seçãobuild principal e para executar os casos de teste descritos na seção anterior:
maven-surefire-plugin
2.19.1
integration-test
test
none
Agora todo o processo pode ser executado pelo comando:mvn -Pintegration clean install.
7. Conclusão
Este tutorial ilustrou o suporte do Apache CXF para o Spring. Em particular, foi mostrado como um serviço da Web pode ser publicado usando um arquivo de configuração do Spring e como um cliente pode interagir com esse serviço por meio de um proxy criado por uma fábrica de proxy do Apache CXF, declarado em outro arquivo de configuração.
A implementação de todos esses exemplos e trechos de código pode ser encontrada emthe linked GitHub project.