您的位置:首页 > 其它

Dubbo搭建maven项目,并实现Dubbo分布式服务管理

2019-06-10 10:37 579 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_38122278/article/details/90770271

一、Dubbo概念介绍

1.1、Dubbo是什么?
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式的时候,才有dubbo这样的分布式服务框架的需求,并且本质上是个服务调用的东东,说白了就是个远程服务调用的分布式框架

其核心部分包含:
1》远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
2》集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
3》自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
1.2. Dubbo能做什么?

1.透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
2.软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
3. 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
1.3. dubbo的架构
dubbo架构图如下所示:

节点角色说明:
Provider: 暴露服务的服务提供方。 (服务提供者在启动时,向注册中心注册自己提供的服务。)
Consumer: 调用远程服务的服务消费方。 (服务消费者在启动时,向注册中心订阅自己所需的服务。 )
Registry: 服务注册与发现的注册中心。(注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。)
Monitor: 统计服务的调用次调和调用时间的监控中心。 (服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。)
Container: 服务运行容器。( 服务容器负责启动,加载,运行服务提供者。)
对于这些角色来说,其他都还好,Monitor可能猿友们前期使用会把它忽略,但是后期会发现它的作用十分明显哦,如服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?为了解决这个问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量。

二、前期准备
2.1、下载Zookeeper-3.3.6 :
https://pan.baidu.com/s/1QUDSlb185PP0gpwbAv4H5w
下载Apache-maven-3.3.9 :
https://pan.baidu.com/s/1i5eXbXOxxBLOKtYkRII5Jw
下载settings.xml :
https://pan.baidu.com/s/1enBc069Gn5jiCt7G4VJVVQ
下载dubbo.xsd :
https://pan.baidu.com/s/19egh9YPcsk0JDRSIlvJJ1w

2.2、配置环境变量
2.2.1、在Zookeeper的根目录下新建一个文件夹dataTmp,我的对应路径为:D:\zookeeper-3.3.6\dataTmp
2.2.2、修改conf文件夹下的 zoo.cfg


2.2.3、在环境变量中添加ZOOKEEPER_HOME 并在path中追加 ;%ZOOKEEPER_HOME%/bin;%ZOOKEEPER_HOME%/conf;

2.2.4、在环境变量中添加MAVEN_HOME 并在path中追加
;%MAVEN_HOME%\bin;

2.2.5、在E盘新建文件夹tools 将dubbo.xsd 和 settings.xml 放进去,后面会用到

三、注册中心、消费者、提供者搭建实例
3.1、Zookeeper的搭建

3.2、配置生产者 (Mybatis+datasource+redis)

3.2.1、创建base项目如下:


3.2.2、maven 配置settings.xml 步骤如下:
Window-------Preferences--------Maven-------User Settings 选择GlobalSettings和UserSettings 然后Apply

3.2.3、将maven工程转换成web工程,步骤如下:
右击工程属性,找到Project Facets,选择Dynamic Web Module,3.0
点击apply。这样把这个maven工程转换成了web工程。

3.2.4、配置dubbo本地catalog文件
由于阿里已经不再维护dubbo,原dubbo xml配置地址失效,在编译时dubbo-provider.xml可能会报错,该问题可以通过如下方式解决:下载dubbo.xsd(附件中有)并在eclipse->window->perferences->XML Catalog设置
其中key与配置文件相同为http://code.alibabatech.com/schema/dubbo/dubbo.xsd

3.2.5、修改pom.xml 如下:

<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zx</groupId>
<artifactId>base</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

<name>base</name>
<url>http://maven.apache.org</url>
<description>Dubbo分布式项目平台</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>

<dependencies>
<!-- ====================================spring boot==================================== -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<!-- ====================================AOP==================================== -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- ====================================Junit单元测试==================================== -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.1.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- ===============================Dubbo + Zookeeper==================================== -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.8.4</version>
</dependency>

<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.2</version>
</dependency>

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>

<!-- ===============================mysql==================================== -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- ===============================druid连接池==================================== -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.21</version>
</dependency>
<!-- ===============================fastjson json转对象(JsonObject)==================================== -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.3</version>
</dependency>

<!-- ===============================dom4j读写Xml==================================== -->

<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>

<!-- ====================== javassist 对 java字节码处理    为JBoss实现动态"AOP"框架。======================== -->

<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>

<!-- ===============================commons==================================== -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.1</version>
</dependency>

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>

<!-- ===============================jedis 缓存==================================== -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>

<!-- ===============================mybatis==================================== -->

<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>

<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>

</dependencies>

</project>

3.2.6、创建Application.java 和 SpringUtil.java

Application.java

package com.zx.base;

import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication(exclude = MybatisAutoConfiguration.class)
//@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ImportResource(locations = "classpath*:/spring/applicationContext.xml")
public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer{
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}

@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
//设置启动端口
container.setPort(8081);
}

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

SpringUtil.java

package com.zx.base.rpc.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;

/**
* SpringUtil类
*/
public final class SpringUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

public static Object get(String name) {
return applicationContext.getBean(name);
}

public static <T> T get(Class<T> cl) {
return applicationContext.getBean(cl);
}

public static void sendEvent(ApplicationEvent event) {
applicationContext.publishEvent(event);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtil.applicationContext = applicationContext;
}

}

3.2.7、在src/main/resources 下创建spring文件夹 创建applicationContext.xml (和刚才的Application.java中的locations对应)
创建logback-spring.xml (springboot启动时会自动加载)

applicationContext.xml

<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 根据环境变量加载不同配置文件 运行时请注意在环境变量中配置-DenvTarget=local-->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/config/system-#{systemProperties['envTarget']}.properties
</value>
</list>
</property>
</bean>
<bean class="com.zx.base.rpc.util.SpringUtil" />
</beans>

logback-spring.xml

<!-- spring boot 默认会加载该文件 -->
<configuration debug="true" scan="true" scanPeriod="5 seconds">
<contextName>base</contextName>
<!-- 默认为local -->
<include resource="config/logback-${envTarget:-local}.xml" />
</configuration>

3.2.8、在src/main/resources 下新建config文件夹,创建logback配置文件
与logback-spring.xml 中的config/logback-${envTarget:-local}.xml 对应

logback-local.xml

<included>
<property name="LOG_PATH" value="${catalina.home:-.}/logs" />
<appender name="ROLLINGFILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/base.log</file>
<rollingPolicy  class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/base.log.%d{yyyy-MM-dd}
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%date{yyyy-MM-dd HH:mm:ss} %logger %level -  %msg%n</Pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%date{yyyy-MM-dd HH:mm:ss} %logger %level -  %msg%n</Pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ROLLINGFILE" />
</root>
</included>

system-local.properties.xml (这里配置系统参数,添加Zookeeper地址和Dubbo地址,数据源,redis地址)

