10 Commits

Author SHA1 Message Date
885b6bc7fb update readme 2018-09-02 12:05:19 +08:00
69a6a73663 增加一个cxf的说明 2018-06-21 18:35:40 +08:00
be5ac402cc cxf客户端代码生成 2018-06-19 14:57:48 +08:00
26e4227ace 增加一个cxf 2018-06-15 18:40:21 +08:00
f8e958fa71 add rabbitmq rpc sample 2018-05-17 15:11:08 +08:00
66c4d7ae39 更新websocket,非Stomp协议的原生WebSocket协议。 2018-03-22 11:09:56 +08:00
4b56e79eac 更新websocket,非Stomp协议的原生WebSocket协议。 2018-03-22 09:10:09 +08:00
03cb51fd4a update resttemplate config 2018-03-20 20:08:33 +08:00
2890b40b4f update shiro schema.sql 2018-03-07 13:15:23 +08:00
30e057eb76 update README 2018-03-05 11:41:43 +08:00
56 changed files with 2642 additions and 47 deletions

View File

@ -3,7 +3,7 @@
[![GitHub issues](https://img.shields.io/github/issues/yidao620c/SpringBootBucket.svg)](https://github.com/yidao620c/SpringBootBucket/issues)
[![License][licensesvg]][license]
[![Github downloads](https://img.shields.io/github/downloads/yidao620c/SpringBootBucket/total.svg)](https://github.com/yidao620c/SpringBootBucket/releases/latest)
[![GitHub release](https://img.shields.io/github/release/yidao620c/SpringBootBucket.svg)](https://github.com/yidao620c/SpringBootBucket/release)
[![GitHub release](https://img.shields.io/github/release/yidao620c/SpringBootBucket.svg)](https://github.com/yidao620c/SpringBootBucket/releases)
Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成。
@ -11,6 +11,8 @@ Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身
每个子项目都会使用最小依赖,大家拿来即可使用,自己可以根据业务需求自由组合搭配不同的技术构建项目。
该分支基于springboot1.5构建。
## 项目简介
![](https://xnstatic-1253397658.file.myqcloud.com/SpringBootBucket.png)
@ -33,6 +35,7 @@ springboot-swagger2 | [集成Swagger2自动生成API文档](https://www.
springboot-jwt | [集成JWT实现接口权限认证](https://www.xncoding.com/2017/07/09/spring/sb-jwt.html)
springboot-multisource | [多数据源配置](https://www.xncoding.com/2017/07/10/spring/sb-multisource.html)
springboot-schedule | [定时任务](https://www.xncoding.com/2017/07/12/spring/sb-schedule.html)
springboot-cxf | [cxf实现WebService](https://www.xncoding.com/2017/07/13/spring/sb-cxf.html)
springboot-websocket | [使用WebScoket实时通信](https://www.xncoding.com/2017/07/15/spring/sb-websocket.html)
springboot-socketio | [集成SocketIO实时通信](https://www.xncoding.com/2017/07/16/spring/sb-socketio.html)
springboot-async | [异步线程池](https://www.xncoding.com/2017/07/20/spring/sb-async.html)
@ -76,7 +79,7 @@ springboot-echarts | [集成Echarts导出图片](https://www.xncoding.c
## 后续计划
1. 集成OAuth2认证
1. 集成QQ、微信、新浪微博第三方登录配合shiro使用
1. 集成QQ、微信、新浪微博第三方登录
1. 集成网络爬虫框架
1. 等SpringBoot 2稳定后全部升级至2

14
springboot-cxf/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
# 此为注释– 将被Git 忽略
# /结尾表示是目录,忽略目录和目录下的所有件
# /开头表示根目录,否则是.gitignore的相对目录
# !开头表示反选
.idea/
target/
*.iml
*.ipr
*.iws
*.log
.svn/
.project
rebel.xml
.rebel-remote.xml.*

20
springboot-cxf/LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Xiong Neng
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

53
springboot-cxf/README.md Normal file
View File

@ -0,0 +1,53 @@
## 实现WebService
利用Apache CXF实现WebService
启动之后wsdl访问链接<http://localhost:8092/services/CommonService?wsdl>
## 客户端动态代理调用
这个在单元测试类ApplicationTests中有演示这里要注意的是model类的包名一定要放到指定的路径下。
也就是targetNamespace的倒叙包名中。
## 客户端代码生成
有两种方式生成客户端调用代码
**Apache的wsdl2java工具**
```
wsdl2java -autoNameResolution http://xxx?wsdl
```
**JDK自带的工具推荐**
```
wsimport -encoding utf-8 -p com.xncoding.webservice.client -keep http://xxx?wsdl -s d:/ws -B-XautoNameResolution
```
其中:
```
-encoding 指定编码格式此处是utf-8的指定格式
-keep是否生成Java源文件
-s指定.java文件的输出目录
-d指定.class文件的输出目录
-p定义生成类的包名不定义的话有默认包名
-verbose在控制台显示输出信息
-b指定jaxws/jaxb绑定文件或额外的schemas
-extension使用扩展来支持SOAP1.2
```
生成的客户端代码不能改包名
``` java
CommonService_Service c = new CommonService_Service();
com.xncoding.webservice.client.User user = c.getCommonServiceImplPort().getUser("Tom");
assertThat(user.getName(), is("Tom"));
```
## 许可证
Copyright (c) 2018 Xiong Neng
基于 MIT 协议发布: <http://www.opensource.org/licenses/MIT>

106
springboot-cxf/pom.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xncoding</groupId>
<artifactId>springboot-cxf</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-restful</name>
<description>CXF实现WebService</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- CXF webservice -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.2.4</version>
</dependency>
<!-- CXF webservice -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!--<proc>none</proc>-->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>

72
springboot-cxf/run.sh Normal file
View File

@ -0,0 +1,72 @@
#!/bin/bash
# 项目自动更新脚本
# 先clone相应的分支下来
# git clone ssh://git@120.24.173.142:7999/xxx.git
# 远程调试启动:
# nohup java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &
function start {
profile="$1"
echo "启动环境profile=${profile}"
jarfile=$(ls target/*.jar)
if [[ "$?" == "0" ]]; then
stop $profile $jarfile
fi
branch=$(git branch |awk '{print $2}')
git pull origin ${branch}
echo "更新完代码开始重新打包"
mvn clean && mvn clean && mvn package -DskipTests=true
if [[ "$?" != "0" ]]; then
echo "编译出错,退出!"
exit 1
fi
echo "nohup java -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &"
nohup java -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &
echo "启动应用中,请查看日志文件..."
}
function stop {
profile="$1"
jarfile="$2"
ps aux | grep "${jarfile}" | grep "spring.profiles.active=${profile}" | grep -v grep > /dev/null
if [[ "$?" == "0" ]]; then
echo "该应用还在跑,我先停了它"
pid=$(ps aux | grep "${jarfile}" | grep "spring.profiles.active=${profile}" | grep -v grep |awk '{print $2}')
if [[ "$pid" != "" ]]; then
kill -9 $pid
fi
echo "停止应用成功..."
fi
}
if [[ "$1" == "start" ]]; then
if [[ "$#" < 2 ]]; then
echo "请输入正确参数:./epay.sh start {profile}"
exit 1
fi
profile="$2"
if [[ "$profile" != "dev" && "$profile" != "test" && "$profile" != "show" && "$profile" != "production" ]]; then
echo "参数错误请输入正确的profile参数使用方法"
echo "./epay.sh start {profile} ==> 启动应用,{profile}取值dev|test|show|production"
exit 1
fi
start "${profile}"
elif [[ "$1" == "stop" ]]; then
if [[ "$#" < 2 ]]; then
echo "请输入正确参数:./epay.sh stop {profile}"
exit 1
fi
profile="$2"
if [[ "$profile" != "dev" && "$profile" != "test" && "$profile" != "show" && "$profile" != "production" ]]; then
echo "参数错误请输入正确的profile参数使用方法"
echo "./epay.sh stop {profile} ==> 停止应用,{profile}取值dev|test|show|production"
exit 1
fi
jarfile=$(ls target/*.jar)
stop $profile $jarfile
else
echo "参数错误,使用方法:{}参数是必填的,[]参数可选"
echo "./epay.sh start {profile} ==> 启动应用,{profile}取值dev|test|show|production"
echo "./epay.sh stop {profile} ==> 停止应用,{profile}取值dev|test|show|production"
exit 1
fi

View File

@ -0,0 +1,12 @@
package com.xncoding.webservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,54 @@
package com.xncoding.webservice.client;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebService(name = "CommonService", targetNamespace = "http://model.webservice.xncoding.com/")
@XmlSeeAlso({
ObjectFactory.class
})
public interface CommonService {
/**
*
* @param userName
* @return
* returns com.xncoding.webservice.client.User
*/
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "getUser", targetNamespace = "http://model.webservice.xncoding.com/", className = "com.xncoding.webservice.client.GetUser")
@ResponseWrapper(localName = "getUserResponse", targetNamespace = "http://model.webservice.xncoding.com/", className = "com.xncoding.webservice.client.GetUserResponse")
public User getUser(
@WebParam(name = "userName", targetNamespace = "")
String userName);
/**
*
* @param userName
* @return
* returns java.lang.String
*/
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "sayHello", targetNamespace = "http://model.webservice.xncoding.com/", className = "com.xncoding.webservice.client.SayHello")
@ResponseWrapper(localName = "sayHelloResponse", targetNamespace = "http://model.webservice.xncoding.com/", className = "com.xncoding.webservice.client.SayHelloResponse")
public String sayHello(
@WebParam(name = "userName", targetNamespace = "")
String userName);
}

View File

@ -0,0 +1,94 @@
package com.xncoding.webservice.client;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebServiceClient(name = "CommonService", targetNamespace = "http://model.webservice.xncoding.com/", wsdlLocation = "http://localhost:8092/services/CommonService?wsdl")
public class CommonService_Service
extends Service
{
private final static URL COMMONSERVICE_WSDL_LOCATION;
private final static WebServiceException COMMONSERVICE_EXCEPTION;
private final static QName COMMONSERVICE_QNAME = new QName("http://model.webservice.xncoding.com/", "CommonService");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("http://localhost:8092/services/CommonService?wsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
COMMONSERVICE_WSDL_LOCATION = url;
COMMONSERVICE_EXCEPTION = e;
}
public CommonService_Service() {
super(__getWsdlLocation(), COMMONSERVICE_QNAME);
}
public CommonService_Service(WebServiceFeature... features) {
super(__getWsdlLocation(), COMMONSERVICE_QNAME, features);
}
public CommonService_Service(URL wsdlLocation) {
super(wsdlLocation, COMMONSERVICE_QNAME);
}
public CommonService_Service(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, COMMONSERVICE_QNAME, features);
}
public CommonService_Service(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public CommonService_Service(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* @return
* returns CommonService
*/
@WebEndpoint(name = "CommonServiceImplPort")
public CommonService getCommonServiceImplPort() {
return super.getPort(new QName("http://model.webservice.xncoding.com/", "CommonServiceImplPort"), CommonService.class);
}
/**
*
* @param features
* A list of {@link WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns CommonService
*/
@WebEndpoint(name = "CommonServiceImplPort")
public CommonService getCommonServiceImplPort(WebServiceFeature... features) {
return super.getPort(new QName("http://model.webservice.xncoding.com/", "CommonServiceImplPort"), CommonService.class, features);
}
private static URL __getWsdlLocation() {
if (COMMONSERVICE_EXCEPTION!= null) {
throw COMMONSERVICE_EXCEPTION;
}
return COMMONSERVICE_WSDL_LOCATION;
}
}

View File

@ -0,0 +1,60 @@
package com.xncoding.webservice.client;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
/**
* <p>getUser complex type的 Java 类。
*
* <p>以下模式片段指定包含在此类中的预期内容。
*
* <pre>
* &lt;complexType name="getUser">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="userName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getUser", propOrder = {
"userName"
})
public class GetUser {
protected String userName;
/**
* 获取userName属性的值。
*
* @return
* possible object is
* {@link String }
*
*/
public String getUserName() {
return userName;
}
/**
* 设置userName属性的值。
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setUserName(String value) {
this.userName = value;
}
}

View File

@ -0,0 +1,62 @@
package com.xncoding.webservice.client;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>getUserResponse complex type的 Java 类。
*
* <p>以下模式片段指定包含在此类中的预期内容。
*
* <pre>
* &lt;complexType name="getUserResponse">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="return" type="{http://model.webservice.xncoding.com/}user" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getUserResponse", propOrder = {
"_return"
})
public class GetUserResponse {
@XmlElement(name = "return")
protected User _return;
/**
* 获取return属性的值。
*
* @return
* possible object is
* {@link User }
*
*/
public User getReturn() {
return _return;
}
/**
* 设置return属性的值。
*
* @param value
* allowed object is
* {@link User }
*
*/
public void setReturn(User value) {
this._return = value;
}
}

View File

@ -0,0 +1,115 @@
package com.xncoding.webservice.client;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.xncoding.webservice.client package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {
private final static QName _GetUser_QNAME = new QName("http://model.webservice.xncoding.com/", "getUser");
private final static QName _GetUserResponse_QNAME = new QName("http://model.webservice.xncoding.com/", "getUserResponse");
private final static QName _SayHello_QNAME = new QName("http://model.webservice.xncoding.com/", "sayHello");
private final static QName _SayHelloResponse_QNAME = new QName("http://model.webservice.xncoding.com/", "sayHelloResponse");
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.xncoding.webservice.client
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link GetUserResponse }
*
*/
public GetUserResponse createGetUserResponse() {
return new GetUserResponse();
}
/**
* Create an instance of {@link SayHello }
*
*/
public SayHello createSayHello() {
return new SayHello();
}
/**
* Create an instance of {@link GetUser }
*
*/
public GetUser createGetUser() {
return new GetUser();
}
/**
* Create an instance of {@link SayHelloResponse }
*
*/
public SayHelloResponse createSayHelloResponse() {
return new SayHelloResponse();
}
/**
* Create an instance of {@link User }
*
*/
public User createUser() {
return new User();
}
/**
* Create an instance of {@link JAXBElement }{@code <}{@link GetUser }{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.webservice.xncoding.com/", name = "getUser")
public JAXBElement<GetUser> createGetUser(GetUser value) {
return new JAXBElement<GetUser>(_GetUser_QNAME, GetUser.class, null, value);
}
/**
* Create an instance of {@link JAXBElement }{@code <}{@link GetUserResponse }{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.webservice.xncoding.com/", name = "getUserResponse")
public JAXBElement<GetUserResponse> createGetUserResponse(GetUserResponse value) {
return new JAXBElement<GetUserResponse>(_GetUserResponse_QNAME, GetUserResponse.class, null, value);
}
/**
* Create an instance of {@link JAXBElement }{@code <}{@link SayHello }{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.webservice.xncoding.com/", name = "sayHello")
public JAXBElement<SayHello> createSayHello(SayHello value) {
return new JAXBElement<SayHello>(_SayHello_QNAME, SayHello.class, null, value);
}
/**
* Create an instance of {@link JAXBElement }{@code <}{@link SayHelloResponse }{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.webservice.xncoding.com/", name = "sayHelloResponse")
public JAXBElement<SayHelloResponse> createSayHelloResponse(SayHelloResponse value) {
return new JAXBElement<SayHelloResponse>(_SayHelloResponse_QNAME, SayHelloResponse.class, null, value);
}
}

View File

@ -0,0 +1,60 @@
package com.xncoding.webservice.client;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
/**
* <p>sayHello complex type的 Java 类。
*
* <p>以下模式片段指定包含在此类中的预期内容。
*
* <pre>
* &lt;complexType name="sayHello">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="userName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "sayHello", propOrder = {
"userName"
})
public class SayHello {
protected String userName;
/**
* 获取userName属性的值。
*
* @return
* possible object is
* {@link String }
*
*/
public String getUserName() {
return userName;
}
/**
* 设置userName属性的值。
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setUserName(String value) {
this.userName = value;
}
}

View File

@ -0,0 +1,62 @@
package com.xncoding.webservice.client;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>sayHelloResponse complex type的 Java 类。
*
* <p>以下模式片段指定包含在此类中的预期内容。
*
* <pre>
* &lt;complexType name="sayHelloResponse">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="return" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "sayHelloResponse", propOrder = {
"_return"
})
public class SayHelloResponse {
@XmlElement(name = "return")
protected String _return;
/**
* 获取return属性的值。
*
* @return
* possible object is
* {@link String }
*
*/
public String getReturn() {
return _return;
}
/**
* 设置return属性的值。
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setReturn(String value) {
this._return = value;
}
}

View File

@ -0,0 +1,114 @@
package com.xncoding.webservice.client;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
/**
* <p>user complex type的 Java 类。
*
* <p>以下模式片段指定包含在此类中的预期内容。
*
* <pre>
* &lt;complexType name="user">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="age" type="{http://www.w3.org/2001/XMLSchema}int" minOccurs="0"/>
* &lt;element name="id" type="{http://www.w3.org/2001/XMLSchema}long" minOccurs="0"/>
* &lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "user", propOrder = {
"age",
"id",
"name"
})
public class User {
protected Integer age;
protected Long id;
protected String name;
/**
* 获取age属性的值。
*
* @return
* possible object is
* {@link Integer }
*
*/
public Integer getAge() {
return age;
}
/**
* 设置age属性的值。
*
* @param value
* allowed object is
* {@link Integer }
*
*/
public void setAge(Integer value) {
this.age = value;
}
/**
* 获取id属性的值。
*
* @return
* possible object is
* {@link Long }
*
*/
public Long getId() {
return id;
}
/**
* 设置id属性的值。
*
* @param value
* allowed object is
* {@link Long }
*
*/
public void setId(Long value) {
this.id = value;
}
/**
* 获取name属性的值。
*
* @return
* possible object is
* {@link String }
*
*/
public String getName() {
return name;
}
/**
* 设置name属性的值。
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setName(String value) {
this.name = value;
}
}

View File

@ -0,0 +1,2 @@
@javax.xml.bind.annotation.XmlSchema(namespace = "http://model.webservice.xncoding.com/")
package com.xncoding.webservice.client;

View File

@ -0,0 +1,38 @@
package com.xncoding.webservice.config;
import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import com.xncoding.webservice.service.ICommonService;
import org.springframework.context.annotation.Configuration;
/**
* 默认服务在 Host:port/services/*** 路径下
* 这里相当于把Commonservice接口发布在了路径/services/CommonService下
* wsdl文档路径为http://localhost:8080/services/CommonService?wsdl
*
* @author XiongNeng
* @version 1.0
* @since 2018/6/15
*/
@Configuration
public class CxfConfig {
@Autowired
private Bus bus;
@Autowired
ICommonService commonService;
/**
* JAX-WS
**/
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(bus, commonService);
endpoint.publish("/CommonService");
return endpoint;
}
}

View File

@ -0,0 +1,47 @@
package com.xncoding.webservice.model;
/**
* 用户
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/4
*/
public class User {
private Long id;
private String name;
private Integer age;
public User() {
}
public User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

View File

@ -0,0 +1,27 @@
package com.xncoding.webservice.service;
import com.xncoding.webservice.model.User;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* ICommonService
*
* @author XiongNeng
* @version 1.0
* @since 2018/6/15
*/
@WebService(name = "CommonService", // 暴露服务名称
targetNamespace = "http://model.webservice.xncoding.com/"// 命名空间,一般是接口的包名倒序
)
public interface ICommonService {
@WebMethod
// @WebResult(name = "String", targetNamespace = "")
public String sayHello(@WebParam(name = "userName") String name);
@WebMethod
// @WebResult(name = "String", targetNamespace = "")
public User getUser(@WebParam(name = "userName") String name);
}

View File

@ -0,0 +1,32 @@
package com.xncoding.webservice.service.impl;
import com.xncoding.webservice.model.User;
import com.xncoding.webservice.service.ICommonService;
import org.springframework.stereotype.Component;
import javax.jws.WebService;
/**
* CommonServiceImpl
*
* @author XiongNeng
* @version 1.0
* @since 2018/6/15
*/
@WebService(serviceName = "CommonService", // 与接口中指定的name一致
targetNamespace = "http://model.webservice.xncoding.com/", // 与接口中的命名空间一致,一般是接口的包名倒
endpointInterface = "com.xncoding.webservice.service.ICommonService"// 接口地址
)
@Component
public class CommonServiceImpl implements ICommonService {
@Override
public String sayHello(String name) {
return "Hello ," + name;
}
@Override
public User getUser(String name) {
return new User(1000L, name, 23);
}
}

View File

@ -0,0 +1,45 @@
package com.xncoding.webservice.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* JacksonUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/4
*/
public class JacksonUtil {
private static ObjectMapper mapper = new ObjectMapper();
public static String bean2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
// try {
// return mapper.readValue(jsonStr, objClass);
// } catch (IOException e) {
// e.printStackTrace();
// return null;
// }
// }
public static <T> T json2Bean(String jsonStr, TypeReference<T> typeReference) {
try {
return mapper.readValue(jsonStr, typeReference);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,33 @@
##########################################################
################## 所有profile共有的配置 #################
##########################################################
################### 项目启动端口 ###################
server.port: 8092
################### spring配置 ###################
spring:
profiles:
active: dev
cxf:
path: /services # 替换默认的/services路径
logging:
level:
org.springframework.web.servlet: ERROR
---
#####################################################################
######################## 开发环境profile ##########################
#####################################################################
spring:
profiles: dev
logging:
level:
ROOT: INFO
com:
xncoding: DEBUG
file: E:/logs/app.log

View File

@ -0,0 +1,23 @@
_____ _______ _____ _____
/\ \ /::\ \ /\ \ /\ \
/::\____\ /::::\ \ /::\____\ /::\ \
/:::/ / /::::::\ \ /:::/ / /::::\ \
/:::/ / /::::::::\ \ /:::/ / /::::::\ \
/:::/ / /:::/~~\:::\ \ /:::/ / /:::/\:::\ \
/:::/ / /:::/ \:::\ \ /:::/____/ /:::/__\:::\ \
/:::/ / /:::/ / \:::\ \ |::| | /::::\ \:::\ \
/:::/ / /:::/____/ \:::\____\ |::| | _____ /::::::\ \:::\ \
/:::/ / |:::| | |:::| | |::| | /\ \ /:::/\:::\ \:::\ \
/:::/____/ |:::|____| |:::| | |::| | /::\____\/:::/__\:::\ \:::\____\
\:::\ \ \:::\ \ /:::/ / |::| | /:::/ /\:::\ \:::\ \::/ /
\:::\ \ \:::\ \ /:::/ / |::| | /:::/ / \:::\ \:::\ \/____/
\:::\ \ \:::\ /:::/ / |::|____|/:::/ / \:::\ \:::\ \
\:::\ \ \:::\__/:::/ / |:::::::::::/ / \:::\ \:::\____\
\:::\ \ \::::::::/ / \::::::::::/____/ \:::\ \::/ /
\:::\ \ \::::::/ / ~~~~~~~~~~ \:::\ \/____/
\:::\ \ \::::/ / \:::\ \
\:::\____\ \::/____/ \:::\____\
\::/ / ~~ \::/ /
\/____/ \/____/

View File

@ -0,0 +1,112 @@
package com.xncoding.webservice;
import com.xncoding.webservice.client.CommonService_Service;
import com.xncoding.webservice.model.User;
import com.xncoding.webservice.service.ICommonService;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
@LocalServerPort
private Integer port;
/**
* 接口地址
*/
private String wsdlAddress;
@Before
public void prepare() {
wsdlAddress = "http://localhost:" + port + "/services/CommonService?wsdl";
}
/**
* 方式1.代理类工厂的方式,需要拿到对方的接口
*/
@Test
public void cl1() {
try {
// 接口地址
// 代理工厂
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
// 设置代理地址
jaxWsProxyFactoryBean.setAddress(wsdlAddress);
// 设置接口类型
jaxWsProxyFactoryBean.setServiceClass(ICommonService.class);
// 创建一个代理接口实现
ICommonService cs = (ICommonService) jaxWsProxyFactoryBean.create();
// 数据准备
String userName = "Leftso";
// 调用代理接口的方法调用并返回结果
String result = cs.sayHello(userName);
System.out.println("返回结果:" + result);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 方式2. 动态调用方式
*/
@Test
public void cl2() {
// 创建动态客户端
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(wsdlAddress);
// 需要密码的情况需要加上用户名和密码
// client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, PASS_WORD));
Object[] objects;
try {
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke("sayHello", "Leftso");
System.out.println("返回类型:" + objects[0].getClass());
System.out.println("返回数据:" + objects[0]);
} catch (java.lang.Exception e) {
e.printStackTrace();
}
}
/**
* 方式3. 动态调用方式返回对象User
*/
@Test
public void cl3() {
// 创建动态客户端
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(wsdlAddress);
Object[] objects;
try {
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke("getUser", "张三");
System.out.println("返回类型:" + objects[0].getClass());
System.out.println("返回数据:" + objects[0]);
User user = (User) objects[0];
System.out.println("返回对象User.name=" + user.getName());
} catch (java.lang.Exception e) {
e.printStackTrace();
}
}
/**
* 方式4. 客户端代码生成方式
*/
@Test
public void cl4() {
CommonService_Service c = new CommonService_Service();
com.xncoding.webservice.client.User user = c.getCommonServiceImplPort().getUser("Tom");
assertThat(user.getName(), is("Tom"));
}
}

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Xiong Neng
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,9 @@
## RabbitMQ实现RPC调用客户端
消息队列RabbitMQ的使用例子演示了RPC调用的客户端例子。
## 许可证
Copyright (c) 2018 Xiong Neng
基于 MIT 协议发布: <http://www.opensource.org/licenses/MIT>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xncoding</groupId>
<artifactId>springboot-rabbitmq-rpc-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-rabbitmq-rpc-client</name>
<description>集成消息队列RabbitMQ RPC调用 - 客户端</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!--<proc>none</proc>-->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,12 @@
package com.xncoding.pos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,89 @@
package com.xncoding.pos.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.AnonymousQueue;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import javax.annotation.Resource;
/**
* RabbitConfig
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/1
*/
@Configuration
public class RabbitConfig {
/**
* 同步RPC队列
*/
public static final String QUEUE_SYNC_RPC = "rpc.sync";
/**
* 异步RPC队列使用临时回复队列或者使用“Direct reply-to”特性
*/
public static final String QUEUE_ASYNC_RPC = "rpc.async";
/**
* 异步RPC队列每个客户端使用不同的固定回复队列需要额外提供correlationId以关联请求和响应
*/
public static final String QUEUE_ASYNC_RPC_WITH_FIXED_REPLY = "rpc.with.fixed.reply";
@Bean
public Queue syncRPCQueue() {
return new Queue(QUEUE_SYNC_RPC);
}
@Bean
public Queue asyncRPCQueue() {
return new Queue(QUEUE_ASYNC_RPC);
}
@Bean
public Queue fixedReplyRPCQueue() {
return new Queue(QUEUE_ASYNC_RPC_WITH_FIXED_REPLY);
}
@Bean
public Queue repliesQueue() {
return new AnonymousQueue();
}
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
@Bean
@Primary
public SimpleMessageListenerContainer replyContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueueNames(repliesQueue().getName());
return container;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
@Bean
public AsyncRabbitTemplate asyncRabbitTemplate(RabbitTemplate template, SimpleMessageListenerContainer container) {
return new AsyncRabbitTemplate(template, container);
}
}

View File

@ -0,0 +1,29 @@
package com.xncoding.pos.model;
/**
* User
*
* @author XiongNeng
* @version 1.0
* @since 2018/5/17
*/
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

View File

@ -0,0 +1,51 @@
package com.xncoding.pos.service;
import com.xncoding.pos.model.User;
import com.xncoding.pos.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Future;
import static com.xncoding.pos.config.RabbitConfig.QUEUE_ASYNC_RPC;
import static com.xncoding.pos.config.RabbitConfig.QUEUE_ASYNC_RPC_WITH_FIXED_REPLY;
/**
* 消息发送服务
*/
@Service
public class SenderService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
AsyncRabbitTemplate asyncRabbitTemplate;
@Autowired
AmqpTemplate amqpTemplate;
@Async
public Future<String> sendAsync(User message) {
// String result = (String) amqpTemplate.convertSendAndReceive(QUEUE_ASYNC_RPC, message, m -> {
// m.getMessageProperties().setCorrelationIdString(StringUtil.generateUUId());
// return m;
// });
String result = (String) amqpTemplate.convertSendAndReceive(QUEUE_ASYNC_RPC, message);
return new AsyncResult<>(result);
}
public Future<String> sendWithFixedReplay(User message) {
// ListenableFuture<String> future = asyncRabbitTemplate.convertSendAndReceive(QUEUE_ASYNC_RPC_WITH_FIXED_REPLY, message, m -> {
// m.getMessageProperties().setCorrelationIdString(StringUtil.generateUUId());
// return m;
// });
return asyncRabbitTemplate.convertSendAndReceive(QUEUE_ASYNC_RPC_WITH_FIXED_REPLY, message);
}
}

View File

@ -0,0 +1,16 @@
package com.xncoding.pos.util;
import java.util.UUID;
/**
* StringUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/5/17
*/
public class StringUtil {
public static String generateUUId() {
return UUID.randomUUID().toString();
}
}

View File

@ -0,0 +1,29 @@
##########################################################
################## 所有profile共有的配置 #################
##########################################################
################### spring配置 ###################
spring:
profiles:
active: dev
---
#####################################################################
######################## 开发环境profile ##########################
#####################################################################
spring:
profiles: dev
rabbitmq:
host: 119.29.12.177
port: 5672
username: guest
password: guest
logging:
level:
ROOT: INFO
com:
xncoding: DEBUG
file: D:/logs/rabbitmq-rpc-client.log

View File

@ -0,0 +1,90 @@
package com.xncoding.service;
import com.xncoding.pos.Application;
import com.xncoding.pos.model.User;
import com.xncoding.pos.service.SenderService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* SenderServiceTest
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/2
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class SenderServiceTest {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private SenderService senderService;
private List<User> users;
@Before
public void prepare() {
users = new ArrayList<>();
User user1 = new User();
user1.setName("用户1");
user1.setAge(19);
users.add(user1);
User user2 = new User();
user2.setName("用户2");
user2.setAge(20);
users.add(user2);
}
@Test
public void testSendAsync() throws InterruptedException, ExecutionException {
List<Future<String>> results = new ArrayList<>();
for (User user : users) {
Future<String> result = senderService.sendAsync(user);
results.add(result);
}
for (Future<String> future : results) {
String result = future.get();
if (result == null) {
Assert.fail("message will not timeout");
} else {
logger.info("tttttttttttt=" + result);
}
}
}
@Test
public void testSendWithFixedReplay() throws InterruptedException, ExecutionException{
List<Future<String>> results = new ArrayList<>();
for (User user : users) {
Future<String> result = senderService.sendWithFixedReplay(user);
results.add(result);
}
for (Future<String> future : results) {
String result = future.get();
if (result == null) {
Assert.fail("message will not timeout");
} else {
logger.info("tttttttttttt=" + result);
}
}
}
}

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Xiong Neng
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,9 @@
## RabbitMQ实现RPC调用服务端
消息队列RabbitMQ的使用例子演示了RPC调用的服务端例子。
## 许可证
Copyright (c) 2018 Xiong Neng
基于 MIT 协议发布: <http://www.opensource.org/licenses/MIT>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xncoding</groupId>
<artifactId>springboot-rabbitmq-rpc-server</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-rabbitmq-rpc-server</name>
<description>集成消息队列RabbitMQ RPC - 服务端</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!--<proc>none</proc>-->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,12 @@
package com.xncoding.pos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,59 @@
package com.xncoding.pos.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* ExecutorConfig
*
* @author XiongNeng
* @version 1.0
* @since 2018/5/17
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
/**
* Set the ThreadPoolExecutor's core pool size.
*/
private int corePoolSize = 10;
/**
* Set the ThreadPoolExecutor's maximum pool size.
*/
private int maxPoolSize = 200;
/**
* Set the capacity for the ThreadPoolExecutor's BlockingQueue.
*/
private int queueCapacity = 10;
// @Bean
// public Executor mySimpleAsync() {
// ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// executor.setCorePoolSize(corePoolSize);
// executor.setMaxPoolSize(maxPoolSize);
// executor.setQueueCapacity(queueCapacity);
// executor.setThreadNamePrefix("MySimpleExecutor-");
// executor.initialize();
// return executor;
// }
@Bean
public Executor myAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("MyExecutor-");
// rejection-policy当pool已经达到max size的时候如何处理新任务
// CALLER_RUNS不在新线程中执行任务而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

View File

@ -0,0 +1,89 @@
package com.xncoding.pos.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import javax.annotation.Resource;
/**
* RabbitConfig
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/1
*/
@Configuration
public class RabbitConfig {
/**
* 同步RPC队列
*/
public static final String QUEUE_SYNC_RPC = "rpc.sync";
/**
* 异步RPC队列使用临时回复队列或者使用“Direct reply-to”特性
*/
public static final String QUEUE_ASYNC_RPC = "rpc.async";
/**
* 异步RPC队列每个客户端使用不同的固定回复队列需要额外提供correlationId以关联请求和响应
*/
public static final String QUEUE_ASYNC_RPC_WITH_FIXED_REPLY = "rpc.with.fixed.reply";
@Bean
public Queue syncRPCQueue() {
return new Queue(QUEUE_SYNC_RPC);
}
@Bean
public Queue asyncRPCQueue() {
return new Queue(QUEUE_ASYNC_RPC);
}
@Bean
public Queue fixedReplyRPCQueue() {
return new Queue(QUEUE_ASYNC_RPC_WITH_FIXED_REPLY);
}
@Bean
public Queue repliesQueue() {
return new AnonymousQueue();
}
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
@Bean
@Primary
public SimpleMessageListenerContainer replyContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueueNames(repliesQueue().getName());
return container;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
@Bean
public AsyncRabbitTemplate asyncRabbitTemplate(RabbitTemplate template, SimpleMessageListenerContainer container) {
return new AsyncRabbitTemplate(template, container);
}
}

View File

@ -0,0 +1,29 @@
package com.xncoding.pos.model;
/**
* User
*
* @author XiongNeng
* @version 1.0
* @since 2018/5/17
*/
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

View File

@ -0,0 +1,87 @@
package com.xncoding.pos.server;
import com.fasterxml.jackson.core.type.TypeReference;
import com.xncoding.pos.model.User;
import com.xncoding.pos.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import java.nio.charset.Charset;
import static com.xncoding.pos.config.RabbitConfig.QUEUE_ASYNC_RPC;
import static com.xncoding.pos.config.RabbitConfig.QUEUE_ASYNC_RPC_WITH_FIXED_REPLY;
@Component
public class AsyncRPCServer {
@Autowired
AmqpTemplate amqpTemplate;
@Autowired
AsyncTask asyncTask;
Logger logger = LoggerFactory.getLogger(getClass());
@RabbitListener(queues = QUEUE_ASYNC_RPC)
public void processAsyncRpc(Message message, @Header(AmqpHeaders.REPLY_TO) String replyTo) {
String body = new String(message.getBody(), Charset.forName("UTF-8"));
User user = JacksonUtil.json2Bean(body, new TypeReference<User>(){});
logger.info("recevie message {} and reply to {}, user.name={}", body, replyTo, user.getName());
if (replyTo.startsWith("amq.rabbitmq.reply-to")) {
logger.debug("starting with version 3.4.0, the RabbitMQ server now supports Direct reply-to");
} else {
logger.info("fall back to using a temporary reply queue");
}
ListenableFuture<String> asyncResult = asyncTask.expensiveOperation(body);
asyncResult.addCallback(new ListenableFutureCallback<String>() {
@Override
public void onSuccess(String result) {
amqpTemplate.convertAndSend(replyTo, result);
}
@Override
public void onFailure(Throwable ex) {
logger.error("接受到QUEUE_ASYNC_RPC失败", ex);
}
});
}
@RabbitListener(queues = QUEUE_ASYNC_RPC_WITH_FIXED_REPLY)
public void processAsyncRpcFixed(User user,
@Header(AmqpHeaders.REPLY_TO) String replyTo,
@Header(AmqpHeaders.CORRELATION_ID) byte[] correlationId) {
// String body = new String(message.getBody(), Charset.forName("UTF-8"));
// User user = JacksonUtil.json2Bean(body, new TypeReference<User>(){});
logger.info("user.name={}", user.getName());
logger.info("use a fixed reply queue={}, correlationId={}", replyTo, new String(correlationId));
ListenableFuture<String> asyncResult = asyncTask.expensiveOperation(user.getName());
asyncResult.addCallback(new ListenableFutureCallback<String>() {
@Override
public void onSuccess(String result) {
amqpTemplate.convertAndSend(replyTo, (Object) result, m -> {
//https://stackoverflow.com/questions/42382307/messageproperties-setcorrelationidstring-is-not-working
m.getMessageProperties().setCorrelationId(correlationId);
return m;
});
}
@Override
public void onFailure(Throwable ex) {
logger.error("接受到QUEUE_ASYNC_RPC_WITH_FIXED_REPLY失败", ex);
}
});
}
}

View File

@ -0,0 +1,26 @@
package com.xncoding.pos.server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
@Component
public class AsyncTask {
Logger logger = LoggerFactory.getLogger(getClass());
@Async
public ListenableFuture<String> expensiveOperation(String message) {
int millis = (int) (Math.random() * 5 * 1000);
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
String result = message + " executed by " + Thread.currentThread().getName() + " for " + millis + " ms";
logger.info("task result {}", result);
return new AsyncResult<String>(result);
}
}

View File

@ -0,0 +1,45 @@
package com.xncoding.pos.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* JacksonUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/4
*/
public class JacksonUtil {
private static ObjectMapper mapper = new ObjectMapper();
public static String bean2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
// try {
// return mapper.readValue(jsonStr, objClass);
// } catch (IOException e) {
// e.printStackTrace();
// return null;
// }
// }
public static <T> T json2Bean(String jsonStr, TypeReference<T> typeReference) {
try {
return mapper.readValue(jsonStr, typeReference);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,38 @@
##########################################################
################## 所有profile共有的配置 #################
##########################################################
################### spring配置 ###################
spring:
profiles:
active: dev
---
#####################################################################
######################## 开发环境profile ##########################
#####################################################################
spring:
profiles: dev
rabbitmq:
host: 119.29.12.177
port: 5672
username: guest
password: guest
# publisher-confirms: true #支持发布确认
# publisher-returns: true #支持发布返回
# listener:
# simple:
# acknowledge-mode: manual #采用手动应答
# concurrency: 1 #指定最小的消费者数量
# max-concurrency: 20 #指定最大的消费者数量
# retry:
# enabled: true #是否支持重试
logging:
level:
ROOT: INFO
com:
xncoding: DEBUG
file: D:/logs/rabbitmq-rpc-server.log

View File

@ -43,7 +43,7 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -6,16 +6,31 @@ import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* RestTemplate客户端连接池配置
*
@ -26,34 +41,43 @@ import org.springframework.web.client.RestTemplate;
@Configuration
public class RestClientConfig {
@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(clientHttpRequestFactory());
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
return restTemplate;
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return new RestTemplate(httpRequestFactory());
}
@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(5);
connectionManager.setDefaultMaxPerRoute(5);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(8000)
.setConnectTimeout(8000)
.setConnectionRequestTimeout(8000)
.build();
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
return true;
}
}).build();
httpClientBuilder.setSSLContext(sslContext);
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory).build();// 注册http和https请求
// 开始设置连接池
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingHttpClientConnectionManager.setMaxTotal(500); // 最大连接数500
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100); // 同路由并发数100
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)); // 重试次数
HttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); // httpClient连接配置
clientHttpRequestFactory.setConnectTimeout(20000); // 连接超时
clientHttpRequestFactory.setReadTimeout(30000); // 数据读取超时时间
clientHttpRequestFactory.setConnectionRequestTimeout(20000); // 连接不够用的等待时间
return clientHttpRequestFactory;
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
log.error("初始化HTTP连接池出错", e);
}
return null;
}
}

View File

@ -71,3 +71,13 @@ CREATE TABLE `t_role_permission` (
INSERT INTO `t_role_permission` VALUES (1, 1, 1, '2017-12-12 09:46:12', '2017-12-12 09:46:12');
INSERT INTO `t_role_permission` VALUES (2, 1, 3, '2017-12-12 09:46:12', '2017-12-12 09:46:12');
INSERT INTO `t_role_permission` VALUES (3, 2, 2, '2017-12-12 09:46:12', '2017-12-12 09:46:12');
-- 项目用户关联表
DROP TABLE IF EXISTS `t_project_user`;
CREATE TABLE `t_project_user` (
`id` INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
`user_id` INT(11) NOT NULL COMMENT '用户ID',
`project_id` INT(11) NOT NULL COMMENT '项目ID',
`created_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='项目用户关联表';

View File

@ -0,0 +1,45 @@
package com.xncoding.jwt.commons;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* JacksonUtil
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/4
*/
public class JacksonUtil {
private static ObjectMapper mapper = new ObjectMapper();
public static String bean2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return "";
}
}
// public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
// try {
// return mapper.readValue(jsonStr, objClass);
// } catch (IOException e) {
// e.printStackTrace();
// return null;
// }
// }
public static <T> T json2Bean(String jsonStr, TypeReference<T> typeReference) {
try {
return mapper.readValue(jsonStr, typeReference);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -1,31 +1,25 @@
package com.xncoding.jwt.config;
import com.xncoding.jwt.handler.SocketHandler;
import com.xncoding.jwt.interceptor.WebSocketInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
/**
* WebSocketConfig
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/28
* @since 2018/3/22
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/simple")
.setAllowedOrigins("*") //解决跨域问题
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(), "/app")
.addInterceptors(new WebSocketInterceptor())
.setAllowedOrigins("*");
}
}

View File

@ -0,0 +1,31 @@
package com.xncoding.jwt.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* STOMP协议的WebStocket
*
* @author XiongNeng
* @version 1.0
* @since 2018/2/28
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/simple")
.setAllowedOrigins("*") //解决跨域问题
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
}
}

View File

@ -0,0 +1,106 @@
package com.xncoding.jwt.handler;
import com.fasterxml.jackson.core.type.TypeReference;
import com.xncoding.jwt.commons.JacksonUtil;
import com.xncoding.jwt.model.WsParam;
import com.xncoding.jwt.model.WsResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* SocketHandler
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/22
*/
@Component
public class SocketHandler extends TextWebSocketHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass());
List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
logger.info("handleTextMessage start");
// 将消息进行转化因为是消息是json数据可能里面包含了发送给某个人的信息所以需要用json相关的工具类处理之后再封装成TextMessage
// 我这儿并没有做处理,消息的封装格式一般有{from:xxxx,to:xxxxx,msg:xxxxx},来自哪里,发送给谁,什么消息等等
String msg = message.getPayload();
logger.info("msg = " + msg);
WsParam<String> wsParam = JacksonUtil.json2Bean(msg, new TypeReference<WsParam<String>>(){});
if ("list".equals(wsParam.getMethod())) {
logger.info("call list method...");
WsResponse<String> response = new WsResponse<>();
response.setResult("hello list");
sendMessageToUser(session, new TextMessage(JacksonUtil.bean2Json(response)));
}
logger.info("handleTextMessage end");
// 给所有用户群发消息
//sendMessagesToUsers(msg);
// 给指定用户群发消息
//sendMessageToUser(userId, msg);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("Connected ... " + session.getId());
sessions.add(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
if (session.isOpen()) {
session.close();
}
sessions.remove(session);
logger.info(String.format("Session %s closed because of %s", session.getId(), status.getReason()));
}
@Override
public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception {
logger.error("error occured at sender " + session, throwable);
}
/**
* 给所有的用户发送消息
*/
public void sendMessagesToUsers(TextMessage message) {
for (WebSocketSession user : sessions) {
try {
// isOpen()在线就发送
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送消息给指定的用户
*/
private void sendMessageToUser(WebSocketSession user, TextMessage message) {
try {
// 在线就发送
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
logger.error("发送消息给指定的用户出错", e);
}
}
}

View File

@ -0,0 +1,40 @@
package com.xncoding.jwt.interceptor;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
/**
* WebSocketInterceptor
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/22
*/
public class WebSocketInterceptor implements HandshakeInterceptor {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse arg1,
WebSocketHandler arg2, Map<String, Object> arg3) throws Exception {
// 将ServerHttpRequest转换成request请求相关的类用来获取request域中的用户信息
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpRequest = servletRequest.getServletRequest();
}
logger.info("beforeHandshake完成");
return true;
}
@Override
public void afterHandshake(ServerHttpRequest arg0, ServerHttpResponse arg1, WebSocketHandler arg2, Exception arg3) {
logger.info("afterHandshake完成");
}
}

View File

@ -0,0 +1,29 @@
package com.xncoding.jwt.model;
/**
* WsParam
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/22
*/
public class WsParam<T> {
private String method;
private T param;
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public T getParam() {
return param;
}
public void setParam(T param) {
this.param = param;
}
}

View File

@ -0,0 +1,20 @@
package com.xncoding.jwt.model;
/**
* WsResponse
*
* @author XiongNeng
* @version 1.0
* @since 2018/3/22
*/
public class WsResponse<T> {
private T result;
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
}

View File

@ -0,0 +1,103 @@
<html>
<head>
<meta charset="UTF-8"/>
<title>广播式WebSocket</title>
<script src="js/sockjs.min.js"></script>
<script src="js/jquery-3.1.1.js"></script>
</head>
<body>
<noscript><h2 style="color: #e80b0a;">Sorry浏览器不支持WebSocket</h2></noscript>
<div>
<div>
<button id="connect" onclick="connect();">连接</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
</div>
<div id="conversationDiv">
<label>输入你的名字</label><input type="text" id="name"/>
<button id="sendName" onclick="sendName();">发送</button>
<p id="response"></p>
<p id="callback"></p>
</div>
</div>
<script type="text/javascript">
var curTryNum = 0;
var url = 'ws://localhost:8282/app';
var ws;
function connect() {
ws = new WebSocket(url);
/**
* 因为服务端在 300s 未传输数据时会主动关闭连接,所以,客户端需要定时发送数据保持连接。
*/
var heartCheck = {
timeout: 50000, //50s
timeoutObj: null,
reset: function(){
clearInterval(this.timeoutObj);
this.start();
},
start: function(){
this.timeoutObj = setInterval(function(){
if(ws.readyState === 1){
ws.send("hb");
}
}, this.timeout)
}
};
ws.onopen = function (evnt) {
console.log("onopen: ", evnt);
heartCheck.start();
};
ws.onmessage = function(message){
// 无论收到什么信息,说明当前连接正常,将心跳检测的计时器重置
heartCheck.reset();
console.log("client received a message.data: " + message.data);
if (message.data !== "hb_ok") {
// 不要将ping的答复信息输出
console.log("msg = " + JSON.stringify(message.data));
var response = JSON.parse(message.data);
console.log("message.data.method = " + response.method);
showResponse(JSON.stringify(response.result));
}
};
ws.onclose = function (event) {
if (event.code !== 4500) {
//4500为服务端在打开多tab时主动关闭返回的编码
connect();
}
};
setConnected(true);
}
function disconnect() {
if (ws != null) {
ws.close();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var data = {
"method": "list",
"param": $("#name").val()
};
ws.send(JSON.stringify(data));
}
function showResponse(message) {
$("#response").html(message);
}
function showCallback(message) {
$("#callback").html(message);
}
function setConnected(connected) {
document.getElementById("connect").disabled = connected;
document.getElementById("disconnect").disabled = !connected;
document.getElementById("conversationDiv").style.visibility = connected ? 'visible' : 'hidden';
$("#response").html();
$("#callback").html();
}
</script>
</body>
</html>