Dubbo搭建maven项目,并实现Dubbo分布式服务管理
一、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>共 "+totalResult+" 条</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>第 " + currentPage + " 页</a></li>\n"); sb.append(" <li><a>共 " + totalPage + " 页</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&useUnicode=true&characterEncoding=utf-8&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&useUnicode=true&characterEncoding=utf-8&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搭建完毕!
- dubbo-如何搭建maven项目,并实现Dubbo分布式服务管理
- 搭建maven项目,并实现Dubbo分布式服务管理
- Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出
- Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出现的问题
- BLUENESSG 早一日受苦、早一日解决、早一日浴火重生 Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出现的问题
- Maven-maven多模块项目搭建+Dubbo分布式服务框架 budong
- idea下用maven创建并搭建dubbox微服务环境项目(四)——整理spring+mybaties(注解)
- idea下用maven创建并搭建dubbox微服务环境项目(五)—综合整理及总结
- 在Spring项目中集成使用dubbo实现分布式服务
- Maven多模块+dubbo+zookeeper分布式架构搭建SSM项目
- idea下用maven创建并搭建dubbox微服务环境项目(三)——开发consume
- maven项目:小型用户管理系统的搭建(实现增删改查)
- Springboot整合dubbo构建maven多模块项目(三) - 把server分为api(服务接口定义)和server(服务实现)两个子module
- idea下用maven创建并搭建dubbox微服务环境项目(二)——开发provider
- [置顶] dubbo 分布式框架,git版本控制,ida使用方法,maven项目管理
- Dubbo分布式服务+Springmvc容器+Maven项目整合,分布式,kakfka消息中间件整合
- idea下用maven创建并搭建dubbox微服务环境项目(一)
- 从0开始搭建:分布式服务框架-Maven多模块+Dubbo+Zookeeper++SpringMVC+mybatis自动生成
- IDEA下搭建maven管理的DUBBO项目,Zookeeper作为注册中心
- eclipse maven 实现web项目管理