#DUBBO CONFIG
dubbo.registry.address=zookeeper://127.0.0.1:2181dubbo.monitor.address=dubbo://127.0.0.1:7070/com.alibaba.dubbo.monitor.MonitorService
#WRITE DB CONFIG
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl.write=jdbc:mysql://123.1.456.789:3306/live
jdbc.username.write=zx
jdbc.password.write=123456
#READ DB CONFIG
jdbc.jdbcUrl.read1=jdbc:mysql://123.1.456.789:3306/live
jdbc.username.read1=zx
jdbc.password.read1=123456
#REDIS CONFIG
redis.host=http://server1:zx@10.1.88.24:6379/0

3.2.9、配置Dubbo
在spring文件夹下创建dubbo-provider.xml

<?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:context="http://www.springframework.org/schema/context"  xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="base" owner="zx"/>
<!--   <dubbo:provider protocol="dubbo" port="20881"/> -->
<dubbo:protocol name="dubbo" port="20881" />
<!-- zookeeper注册中心 -->
<dubbo:registry address="${dubbo.registry.address}"/>
<!-- 配置监控的服务地址和IP -->
<dubbo:monitor address="${dubbo.monitor.address}" />
<!-- 配置导出文件大小上限 -->
<dubbo:protocol payload="11000000"></dubbo:protocol>
</beans>

在 applicationContext.xml中 追加

<!-- dubbo配置 -->
<import resource="dubbo-provider.xml"/>

3.2.10、 创建包和类,下面我的结构图
com.zx.base 下放的springboot启动类
com.zx.base.api.model.manager 下放的表实体类(generator逆向生成的)
com.zx.base.api.service.manager 下放的是service层接口
com.zx.base.api.vo 下放的是分页实体类
com.zx.base.prc.dbrw 下放的是数据源相关的实体类
com.zx.base.rpc.dao.manager 下放的是表Mapper (generator逆向生成的)
com.zx.base.rpc.service.manager 下放的是Service接口实现类
com.zx.base.rpc.util 下放的是各种工具类

ITestService.java

package com.zx.base.api.service.manager;

public interface ITestService {
public String getName();
}

Page.java

package com.zx.base.api.vo;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
public class Page implements Serializable{

private static final long serialVersionUID = -7510688937825278196L;
private int showCount; // 每页显示记录数
private int totalPage; // 总页数
private int totalResult; // 总记录数
private int currentPage; // 当前页
private int currentResult; // 当前记录起始索引
private int currentPageForApp; // 当前页 app用
private int currentResultForApp; // 当前记录起始索引 app用
private boolean entityOrField; // true:需要分页的地方,传入的参数就是Page实体;false:需要分页的地方,传入的参数所代表的实体拥有Page属性
private String pageStr; // 最终页面显示的底部翻页导航,详细见:getPageStr();
private String pageHtml;//一对多分页
private boolean export;
private String isSearch;

public String getIsSearch() {
return isSearch;
}
public void setIsSearch(String isSearch) {
this.isSearch = isSearch;
}

public boolean isExport() {

return export;
}

public void setExport(boolean export) {

this.export = export;
}

public Page() {
this.showCount = 15;
}

public int getTotalPage() {
if (totalResult % showCount == 0)
totalPage = totalResult / showCount;
else
totalPage = totalResult / showCount + 1;
return totalPage;
}

public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}

public int getTotalResult() {
return totalResult;
}

public void setTotalResult(int totalResult) {
this.totalResult = totalResult;
}
public int getPageNum() {
return currentPage;
}
public int getCurrentPage() {
if (currentPage <= 0)
currentPage = 1;
if (currentPage > getTotalPage())
currentPage = getTotalPage();
return currentPage;
}

public int getCurrentPageOfBeforeQuery() {
if (currentPage <= 0)
currentPage = 1;
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}

public int getCurrentPageForApp() {
if (currentPageForApp <= 0)
currentPageForApp = 1;
return currentPageForApp;
}

