一、首先了解两个概念,JAX-WS与JAX-RS的区别
JAX-WS:java TM API forXML_Based web Service
JAX-RS:java TM API forRestFul web Service
两者是不同风格的
SOA(Service Oriented Architecture)架构:面向服务的架构,它是一种设计思想,服务之间通过相互依赖最终提供系一系列的功能。
-----微服务架构大部分思想都是由SOA而来,只是微服务架构更具组件化,还有一些架构建模
JAX-RS:JAVA EE6 引进的新技术,支持rest架构风格创建web服务
JAX-WS:基于xml web service的java Api 允一个远程调用可以转换为一个基于XML的协议例如SOAP,在使用JAX-WS过程中,开发者不需要编写任何生成和处理SOAP消息的代码。JAX-WS的运行时实现会将这些API的调用转换成为对应的SOAP消息。
二、目前比较主流的web服务实现方法
Apache Axis1、Apache Axis2、Codehaus XFire、Apache CXF、Apache Wink、Jboss RESTEasy、sun JAX-WS(最简单、方便)、阿里巴巴 Dubbo等
Rest:简单易用,效率高
Soap:成熟度高,安全性高
(个人观点-----无理由)
Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输入输出,可以和Spring框架无缝衔接。
正文
需要引入的jar包
可能会出现springboot版本会和cxf版本出现不兼容的情况,需要注意,不匹配会出现找不到cxf下的类
<version>2.5.1</version>
<cxf.version>3.4.0</cxf.version>
<!-- CXF webservice -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.0</version>
</dependency>
<!-- 提供JacksonJsonProvider,非必需,可以使用其他json转化替换-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<!--soup 是一款Java 的HTML解析器,
可直接解析某个URL地址、HTML文本内容。
它提供了一套非常省力的API,
可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>${cxf.version}</version>
</dependency>
创建实体类,
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
@XmlRootElement(name = "emplyee")
public class EmployeeXML implements Serializable {
private Integer empid;
private String deptno;
private String empjob;
private String empname;
private String EntryDate;
private Integer empstatus;
}
基于soap的整合
cxf配置类:
import com.example.wxwsofto.service.webService.cxf.ProspectService;
import com.example.wxwsofto.service.webService.cxf.impl.ProspectServiceImpl;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import javax.xml.ws.Endpoint;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* http://localhost:8081/cxf/prosService?wsdl 访问链接必须加上wsdl否则会抛出异常
*/
@Configuration
public class cxfConfig {
@Bean
public ProspectService myService() {
return new ProspectServiceImpl();
}
//CXFServlet()用来拦截cxf,拦截路径/cxf/*
@Bean
public ServletRegistrationBean createServletRegistrationBean(){
return new ServletRegistrationBean(new CXFServlet(),"/cxf/*");
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus(){
return new SpringBus();
}
@Bean
@Qualifier("prosService")
public Endpoint endpoint(){
EndpointImpl endpoint = new EndpointImpl(springBus(),myService());
endpoint.getInInterceptors().add(new AuthInterceptorJAWS());//添加校验拦截器
endpoint.publish("/prosService"); //暴露的api
return endpoint;
}
@Bean("jsonProvider") // 构造一个json转化bean,因为后面需要用这个bean配置json转化,所以给他取个名
public JacksonJsonProvider getJacksonJsonProvider() {
return new JacksonJsonProvider();
}
}
webservice 接口:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* webservice的接口类与服务实现类的namespace保持一致 否则会找不到
*/
@WebService(targetNamespace = "http://prospect.service.com",
name = "prosService")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML}) //返回类型
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML}) //请求类型
public interface ProspectService {
@WebMethod
@WebResult(name = "String",targetNamespace = "")
String sendMessage(@WebParam(name = "message") String message);
}
import cn.hutool.core.lang.UUID;
import com.example.wxwsofto.service.webService.cxf.ProspectService;
import org.springframework.stereotype.Component;
import javax.jws.WebService;
@Component
@WebService(
targetNamespace = "http://prospect.service.com", // 与接口中的命名空间一致,一般是接口的包名倒
endpointInterface = "com.example.wxwsofto.service.webService.cxf.ProspectService",
serviceName ="prosService" ) //暴露的接口名字
public class ProspectServiceImpl implements ProspectService {
@Override
public String sendMessage(String message) {
return UUID.fastUUID().toString();
}
}
服务的调用:
import com.example.wxwsofto.service.webService.config.LoginInterceptorJAWS;
import com.example.wxwsofto.service.webService.cxf.ProspectService;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* webservice的接口类与服务实现类的namespace保持一致 否则会找不到
*/
@RestController
public class WebServiceInvoke {
@ResponseBody
@RequestMapping("/webServiceInvoke")
public String invoke(){
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient("http://localhost:8081/cxf/prosService?wsdl");
// 需要密码的情况需要加上用户名和密码
client.getOutInterceptors().add(new LoginInterceptorJAWS("root","root"));
Object[] objects = new Object[0];
try {
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke("sendMessage", "wxw");
System.out.println("返回数据:" + objects[0]);
} catch (java.lang.Exception e) {
e.printStackTrace();
}
return objects[0].toString();
}
/**
* \用代理类工厂,需要拿到对方的接口
* @return
*/
@ResponseBody
@RequestMapping("/webServiceInvokeProxy")
public String invokeProxy(){
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setAddress("http://localhost:8081/cxf/prosService?wsdl");
jaxWsProxyFactoryBean.getOutInterceptors().add(new LoginInterceptorJAWS("root","root"));
jaxWsProxyFactoryBean.setServiceClass(ProspectService.class);
ProspectService prospectService = (ProspectService) jaxWsProxyFactoryBean.create();
String nihao = prospectService.sendMessage("nihao");
return nihao;
}
}
上边服务的调用,引入了用户名密码的校验,使用的cxf的拦截器 AbstractPhaseInterceptor,分为两个方向请求进入和请求响应进行拦截
限流加身份校验
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.phase.PhaseInterceptorChain;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import javax.servlet.http.HttpServletRequest;
import javax.xml.soap.SOAPException;
import java.util.List;
import java.util.Map;
public class AuthInterceptorJAWS extends AbstractPhaseInterceptor<SoapMessage> {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private static final String USERNAME = "root";
private static final String PASSWORD = "root";
private Map<String, RateLimiter> rateLimiterMap = MapUtil.newConcurrentHashMap();
private RateLimiter rateLimiter =null;
public AuthInterceptorJAWS() {
/*定义在那个阶段拦截*/
super(Phase.PRE_PROTOCOL);
}
@Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
List<Header> headers = null;
String username = null;
String password = null;
try {
headers=soapMessage.getHeaders();
} catch (Exception e) {
logger.error("获取请求头信息失败,失败信息:{}",e.getMessage());
}
if (headers==null){
throw new Fault(new IllegalArgumentException("找不到用户信息,无法验证用户信息"));
}
for (Header header: headers
) {
SoapHeader soapHeader =(SoapHeader)header;
/*
WebServiceContext webServiceContext =new WebServiceContextImpl();
MessageContext messageContext = webServiceContext.getMessageContext();
HttpServletRequest request =(HttpServletRequest)messageContext.get(MessageContext.SERVLET_REQUEST);
String pathInfo = request.getPathInfo();
System.out.println("pathInfo:"+pathInfo);*/
Message currentMessage = PhaseInterceptorChain.getCurrentMessage();
//获取request
HttpServletRequest request = (HttpServletRequest) currentMessage.get(AbstractHTTPDestination.HTTP_REQUEST);
System.out.println("getHttpServletMapping:"+request.getHttpServletMapping().getServletName());
System.out.println("getMethos:"+request.getMethod());
System.out.println("getPathInfo:"+request.getPathInfo());
Element element = (Element) soapHeader.getObject();
String textContent = element.getTextContent();
System.out.println("textContent:"+textContent);
String baseURI = element.getBaseURI();
System.out.println("baseURI:"+baseURI);
NamedNodeMap attributes = element.getAttributes();
for (int i=0;i<attributes.getLength();i++){
System.out.println("attributes的属性"+i+attributes.item(i));
}
Currentlimit(baseURI);
boolean limit = rateLimiter.tryAcquire();
if (!limit){
throw new Fault(new IllegalArgumentException("访问过快,请稍在尝试"));
}
NodeList usernameNode = element.getElementsByTagName("username");
NodeList passwordNode = element.getElementsByTagName("password");
username = usernameNode.item(0).getTextContent();
password = passwordNode.item(0).getTextContent();
System.out.println(username+password);
if (StrUtil.isBlank(username)||StrUtil.isBlank(password)){
throw new Fault(new IllegalArgumentException("用户信息为空"));
}
if (!(username.equals(USERNAME)&&password.equals(PASSWORD))){
SOAPException soapException = new SOAPException("认证失败");
logger.error("用户认证失败");
throw new Fault(soapException);
}
}
}
/**
* 限流
*/
public void Currentlimit(String uri){
if (rateLimiter==null){
rateLimiter = RateLimiter.create(1);
rateLimiterMap.put(uri,rateLimiter);
}
}
}
在请求的前身份信息封装到请求头里面,
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import java.util.List;
public class LoginInterceptorJAWS extends AbstractPhaseInterceptor<SoapMessage> {
private String username="root";
private String password="root";
public LoginInterceptorJAWS(String username, String password) {
//设置在发送请求前阶段进行拦截
super(Phase.PREPARE_SEND);
this.username=username;
this.password=password;
}
@Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
List<Header> headers = soapMessage.getHeaders();
Document doc = DOMUtils.createDocument();
Element auth = doc.createElementNS("http://prospect.service.com","SecurityHeader");
Element UserName = doc.createElement("username");
Element UserPass = doc.createElement("password");
UserName.setTextContent(username);
UserPass.setTextContent(password);
auth.appendChild(UserName);
auth.appendChild(UserPass);
System.out.println(auth.toString());
System.out.println(doc.toString());
headers.add(0, new Header(new QName("SecurityHeader"),auth));
}
}
至此
整合Restful api
编写rest一个接口
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public interface ProspectInterface {
@GET
@Produces({MediaType.APPLICATION_XML})
@Path("/getMessage1/{message}")
public Employee getMessage1(@PathParam("message") String message);
@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/getMessage2/{message:[0-9]{0,10}}")
public String getMessage2(@PathParam("message") Integer message);
}
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
@Component
@Path("/")
public class ProspectInterfaceImpl implements ProspectInterface {
@GET
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML})
@Path("/getMessage1/{message}")
@Override
public Employee getMessage1(@PathParam("message") String message) {
JSONObject obj = JSONUtil.createObj();
//obj.putOnce(String.valueOf(message),UUID.fastUUID().toString());
Employee employee =new Employee();
employee.setEmpjob("java");
employee.setDeptno("zhogdain");
employee.setEmpname(message);
return employee;
}
@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/getMessage2/{message:[0-9]{0,10}}")
@Override
public String getMessage2(@PathParam("message") Integer message) {
JSONObject obj = JSONUtil.createObj();
obj.putOnce(String.valueOf(message),UUID.fastUUID().toString());
System.out.println(obj.toString());
return obj.toString();
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 将Bean托管给Spring -->
<bean id="prosService" class="com.example.wxwsofto.service.webService.cxf.impl.ProspectInterfaceImpl">
</bean>
<!-- 配置需要暴露的Rest ful Service -->
<jaxrs:server id="restContainer" address="/prosAdders"> <!-- 暴露restful api 类似于前文提到的webService【暴露soap】 即访问的时候要加上这个address -->
<jaxrs:serviceBeans>
<!-- 相当于打包发布服务 -->
<ref bean="prosService" />
</jaxrs:serviceBeans>
<!-- 提供一个json转化,没有这个不能自动返回json jsonProvider就是前面@Bean生成的在CxfConfig -->
<jaxrs:providers>
<ref bean="jsonProvider" />
</jaxrs:providers>
</jaxrs:server>
</beans>
以上仅供参考,如有帮助到你,可以点个star