public void setCurrentPageForApp(int currentPageForApp) {
this.currentPageForApp = currentPageForApp;
}
public String getPageStr() {
StringBuffer sb = new StringBuffer();
if (totalResult > 0) {
sb.append("    <ul class=\"pagination\">\n");
sb.append("<li><a>共&nbsp;"+totalResult+"&nbsp;条</a></li>\n");
if (currentPage == 1) {
/*                sb.append("    <li><a>共<font color=red>" + totalResult
+ "</font>条</a></li>\n");
sb.append("    <li><input type=\"number\" value=\"\" id=\"toGoPage\" style=\"width:60px;text-align:center;float:left\" placeholder=\"页码\"/></li>\n");
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"toTZ();\"  class=\"btn btn-mini btn-success\">跳转</a></li>\n");
sb.append("    <li><a>首页</a></li>\n");*/
sb.append("    <li><a><i class=\"icon-double-angle-left\"></i></a></li>\n");
} else {
/*                sb.append("    <li><a>共<font color=red>" + totalResult
+ "</font>条</a></li>\n");
sb.append("    <li><input type=\"number\" value=\"\" id=\"toGoPage\" style=\"width:60px;text-align:center;float:left\" placeholder=\"页码\"/></li>\n");
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"toTZ();\"  class=\"btn btn-mini btn-success\">跳转</a></li>\n");
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"nextPage(1)\">首页</a></li>\n");*/
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"nextPage("
+ (currentPage - 1) + ")\"><i class=\"icon-double-angle-left\"></i></a></li>\n");
}
int showTag = 5; // 分页标签显示数量
int startTag = 1;
if (currentPage > showTag/2 + 1) {
startTag = currentPage - showTag/2;
startTag = startTag > totalPage-showTag ? totalPage-showTag + 1:startTag;
startTag = startTag <=0 ? 1:startTag;
}
int endTag = startTag + showTag - 1;
for (int i = startTag; i <= totalPage && i <= endTag; i++) {
if (currentPage == i)
sb.append("<li class=\"current active\"><a>" + i + "</a></li>\n");
else
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"nextPage("
+ i + ")\">" + i + "</a></li>\n");
}
if (currentPage == totalPage) {
sb.append("    <li><a><i class=\"icon-double-angle-right\"></i></a></li>\n");
} else {
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"nextPage("
+ (currentPage + 1) + ")\"><i class=\"icon-double-angle-right\"></i></a></li>\n");
}
sb.append("    <li><a>第&nbsp;" + currentPage + "&nbsp;页</a></li>\n");
sb.append("    <li><a>共&nbsp;" + totalPage + "&nbsp;页</a></li>\n");
sb.append("    <li><a style=\"padding-bottom: 0px;padding-top: 1px;padding-left: 0px;padding-right: 0px;margin-left: 10px;\"><input type=\"text\" value=\"\" id=\"toGoPage\" style=\"width:40px;border:0px solid #d5d5d5;text-align:center;font-size: 14px;line-height: 19px;\"></a></li>\n");
sb.append("    <li style=\"cursor:pointer;\"><a onclick=\"toTZ();\" style=\"padding-bottom: 6px;margin-left: 10px;\">跳转</a></li>\n");
sb.append("</ul>\n");
sb.append("<script type=\"text/javascript\">\n");
sb.append("function nextPage(page){");
sb.append("    if(true && document.forms[0]){\n");
sb.append("        var url = document.forms[0].getAttribute(\"action\");\n");
sb.append("        if(url.indexOf('?')>-1){url += \"&"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";}\n");
sb.append("        else{url += \"?"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";}\n");
sb.append("        document.forms[0].action = url+page;\n");
sb.append("        document.forms[0].submit();\n");
sb.append("    }else{\n");
sb.append("        var url = document.location+'';\n");
sb.append("        if(url.indexOf('?')>-1){\n");
sb.append("            if(url.indexOf('currentPage')>-1){\n");
sb.append("                var reg = /currentPage=\\d*/g;\n");
sb.append("                url = url.replace(reg,'currentPage=');\n");
sb.append("            }else{\n");
sb.append("                url += \"&"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";\n");
sb.append("            }\n");
sb.append("        }else{url += \"?"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";}\n");
sb.append("        document.location = url + page;\n");
sb.append("    }\n");
sb.append("}\n");
sb.append("function toTZ(){");
sb.append("var toPaggeVlue = document.getElementById(\"toGoPage\").value;");
sb.append("if(toPaggeVlue == ''){document.getElementById(\"toGoPage\").value=1;return;}");
sb.append("if(isNaN(Number(toPaggeVlue))){document.getElementById(\"toGoPage\").value=1;return;}");
sb.append("nextPage(toPaggeVlue);");
sb.append("}\n");
sb.append("</script>\n");
}
try {
pageStr = new String(sb.toString().getBytes("UTF-8"),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return pageStr;
}

public void setPageStr(String pageStr) {
this.pageStr = pageStr;
}
public String getPageHtml() {
StringBuffer sb = new StringBuffer();
if (totalResult > 0) {
sb.append("<table class='kukaoPageTable' style=\"width: 100%;margin-top: -10px;\">\n");
sb.append("<tr><td><div class=\"row\"><div class=\"col-sm-6\">\n");
sb.append("<div class='kukaoPageAllCounts' style=\"padding-top: 6px;\"> 共 "+totalResult+" 条</div>\n");
sb.append("</div><div class=\"col-sm-6\"> <div style=\"float: right;margin: 0;\"><ul class=\"pagination\" style=\"margin: 0;\">");
if (getCurrentPage() == 1) {
sb.append("<li class=\"paginate_button previous disabled\"><a onclick=\"javascript:void(0);\">上一页</a></li>\n");
} else {
sb.append("    <li class=\"paginate_button previous\"><a  onclick=\"nextPage("+ (currentPage - 1) +")\">上一页</a></li>\n");
}
int showTag = 10; // 分页标签显示数量
int startTag = 1;
if (currentPage > showTag/2 + 1) {
startTag = currentPage - showTag/2;
startTag = startTag > getTotalPage()-showTag ? getTotalPage()-showTag + 1:startTag;
startTag = startTag <=0 ? 1:startTag;
}
int endTag = startTag + showTag - 1;
for (int i = startTag; i <= getTotalPage() && i <= endTag; i++) {
if (currentPage == i)
sb.append("<li class=\"paginate_button active\"><a>" + i + "</a></li>\n");
else
sb.append("    <li class=\"paginate_button\"><a onclick=\"nextPage("+ i + ")\">" + i + "</a></li>\n");
}
if (currentPage == getTotalPage()) {
sb.append("    <li class=\"paginate_button next\"><a onclick=\"javascript:void(0);\">下一页</a></li>\n");
} else {
sb.append("    <li class=\"paginate_button next\"><a onclick=\"nextPage("+(currentPage + 1)+")\">下一页</a></li>\n");
}
sb.append("</ul></div></div></div></td></tr></table>\n");
sb.append("<script type=\"text/javascript\">\n");
sb.append("function nextPage(page){");
sb.append("    if(true && document.forms[0]){\n");
sb.append("        var url = document.forms[0].getAttribute(\"action\");\n");
sb.append("        if(url.indexOf('?')>-1){url += \"&"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";}\n");
sb.append("        else{url += \"?"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";}\n");
sb.append("        document.forms[0].action = url+page;\n");
sb.append("        document.forms[0].submit();\n");
sb.append("    }else{\n");
sb.append("        var url = document.location+'';\n");
sb.append("        if(url.indexOf('?')>-1){\n");
sb.append("            if(url.indexOf('currentPage')>-1){\n");
sb.append("                var reg = /currentPage=\\d*/g;\n");
sb.append("                url = url.replace(reg,'currentPage=');\n");
sb.append("            }else{\n");
sb.append("                url += \"&"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";\n");
sb.append("            }\n");
sb.append("        }else{url += \"?"
+ (entityOrField ? "currentPage" : "page.currentPage")
+ "=\";}\n");
sb.append("        document.location = url + page;\n");
sb.append("    }\n");
sb.append("}\n");
sb.append("</script>\n");
}
try {
pageHtml = new String(sb.toString().getBytes("UTF-8"),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return pageHtml;
}
public void setPageHtml(String pageHtml) {
this.pageHtml = pageHtml;
}

public int getShowCount() {
return showCount;
}

public void setShowCount(int showCount) {

this.showCount = showCount;
}

public int getCurrentResult() {
currentResult = (getCurrentPage() - 1) * getShowCount();
if (currentResult < 0)
currentResult = 0;
return currentResult;
}

public void setCurrentResult(int currentResult) {
this.currentResult = currentResult;
}

public int getCurrentResultForApp() {
currentResultForApp = (getCurrentPageForApp() - 1) * getShowCount();
if (currentResultForApp < 0)
currentResultForApp = 0;
return currentResultForApp;
}

public void setCurrentResultForApp(int currentResultForApp) {
this.currentResultForApp = currentResultForApp;
}

public boolean isEntityOrField() {
return entityOrField;
}

public void setEntityOrField(boolean entityOrField) {
this.entityOrField = entityOrField;
}
}

DynamicDataSource.java

package com.zx.base.prc.dbrw;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

private Object writeDataSource; //写数据源

private List<Object> readDataSources; //多个读数据源

private int readDataSourceSize; //读数据源个数

private int readDataSourcePollPattern = 0; //获取读数据源方式,0:随机,1:轮询

private AtomicLong counter = new AtomicLong(0);

private static final Long MAX_POOL = Long.MAX_VALUE;

private final Lock lock = new ReentrantLock();

@Override
public void afterPropertiesSet() {
if (this.writeDataSource == null) {
throw new IllegalArgumentException("Property 'writeDataSource' is required");
}
setDefaultTargetDataSource(writeDataSource);
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DynamicDataSourceGlobal.WRITE.name(), writeDataSource);
if (this.readDataSources == null) {
readDataSourceSize = 0;
} else {
for(int i=0; i<readDataSources.size(); i++) {
targetDataSources.put(DynamicDataSourceGlobal.READ.name() + i, readDataSources.get(i));
}
readDataSourceSize = readDataSources.size();
}
setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}

@Override
protected Object determineCurrentLookupKey() {

DynamicDataSourceGlobal dynamicDataSourceGlobal = DynamicDataSourceHolder.getDataSource();

if(dynamicDataSourceGlobal == null
|| dynamicDataSourceGlobal == DynamicDataSourceGlobal.WRITE
|| readDataSourceSize <= 0) {
return DynamicDataSourceGlobal.WRITE.name();
}

int index = 1;

if(readDataSourcePollPattern == 1) {
//轮询方式
long currValue = counter.incrementAndGet();
if((currValue + 1) >= MAX_POOL) {
try {
lock.lock();
if((currValue + 1) >= MAX_POOL) {
counter.set(0);
}
} finally {
lock.unlock();
}
}
index = (int) (currValue % readDataSourceSize);
} else {
//随机方式
index = ThreadLocalRandom.current().nextInt(0, readDataSourceSize);
}
return dynamicDataSourceGlobal.name() + index;
}

public void setWriteDataSource(Object writeDataSource) {
this.writeDataSource = writeDataSource;
}

public void setReadDataSources(List<Object> readDataSources) {
this.readDataSources = readDataSources;
}

public void setReadDataSourcePollPattern(int readDataSourcePollPattern) {
this.readDataSourcePollPattern = readDataSourcePollPattern;
}
}

DynamicDataSourceGlobal.java

package com.zx.base.prc.dbrw;
public enum DynamicDataSourceGlobal {
READ, WRITE;
}

DynamicDataSourceHolder.java

package com.zx.base.prc.dbrw;
public final class DynamicDataSourceHolder {
private static final ThreadLocal<DynamicDataSourceGlobal> holder = new  ThreadLocal<DynamicDataSourceGlobal>();
private DynamicDataSourceHolder() {
//
}
public static void putDataSource(DynamicDataSourceGlobal dataSource){
holder.set(dataSource);
}
public static DynamicDataSourceGlobal getDataSource(){
return holder.get();
}
public static void clearDataSource() {
holder.remove();
}
}

DynamicDataSourceTransactionManager.java

package com.zx.base.prc.dbrw;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;

public class DynamicDataSourceTransactionManager extends DataSourceTransactionManager {

/**
* 只读事务到读库,读写事务到写库
* @param transaction
* @param definition
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {

//设置数据源
boolean readOnly = definition.isReadOnly();
if(readOnly) {
DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.READ);
} else {
DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.WRITE);
}
super.doBegin(transaction, definition);
}

/**
* 清理本地线程的数据源
* @param transaction
*/
@Override
protected void doCleanupAfterCompletion(Object transaction) {
super.doCleanupAfterCompletion(transaction);
DynamicDataSourceHolder.clearDataSource();
}
}

DynamicPlugin.java

package com.zx.base.prc.dbrw;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Intercepts({
@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class, Object.class }),
@Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class }) })
public class DynamicPlugin implements Interceptor {

protected static final Logger logger = LoggerFactory.getLogger(DynamicPlugin.class);

private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";

private static final Map<String, DynamicDataSourceGlobal> cacheMap = new ConcurrentHashMap<>();

@Override
public Object intercept(Invocation invocation) throws Throwable {

boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
if(!synchronizationActive) {
Object[] objects = invocation.getArgs();
MappedStatement ms = (MappedStatement) objects[0];

DynamicDataSourceGlobal dynamicDataSourceGlobal = null;

if((dynamicDataSourceGlobal = cacheMap.get(ms.getId())) == null) {
//读方法
if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
//!selectKey 为自增id查询主键(SELECT LAST_INSERT_ID() )方法,使用主库
if(ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
dynamicDataSourceGlobal = DynamicDataSourceGlobal.WRITE;
} else {
BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);
String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " ");
if(sql.matches(REGEX)) {
dynamicDataSourceGlobal = DynamicDataSourceGlobal.WRITE;
} else {
dynamicDataSourceGlobal = DynamicDataSourceGlobal.READ;
}
}
}else{
dynamicDataSourceGlobal = DynamicDataSourceGlobal.WRITE;
}
logger.warn("设置方法[{}] use [{}] Strategy, SqlCommandType [{}]..", ms.getId(), dynamicDataSourceGlobal.name(), ms.getSqlCommandType().name());
cacheMap.put(ms.getId(), dynamicDataSourceGlobal);
}
DynamicDataSourceHolder.putDataSource(dynamicDataSourceGlobal);
}

return invocation.proceed();
}

@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
return target;
}
}

@Override
public void setProperties(Properties properties) {
//
}
}

TestServiceImpl.java

package com.zx.base.rpc.service.manager;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.zx.base.api.service.manager.ITestService;
import com.zx.base.rpc.dao.manager.TeacherMapper;
@Service
public class TestServiceImpl implements ITestService{

@Resource
TeacherMapper teacherMapper;

@Override
public String getName() {
return "zhouxuan";
}

}

PagePlugin.java

package com.zx.base.rpc.util;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import javax.xml.bind.PropertyException;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

import com.zx.base.api.vo.Page;

@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {
Connection.class, Integer.class }) })
public class PagePlugin implements Interceptor {

private static String dialect = ""; // 数据库方言
private static String pageSqlId = ""; // mapper.xml中需要拦截的ID(正则匹配)

public Object intercept(Invocation ivk) throws Throwable {
if (ivk.getTarget() instanceof RoutingStatementHandler) {
RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk
.getTarget();
BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper
.getValueByFieldName(statementHandler, "delegate");
MappedStatement mappedStatement = (MappedStatement) ReflectHelper
.getValueByFieldName(delegate, "mappedStatement");

if (mappedStatement.getId().matches(pageSqlId)) { // 拦截需要分页的SQL
BoundSql boundSql = delegate.getBoundSql();
Object parameterObject = boundSql.getParameterObject();// 分页SQL<select>中parameterType属性对应的实体参数,即Mapper接口中执行分页方法的参数,该参数不得为空
if (parameterObject == null) {
throw new NullPointerException("parameterObject尚未实例化!");
} else {
Connection connection = (Connection) ivk.getArgs()[0];
String sql = boundSql.getSql();
// String countSql = "select count(0) from (" + sql+
// ") as tmp_count"; //记录统计
String countSql = "select count(0) from (" + sql
+ ")  tmp_count"; // 记录统计 == oracle 加 as 报错(SQL
// command not properly ended)
PreparedStatement countStmt = connection
.prepareStatement(countSql);
BoundSql countBS = new BoundSql(
mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), parameterObject);
setParameters(countStmt, mappedStatement, countBS,parameterObject);
ResultSet rs = countStmt.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rs.close();
countStmt.close();
Page page = null;
if (parameterObject instanceof Page) { // 参数就是Page实体
page = (Page) parameterObject;
page.setEntityOrField(true); // 见com.jalan.entity.Page.entityOrField
// 注释
page.setTotalResult(count);
} else { // 参数为某个实体,该实体拥有Page属性
Field pageField = ReflectHelper.getFieldByFieldName(
parameterObject, "page");
if (pageField != null) {
page = (Page) ReflectHelper.getValueByFieldName(
parameterObject, "page");
if (page == null)
page = new Page();
page.setEntityOrField(false); // 见com.jalan.entity.Page.entityOrField
// 注释
page.setTotalResult(count);
ReflectHelper.setValueByFieldName(parameterObject,
"page", page); // 通过反射,对实体对象设置分页对象
} else {
throw new NoSuchFieldException(parameterObject
.getClass().getName() + "不存在 page 属性!");
}
}
if( !page.isExport()){
String pageSql = generatePageSql(sql, page);
ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); // 将分页sql语句反射回BoundSql.
}
}
}
}
return ivk.proceed();
}

/**
* 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.
* DefaultParameterHandler
*
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
*/
@SuppressWarnings("unchecked")
private void setParameters(PreparedStatement ps,
MappedStatement mappedStatement, BoundSql boundSql,
Object parameterObject) throws SQLException {
ErrorContext.instance().activity("setting parameters")
.object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql
.getParameterMappings();
if (parameterMappings != null) {
Configuration configuration = mappedStatement.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration
.getTypeHandlerRegistry();
MetaObject metaObject = parameterObject == null ? null
: configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
PropertyTokenizer prop = new PropertyTokenizer(propertyName);
if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry
.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (propertyName
.startsWith(ForEachSqlNode.ITEM_PREFIX)
&& boundSql.hasAdditionalParameter(prop.getName())) {
value = boundSql.getAdditionalParameter(prop.getName());
if (value != null) {
value = configuration.newMetaObject(value)
.getValue(
propertyName.substring(prop
.getName().length()));
}
} else {
value = metaObject == null ? null : metaObject
.getValue(propertyName);
}
@SuppressWarnings("rawtypes")
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException(
"There was no TypeHandler found for parameter "
+ propertyName + " of statement "
+ mappedStatement.getId());
}
typeHandler.setParameter(ps, i + 1, value,
parameterMapping.getJdbcType());
}
}
}
}

/**
* 根据数据库方言,生成特定的分页sql
*
* @param sql
* @param page
* @return
*/
private String generatePageSql(String sql, Page page) {
if (page != null && Tools.notEmpty(dialect)) {
StringBuffer pageSql = new StringBuffer();
if ("mysql".equals(dialect)) {
pageSql.append(sql);
pageSql.append(" limit " + page.getCurrentResult() + ","
+ page.getShowCount());
} else if ("oracle".equals(dialect)) {
pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
pageSql.append(sql);
// pageSql.append(") as tmp_tb where ROWNUM<=");
pageSql.append(") tmp_tb where ROWNUM<=");
pageSql.append(page.getCurrentResult() + page.getShowCount());
pageSql.append(") where row_id>");
pageSql.append(page.getCurrentResult());
}
return pageSql.toString();
} else {
return sql;
}
}

public Object plugin(Object arg0) {
return Plugin.wrap(arg0, this);
}

public void setProperties(Properties p) {
dialect = p.getProperty("dialect");
if (Tools.isEmpty(dialect)) {
try {
throw new PropertyException("dialect property is not found!");
} catch (PropertyException e) {
e.printStackTrace();
}
}
pageSqlId = p.getProperty("pageSqlId");
if (Tools.isEmpty(pageSqlId)) {
try {
throw new PropertyException("pageSqlId property is not found!");
} catch (PropertyException e) {
e.printStackTrace();
}
}
}

}

ReflectHelper.java

package com.zx.base.rpc.util;
import java.lang.reflect.Field;
/**
* @author Administrator
*     反射工具
*/
public class ReflectHelper {
/**
* 获取obj对象fieldName的Field
* @param obj
* @param fieldName
* @return
*/
public static Field getFieldByFieldName(Object obj, String fieldName) {
for (Class<?> superClass = obj.getClass(); superClass !=  Object.class; superClass = superClass
.getSuperclass()) {
try {
return superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
}
}
return null;
}
/**
* 获取obj对象fieldName的属性值
* @param obj
* @param fieldName
* @return
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static Object getValueByFieldName(Object obj, String fieldName)
throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field field = getFieldByFieldName(obj, fieldName);
Object value = null;
if(field!=null){
if (field.isAccessible()) {
value = field.get(obj);
} else {
field.setAccessible(true);
value = field.get(obj);
field.setAccessible(false);
}
}
return value;
}
/**
* 设置obj对象fieldName的属性值
* @param obj
* @param fieldName
* @param value
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void setValueByFieldName(Object obj, String fieldName,
Object value) throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
if (field.isAccessible()) {
field.set(obj, value);
} else {
field.setAccessible(true);
field.set(obj, value);
field.setAccessible(false);
}
}
}

Tools.java

package com.zx.base.rpc.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

public class Tools {

/**
* 随机生成六位数验证码
*
* @return
*/
public static int getRandomNum() {
Random r = new Random();
return r.nextInt(900000) + 100000;// (Math.random()*(999999-100000)+100000)
}

/**
* 随机生成主键
*
* @return
*/
public static String getRandomKey() {
long part1 = System.currentTimeMillis();
String part2 = String.format("%07d", new Random().nextInt(10000000));
StringBuffer str = new StringBuffer().append(part1).append(part2);
return str.toString();
}

/**
* 检测字符串是否不为空(null,"","null")
*
* @param s
* @return 不为空则返回true,否则返回false
*/
public static boolean notEmpty(String s) {
return s != null && !"".equals(s) && !"null".equals(s);
}

/**
* 检测字符串是否为空(null,"","null")
*
* @param s
* @return 为空则返回true,不否则返回false
*/
public static boolean isEmpty(String s) {
return s == null || "".equals(s) || "null".equals(s);
}

/**
* 字符串转换为字符串数组
*
* @param str
*            字符串
* @param splitRegex
*            分隔符
* @return
*/
public static String[] str2StrArray(String str, String splitRegex) {
if (isEmpty(str)) {
return null;
}
System.out.println(splitRegex);
return str.split(splitRegex);
}

/**
* 用默认的分隔符(,)将字符串转换为字符串数组
*
* @param str
*            字符串
* @return
*/
public static String[] str2StrArray(String str) {
return str2StrArray(str, ",\\s*");
}

/**
* 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串
*
* @param date
* @return yyyy-MM-dd HH:mm:ss
*/
public static String date2Str(Date date) {
return date2Str(date, "yyyy-MM-dd HH:mm:ss");
}

/**
* 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期
*
* @param date
* @return
*/
public static Date str2Date(String date) {
if (notEmpty(date)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return new Date();
} else {
return null;
}
}

/**
* 按照参数format的格式,日期转字符串
*
* @param date
* @param format
* @return
*/
public static String date2Str(Date date, String format) {
if (date != null) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
} else {
return "";
}
}

/**
* 把时间根据时、分、秒转换为时间段
*
* @param StrDate
*/
public static String getTimes(String StrDate) {
String resultTimes = "";

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Date now;

try {
now = new Date();
java.util.Date date = df.parse(StrDate);
long times = now.getTime() - date.getTime();
long day = times / (24 * 60 * 60 * 1000);
long hour = (times / (60 * 60 * 1000) - day * 24);
long min = ((times / (60 * 1000)) - day * 24 * 60 - hour * 60);
long sec = (times / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);

StringBuffer sb = new StringBuffer();
// sb.append("发表于:");
if (hour > 0) {
sb.append(hour + "小时前");
} else if (min > 0) {
sb.append(min + "分钟前");
} else {
sb.append(sec + "秒前");
}

resultTimes = sb.toString();
} catch (ParseException e) {
e.printStackTrace();
}

return resultTimes;
}

public static boolean isNum(String strin) {
String s = strin.replaceAll("[0-9;]+", "");
if (s.equals("")) {
return true;
} else {
return false;
}
}

public static int getWordCount(String s) {
s = s.replaceAll("[^\\x00-\\xff]", "**");
int length = s.length();
return length;
}

/**
* 按字节长度截取字符串
*
* @param str
*            将要截取的字符串参数
* @param toCount
*            截取的字节长度
* @param more
*            字符串末尾补上的字符串
* @return 返回截取后的字符串
*/
@SuppressWarnings("static-access")
public static String substring(String str, int toCount, String more) {
int reInt = 0;
String reStr = "";
if (str == null)
return "";
char[] tempChar = str.toCharArray();
for (int kk = 0; (kk < tempChar.length && toCount > reInt); kk++) {
String s1 = str.valueOf(tempChar[kk]);
byte[] b = s1.getBytes();
reInt += b.length;
reStr += tempChar[kk];
}
if (toCount == reInt || (toCount == reInt - 1))
reStr += more;
return reStr;
}

/**
* 判断两个字符串是否相等
*
* @param one
* @param two
* @return
*/
public static boolean isEquals(String one, String two) {
if (one != null && two != null) {
return one.trim().equals(two.trim());
} else if (one == null && two == null) {
return true;
} else if (one == null && two.trim().length() == 0) {
return true;
} else if (two == null && one.trim().length() == 0) {
return true;
} else {
return false;
}
}

/**
* @Title: getFormatDate
* @param @param format
* @param @return
* @return String
* @throws
*/
public static String getFormatDate(String format) {
SimpleDateFormat date = new SimpleDateFormat(format);
return date.format(new Date());
}

}

3.2.11、集成mybatis
在pom.xml文件中追加 mybatis-generator配置

<build>
<finalName>base</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- mybatis代码自动生成工具,执行命令  mybatis-generator:generate -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<configurationFile>src/main/resources/mybatis/generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArguments>
<verbose />
<bootclasspath>${java.home}\lib\rt.jar;${java.home}\lib\jce.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>base</finalName>
<appendAssemblyId>true</appendAssemblyId>
<descriptors>
<descriptor>src/main/resources/assembly/api.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.tld</include>
<include>**/*.json</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.tld</include>
<include>**/*.p12</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

3.2.12、创建mybatis 配置文件

api.xml

<assembly>
<id>api</id>
<formats>
<format>jar</format>
</formats>
<baseDirectory>com\zx\base</baseDirectory>
<fileSets>
<fileSet>
<directory>${project.basedir}\target\base\WEB-INF\classes\com\zx\base\api</directory>
<excludes>
<exclude>${project.basedir}\target\base\WEB-INF\classes\com\zx\base\api\service\manager\TestServiceImpl.class</exclude>
</excludes>
<outputDirectory>/api</outputDirectory>
</fileSet>
</fileSets>
</assembly>

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator  Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- 数据库驱动 -->
<classPathEntry  location="D:/apache-maven-3.3.9/repository/mysql/mysql-connector-java/5.1.40/mysql-connector-java-5.1.40.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<property name="javaFileEncoding" value="UTF-8"/>
<commentGenerator   type="org.mybatis.generator.internal.CustomeCommentGenerator">
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="false" />
<property name="suppressDate" value="true" />
</commentGenerator>
<!--数据库链接URL,用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"  connectionURL="jdbc:mysql://123.1.456.789:3306/live" userId="kuakao"  password="123456"></jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<javaModelGenerator targetPackage="com.zx.base.api.model.manager"
targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>

<!--对应的mapper.xml文件 -->
<sqlMapGenerator targetPackage="manager"
targetProject="./src/main/resources/mybatis">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>

<!-- 对应的Mapper接口类文件 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.zx.base.rpc.dao.manager"  targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 要生成哪些表-->
<table tableName="sys_user" domainObjectName="SysUser"  enableCountByExample="true" enableUpdateByExample="true"  enableDeleteByExample="true" enableSelectByExample="true"  selectByExampleQueryId="true">
</table>

</context>
</generatorConfiguration>

mybatis-config-mappings.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 这个配置使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载 -->
<setting name="aggressiveLazyLoading" value="true" />
<!-- 允许或不允许多种结果集从一个单独的语句中返回(需要适合的驱动) -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 使用列标签代替列名。不同的驱动在这方便表现不同。参考驱动文档或充分测试两种方法来决定所使用的驱动 -->
<setting name="useColumnLabel" value="true" />
<!-- 允许JDBC支持生成的键。需要适合的驱动。如果设置为true则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如Derby) -->
<setting name="useGeneratedKeys" value="false" />
<!-- 指定MyBatis如何自动映射列到字段/属性。PARTIAL只会自动映射简单,没有嵌套的结果。FULL会自动映射任意复杂的结果(嵌套的或其他情况) -->
<setting name="autoMappingBehavior" value="FULL" />
<!-- 配置默认的执行器。SIMPLE执行器没有什么特别之处。REUSE执行器重用预处理语句。BATCH执行器重用语句和批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 设置超时时间,它决定驱动等待一个数据库响应的时间 -->
<setting name="defaultStatementTimeout" value="25000" />
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<typeAliases>
<package name="com.zx.base.api.model.manager" />
</typeAliases>
<plugins>
<plugin interceptor="com.zx.base.rpc.util.PagePlugin">
<property name="dialect" value="mysql" />
<property name="pageSqlId" value=".*listPage*.*" />
</plugin>
<plugin interceptor="com.zx.base.prc.dbrw.DynamicPlugin"></plugin>
</plugins>
</configuration>

3.2.13、配置datasource 和 redis

datasource.xml

<?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:context="http://www.springframework.org/schema/context"  xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
">
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="10000" />
<property name="logSlowSql" value="true" />
</bean>

<bean id="abstractDataSource" abstract="true"  class="com.alibaba.druid.pool.DruidDataSource" init-method="init"  destroy-method="close">
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="50" />
<property name="minIdle" value="50" />
<property name="maxActive" value="500" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"  value="20" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" />
<property name="connectionProperties"  value="druid.stat.mergeSql=true" />
<property name="useGlobalDataSourceStat" value="true" />
</bean>

<bean id="dataSourceWrite" parent="abstractDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url"  value="${jdbc.jdbcUrl.write}?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull" />
<property name="username" value="${jdbc.username.write}" />
<property name="password" value="${jdbc.password.write}" />
</bean>
<bean id="dataSourceRead" parent="abstractDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url"  value="${jdbc.jdbcUrl.read1}?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull" />
<property name="username" value="${jdbc.username.read1}" />
<property name="password" value="${jdbc.password.read1}" />
</bean>

<bean id="dataSource" class="com.zx.base.prc.dbrw.DynamicDataSource"  primary="true">
<property name="writeDataSource" ref="dataSourceWrite" />
<property name="readDataSources">
<list>
<ref bean="dataSourceRead" />
</list>
</property>
</bean>
<bean id="zxSessionFactory"  class="org.mybatis.spring.SqlSessionFactoryBean" primary="true">
<property name="dataSource" ref="dataSource" />
<property name="configLocation"  value="classpath:/mybatis/mybatis-config-mappings.xml" />
<property name="mapperLocations">
<array>
<value>classpath:/mybatis/manager/*Mapper.xml</value>

</array>
</property>
</bean>

<bean id="zxmapper"  class="org.mybatis.spring.mapper.MapperScannerConfigurer" primary="true">
<property name="basePackage" value="com.zx.base.rpc.dao.manager" />
<property name="sqlSessionFactoryBeanName" value="zxSessionFactory"  />
</bean>
<!-- zx事务配置 -->
<bean id="zxTxManager"
class="com.zx.base.prc.dbrw.DynamicDataSourceTransactionManager"  primary="true">
<property name="dataSource" ref="dataSource" />
</bean>

<tx:advice id="zxTxAdvice" transaction-manager="zxTxManager">
<tx:attributes>
<tx:method name="query*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="list*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="load*" read-only="true"/>
<tx:method name="search*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"  rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="zxPointCut"
expression="(execution (*  com.zx.base.rpc.service.manager.*ServiceImpl.*(..)))" />
<aop:advisor advice-ref="zxTxAdvice" pointcut-ref="zxPointCut" />
</aop:config>
<tx:annotation-driven transaction-manager="zxTxManager" />

</beans>

redis.xml

<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">

<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="jedisPoolConfig"/>
<constructor-arg index="1">
<list>
<bean name="master" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.host}"/>
</bean>
</list>
</constructor-arg>
</bean>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="500" />
<!-- 最小空闲连接数 -->
<property name="minIdle" value="10" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="100" />
<!-- 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
<property name="numTestsPerEvictionRun" value="250"/>
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<property name="minEvictableIdleTimeMillis" value="-1" />
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<property name="maxWaitMillis" value="10000"/>
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="jmxEnabled" value="true"/>
<property name="jmxNamePrefix" value="jjk"/>
<property name="blockWhenExhausted" value="false"/>
</bean>
</beans>

在 applicationContext.xml中 追加

<!-- 数据源配置 -->
<import resource="datasource.xml"/>
<!-- redis配置 -->
<import resource="redis.xml"/>

3.2.14、将接口ITestService 发布
在dubbo-provider.xml中 添加

<dubbo:service interface="com.zx.base.api.service.manager.ITestService"  ref="testServiceImpl" protocol="dubbo" timeout="1200000" />

3.2.15、启动base
右键Application.java ------run as ------Java Application
看到如下信息说明生产者启动成功

3.2.16、查看dubbo是否注册了 刚刚的 ITestService
进入zookeeper安装目录 /bin 文件夹下 点击zkCli

输入 ls /dubbo

可以看到已经注册成功了
至此生产者项目已经搭建成功

3.3、配置消费者

3.3.1、新建Maven项目 consumer 步骤和之前的base一样

3.3.2、修改pom.xml文件

<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.0http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zx</groupId>
<artifactId>consumer</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>consumer Maven Webapp</name>
<url>http://maven.apache.org</url>

<properties>
<!-- jar 版本设置 -->
<spring.version>4.1.4.RELEASE</spring.version>
<junit.version>4.11</junit.version>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.5</slf4j.version>
<poi.version>3.10-FINAL</poi.version>
<commons-lang3.version>3.1</commons-lang3.version>
<commons-io.version>2.4</commons-io.version>
<commons-codec.version>1.8</commons-codec.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<commons-beanutils.version>1.8.3</commons-beanutils.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>

<dependency>
<groupId>com.zx</groupId>
<artifactId>base</artifactId>
<version>1.0</version>
<scope>system</scope>
<type>jar</type>
<systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/base-api.jar</systemPath>
</dependency>

<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!--  https://mvnrepository.com/artifact/org.apache.commons/commons-email -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.7</version>
<scope>compile</scope>
</dependency>
<!-- ################### json解释 ################ -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-lgpl</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-lgpl</artifactId>
<version>1.9.6</version>
</dependency>
<!-- ############################################## -->
<!-- javax提供的annotation -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- 提供对c标签的支持 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- aop代理 -->
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version> 1.6.11</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.5.0</version>
</dependency>
<!-- GENERAL UTILS begin <dependency>  <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId>  <version>${commons-lang3.version}</version>
</dependency> <dependency> <groupId>commons-codec</groupId>  <artifactId>commons-codec</artifactId> <version>${commons-codec.version}</version>
</dependency> -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons-beanutils.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.1</version>
</dependency>
<!-- http client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.1</version>
</dependency>
<!-- servlet api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<!--json-lib -->
<dependency>
<groupId>net.sf.ezmorph</groupId>
<artifactId>ezmorph</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>net.sf.morph</groupId>
<artifactId>morph</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>org.fusesource</groupId>
<artifactId>sigar</artifactId>
<version>1.6.4</version>
</dependency>
<!-- <dependency> <groupId>javax.servlet</groupId>  <artifactId>servlet-api</artifactId> <version>2.5</version>  <scope>provided</scope>
</dependency> -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- <dependency> <groupId>commons-beanutils</groupId>  <artifactId>commons-beanutils</artifactId> <version>1.9.2</version>
</dependency> -->
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<!-- zookeeper -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.2</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-webservice</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.8.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson  -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
</dependency>
<!--  https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>consumer</finalName>
<plugins>
<!-- Plugin to run and test through maven -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.3.6.v20151106</version>
<configuration>
<systemProperties>
</systemProperties>
<scanIntervalSeconds>0</scanIntervalSeconds>
<stopKey>foo</stopKey>
<stopPort>9991</stopPort>
<httpConnector>
<port>8080</port>
</httpConnector>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
<!-- Ensures we are compiling at 1.8 level -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

3.3.3、右键base项目 Run As ----Maven install
将生成的base-api.jar 复制到 consumer的 src/main/webapp/WEB-INF/lib下

3.3.4、搭建SpringMVC ,详情请见 使用Maven搭建SpringMVC
下面是我的结构图
com.zx.controller包 下放的是Controller层
com.zx.util包 下放的是工具类

web.xml
<?xml version="1.0" encoding="UTF-8"?>


org.springframework.web.context.ContextLoaderListener


org.springframework.web.util.IntrospectorCleanupListener



contextConfigLocation
classpath:/spring/applicationContext*.xml

<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener
</listener-class>
</listener>
<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>classpath:/config/logback.xml</param-value>
</context-param>

<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

spring-servlet.xml

<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"  xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config />
<!-- 配置扫描的包 -->
<context:component-scan base-package="com.zx.**" />
<!-- 注册HandlerMapper、HandlerAdapter两个映射类 -->
<mvc:annotation-driven />
<!-- 访问静态资源 -->
<mvc:default-servlet-handler />

<!-- 视图解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

</beans>
applicationContext.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"  xmlns:task="http://www.springframework.org/schema/task"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config />
<context:component-scan base-package="com.zx.*" />
<aop:aspectj-autoproxy proxy-target-class="true" />

<import resource="redis.xml"/>
<import resource="dubbo-consumer.xml"/>

<mvc:annotation-driven />
<!-- <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize"><value>100000</value></property>
<property name="defaultEncoding"><value>UTF-8</value></property>
</bean>  -->

<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/config/system-#{systemProperties['envTarget']}.properties
</value>
</list>
</property>
</bean>
<!-- spring session -->
<!-- <beans:bean id="jedisConnectionFactory"  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<beans:property name="shardInfo" ref="jedisShareInfo"/>
</beans:bean>

<beans:bean name="jedisShareInfo" class="redis.clients.jedis.JedisShardInfo">
<beans:constructor-arg index="0" value="${redis.host}"/>
</beans:bean> -->

<!-- 将session放入redis -->
<!--  <beans:bean id="redisHttpSessionConfiguration"
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<beans:property name="maxInactiveIntervalInSeconds" value="600" />
</beans:bean>
<util:constant  static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
-->
<mvc:annotation-driven  conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.zx.util.TimestampConverter"></bean>
</list>
</property>
</bean>
<bean class="com.zx.util.SpringUtil" />
<bean class="com.zx.util.SpringContextHolder" lazy-init="false" />

</beans>

dubbo-consumer.xml

<?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:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="consumer" owner="zx"></dubbo:application>
<!-- zookeeper注册中心 -->
<dubbo:registry address="${dubbo.registry.address}"  file="${catalina.home}/dubbo-registry/dubbo-registry.properties" />
<!--  配置监控的服务地址和IP -->
<!-- <dubbo:monitor address="${dubbo.monitor.address}" /> -->

<!--  <dubbo:reference interface="com.zx.base.api.ITestService" id="testService"  check="false"/> -->
<dubbo:reference interface="com.zx.base.api.service.manager.ITestService"  id="testService" protocol="dubbo" check="false" />

</beans>

redis.xml

<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="jedisPoolConfig"/>
<constructor-arg index="1">
<list>
<bean name="master" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0"  value="http://server1:zx@123.1.456.24:6379/0"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="500" />
<!-- 最小空闲连接数 -->
<property name="minIdle" value="10" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="100" />
<!-- 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
<property name="numTestsPerEvictionRun" value="250"/>
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<property name="minEvictableIdleTimeMillis" value="-1" />
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<property name="maxWaitMillis" value="10000"/>
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="jmxEnabled" value="true"/>
<property name="jmxNamePrefix" value="jjk"/>
<property name="blockWhenExhausted" value="false"/>
</bean>
</beans>

logback.xml

<!DOCTYPE xml>
<configuration debug="true" scan="true" scanPeriod="5 seconds">
<contextName>consumer</contextName>
<!-- 默认为local -->
<include resource="config/logback-${envTarget:-local}.xml" />
</configuration>

logback-local.xml

<included>
<property name="LOG_PATH" value="${catalina.home}/logs" />
<appender
name="ROLLINGFILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/consumer.log</file>
<rollingPolicy  class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/consumer.log.%d{yyyy-MM-dd}
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%date{yyyy-MM-dd HH:mm:ss} %logger %level -  %msg%n</Pattern>
</encoder>
</appender>
<appender
name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%date{yyyy-MM-dd HH:mm:ss} %logger %level -  %msg%n</Pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ROLLINGFILE" />
</root>
</included>

system-local.properties

#DUBBO CONFIG
dubbo.registry.address=zookeeper://127.0.0.1:2181

SpringUtil.java

package com.zx.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
public final class SpringUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

public static Object get(String name) {
return applicationContext.getBean(name);
}

public static <T> T get(Class<T> cl) {
return applicationContext.getBean(cl);
}

public static void sendEvent(ApplicationEvent event) {
applicationContext.publishEvent(event);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtil.applicationContext = applicationContext;
}

}

SpringContextHolder.java

package com.zx.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext.
*/
@SuppressWarnings("unchecked")
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

private static ApplicationContext applicationContext = null;
private static final Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);

/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}

/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}

/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}

/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clear() {
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
applicationContext = null;
}

/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
logger.debug("注入ApplicationContext到SpringContextHolder:" + applicationContext);

if (SpringContextHolder.applicationContext != null) {
logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:"
+ SpringContextHolder.applicationContext);
}

SpringContextHolder.applicationContext = applicationContext; //NOSONAR

}

/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
public void destroy() throws Exception {
SpringContextHolder.clear();
}

/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
}
}
}

TimestampConverter.java

package com.zx.util;

import java.sql.Timestamp;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class TimestampConverter implements Converter<String, Timestamp>{

@Override
public Timestamp convert(String timeStr) {
if(timeStr != null && timeStr.length() > 0){
return Timestamp.valueOf(timeStr);
}
return null;
}

}

TestController.java

package com.zx.controller;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.zx.base.api.service.manager.ITestService;

@Controller
@RequestMapping("/test")
public class TestController {
@Resource
ITestService testService;

@RequestMapping("/helloworld")
public ModelAndView hello(){

ModelAndView view = new ModelAndView("success");
return view;
}

@RequestMapping("/getName")
public void getName(){
String name = testService.getName();
System.out.println(name);
}
}

success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>成功页面</title>
</head>
<body>
<h4>恭喜您成功了</h4>
</body>
</html>

3.3.5、启动consumer


3.3.6、调用Controller接口
成功映射到success.jsp

在调一下另外一个Service层的接口

看到控制台成功打印 ,说明成功了

至此,消费者项目consumer搭建完毕!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