一、關(guān)于ZooKeeper和Dubbo
1. ZooKeeper(動物園管理員)是什么
注冊中心負(fù)責(zé)服務(wù)地址的注冊與查找,相當(dāng)于目錄服務(wù),服務(wù)提供者和消費(fèi)者只在啟動時與注冊中心交互,注冊中心不轉(zhuǎn)發(fā)請求,壓力較小。使用dubbo-2.3.3以上版本,建議使用zookeeper注冊中心。
ZooKeeper是一個分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是Google的Chubby一個開源的實現(xiàn),它是集群的管理者,監(jiān)視著集群中各個節(jié)點(diǎn)的狀態(tài)根據(jù)節(jié)點(diǎn)提交的反饋進(jìn)行下一步合理操作。最終,將簡單易用的接口和性能高效、功能穩(wěn)定的系統(tǒng)提供給用戶。
2. Dubbo
這個項目是基于SOA架構(gòu)的,表現(xiàn)層和服務(wù)層在不同的系統(tǒng)中,而且還會調(diào)用一些其他系統(tǒng)的服務(wù),所以系統(tǒng)之間的通信時不可避免的。
那么如何進(jìn)行跨系統(tǒng)通信呢?
1、Webservice:效率不高基于soap協(xié)議。項目中不推薦使用。
2、使用restful形式的服務(wù):http+json。很多項目中應(yīng)用。如果服務(wù)太多,服務(wù)之間調(diào)用關(guān)系混亂,需要治療服務(wù)。
3、使用dubbo。使用rpc協(xié)議進(jìn)行遠(yuǎn)程調(diào)用,直接使用socket通信。傳輸效率高,并且可以統(tǒng)計出系統(tǒng)之間的調(diào)用關(guān)系、調(diào)用次數(shù)。
什么是dubbo?
隨著互聯(lián)網(wǎng)的發(fā)展,網(wǎng)站應(yīng)用的規(guī)模不斷擴(kuò)大,常規(guī)的垂直應(yīng)用架構(gòu)已無法應(yīng)對,分布式服務(wù)架構(gòu)以及流動計算架構(gòu)勢在必行,亟需一個治理系統(tǒng)確保架構(gòu)有條不紊的演進(jìn)。

單一應(yīng)用架構(gòu)
當(dāng)網(wǎng)站流量很小時,只需一個應(yīng)用,將所有功能都部署在一起,以減少部署節(jié)點(diǎn)和成本。
此時,用于簡化增刪改查工作量的 數(shù)據(jù)訪問框架(ORM) 是關(guān)鍵。垂直應(yīng)用架構(gòu)
當(dāng)訪問量逐漸增大,單一應(yīng)用增加機(jī)器帶來的加速度越來越小,將應(yīng)用拆成互不相干的幾個應(yīng)用,以提升效率。
此時,用于加速前端頁面開發(fā)的Web框架(MVC)是關(guān)鍵。分布式服務(wù)架構(gòu)
當(dāng)垂直應(yīng)用越來越多,應(yīng)用之間交互不可避免,將核心業(yè)務(wù)抽取出來,作為獨(dú)立的服務(wù),逐漸形成穩(wěn)定的服務(wù)中心,使前端應(yīng)用能更快速的響應(yīng)多變的市場需求。
此時,用于提高業(yè)務(wù)復(fù)用及整合的 分布式服務(wù)框架(RPC) 是關(guān)鍵。流動計算架構(gòu)
當(dāng)服務(wù)越來越多,容量的評估,小服務(wù)資源的浪費(fèi)等問題逐漸顯現(xiàn),此時需增加一個調(diào)度中心基于訪問壓力實時管理集群容量,提高集群利用率。
此時,用于提高機(jī)器利用率的 資源調(diào)度和治理中心(SOA) 是關(guān)鍵。
Dubbo就是資源調(diào)度和治理中心的管理工具。
其核心部分包含:
- 遠(yuǎn)程通訊: 提供對多種基于長連接的NIO框架抽象封裝,包括多種線程模型,序列化,以及“請求-響應(yīng)”模式的信息交換方式。
- 集群容錯: 提供基于接口方法的透明遠(yuǎn)程過程調(diào)用,包括多協(xié)議支持,以及軟負(fù)載均衡,失敗容錯,地址路由,動態(tài)配置等集群支持。
- 自動發(fā)現(xiàn): 基于注冊中心目錄服務(wù),使服務(wù)消費(fèi)方能動態(tài)的查找服務(wù)提供方,使地址透明,使服務(wù)提供方可以平滑增加或減少機(jī)器。
Dubbo的架構(gòu)

節(jié)點(diǎn)角色說明:
Provider: 暴露服務(wù)的服務(wù)提供方。
Consumer: 調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方。
Registry: 服務(wù)注冊與發(fā)現(xiàn)的注冊中心。
Monitor: 統(tǒng)計服務(wù)的調(diào)用次調(diào)和調(diào)用時間的監(jiān)控中心。
Container: 服務(wù)運(yùn)行容器。
調(diào)用關(guān)系說明:
- 服務(wù)容器負(fù)責(zé)啟動,加載,運(yùn)行服務(wù)提供者。
- 服務(wù)提供者在啟動時,向注冊中心注冊自己提供的服務(wù)。
- 服務(wù)消費(fèi)者在啟動時,向注冊中心訂閱自己所需的服務(wù)。
- 注冊中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。
- 服務(wù)消費(fèi)者,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺調(diào)用。
- 服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計調(diào)用次數(shù)和調(diào)用時間,定時每分鐘發(fā)送一次統(tǒng)計數(shù)據(jù)到監(jiān)控中心。
dubbo使用方法
Dubbo采用全Spring配置方式,透明化接入應(yīng)用,對應(yīng)用沒有任何API侵入,只需用Spring加載Dubbo的配置即可,Dubbo基于Spring的Schema擴(kuò)展進(jìn)行加載。
發(fā)布服務(wù):
<!-- 和本地服務(wù)一樣實現(xiàn)遠(yuǎn)程服務(wù) -->
<bean id="xxxService" class="com.xxx.XxxServiceImpl" />
<!-- 增加暴露遠(yuǎn)程服務(wù)配置 -->
<dubbo:service interface="com.xxx.XxxService" ref="xxxService" />
調(diào)用服務(wù):
<!-- 增加引用遠(yuǎn)程服務(wù)配置 -->
<dubbo:reference id="xxxService" interface="com.xxx.XxxService" />
<!-- 和本地服務(wù)一樣使用遠(yuǎn)程服務(wù) -->
<bean id="xxxAction" class="com.xxx.XxxAction">
<property name="xxxService" ref="xxxService" />
</bean>
二、搭建zookeeper服務(wù)注冊中心
1. zookeeper單機(jī)版
第一步:安裝jdk
最好先建一個專門存放上傳文件的文件夾:
命令 mkdir -p /app/tools-
上傳jdk的Linux版本到這個文件夾中,最好是1.7版本,和zookeeper比較搭,上次我用了1.8版本,有一些莫名其妙的bug。
上傳命令:我沒用命令,直接工具上傳的。
上傳界面 再新建一個文件夾,存放解壓后的文件,然后解壓到這個文件夾。
mkdir /usr/local/java
tar -zxvf jdk-1.7-40.tar.gz -C /usr/local/java/配置Linux的jdk環(huán)境變量
修改/etc/profile文件:vi /etc/profile
在尾部追加:
# java environment
export JAVA_HOME=/usr/java/jdk1.7.0_40
export CLASSPATH=.:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar
export PATH=$PATH:${JAVA_HOME}/bin
:wq保存退出

配置完成后刷新配置: source /etc/profile
-
測試是否成功: java -versionimage.png
有可能出現(xiàn)的bug :
-bash: /usr/jdk1.7.0_40/bin/java: /lib/ld-linux.so.2: bad ELF interpreter: 沒有那個文件或目錄
解決方式:
輸入命令 yum install ld-linux.so.2
第二步:解壓zookeeper安裝包
創(chuàng)建zookeeper解壓目錄:mkdir /usr/local/zookeeper
解壓:tar -zxvf zookeeper-3.4.8.tar.gz -C /usr/local/zookeeper/
第三步:將conf文件夾下zoo_sample.cfg復(fù)制一份,改名為zoo.cfg
命令:cp zoo_sample.cfg zoo.cfg
第四步:修改配置dataDir屬性,指定一個真實目錄
在zookeeper根目錄中,創(chuàng)建一個data文件夾和logs文件夾,然后修改conf文件夾中的zoo.cfg的dataDir屬性和dataLogDir屬性:
dataDir=/usr/local/zookeeper/zookeeper-3.4.8/data
dataLogDir=/usr/local/zookeeper/zookeeper-3.4.8/logs

第五步:啟動zookeeper并進(jìn)行測試
關(guān)閉防火墻:systemctl stop firewalld
禁止防火墻開機(jī)啟動:systemctl disable firewalld
啟動zookeeper:./zookeeper-3.4.8/bin/zkServer.sh start
查看狀態(tài):./zookeeper-3.4.8/bin/zkServer.sh status
客戶端連接測試以下::./zookeeper-3.4.8/bin/zkCli.sh -server 127.0.0.1:2181

進(jìn)去看一下就行了,一般也不會動它。
第六步,部署dubbo監(jiān)控中心
首先上傳tomcat,解壓tomcat;
上傳dubbo-admin-2.5.4.war包,放到tomcat的webapps文件夾下面;
啟動tomcat:./startup.sh start
訪問http://192.168.245.130:8080/dubbo-admin-2.5.4/,輸入用戶名root和密碼root,進(jìn)入監(jiān)控頁面,沒有服務(wù)的話,看不出啥效果,我這個已經(jīng)有服務(wù)了。


2.zookeeper注冊中心集群版
單機(jī)模式就不廢話太多了,開始集群。
第一步,將解壓好的zookeeper文件夾復(fù)制一份:cp -rf zookeeper-3.4.8/ zk1
第二步,修改zk1中的zoo.cfg文件,并在data文件夾下創(chuàng)建myid文件,寫入數(shù)字1
進(jìn)入data文件夾,創(chuàng)建myid:echo 1>>myid
修改zoo.cfg:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/zk1/data
clientPort=2181
dataLogDir=/usr/local/zookeeper/zk1/logs
server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883
這里需要注意,三個端口號是不同的,千萬不要有重復(fù),到時候leader選不出來。
- 3個端口的作用:
2181:對cline端提供服務(wù)
2881:集群內(nèi)機(jī)器通訊使用(Leader監(jiān)聽此端口)
3881:選舉leader使用
第三步,復(fù)制兩份zk1,分別為zk2、zk3,修改zk2(2182,2)和zk3(2183,3)的zoo.cfg文件和myid文件。
第四步,啟動zookeeper集群,查看狀態(tài)。
./zk1/bin/zkServer.sh start

為什么是zookeeper集群的基數(shù)三個呢?準(zhǔn)確的說為什么是單數(shù)呢?
容錯
所謂的zookeeper容錯是指,當(dāng)宕掉幾個zookeeper服務(wù)器之后,剩下的個數(shù)必須大于宕掉的個數(shù),也就是剩下的服務(wù)數(shù)必須大于n/2,zookeeper才可以繼續(xù)使用,無論奇偶數(shù)都可以選舉leader。5臺機(jī)器最多宕掉2臺,還可以繼續(xù)使用,因為剩下3臺大于5/2。
說為什么最好為奇數(shù)個,是在以最大容錯服務(wù)器個數(shù)的條件下,會節(jié)省資源,比如,最大容錯為2的情況下,對應(yīng)的zookeeper服務(wù)數(shù),奇數(shù)為5,而偶數(shù)為6,也就是6個zookeeper服務(wù)的情況下最多能宕掉2個服務(wù),所以從節(jié)約資源的角度看,沒必要部署6(偶數(shù))個zookeeper服務(wù)。防腦裂
一個zookeeper集群中,可以有多個follower、observer服務(wù)器,但是必需只能有一個leader服務(wù)器。
如果leader服務(wù)器掛掉了,剩下的服務(wù)器集群會通過半數(shù)以上投票選出一個新的leader服務(wù)器。
集群互不通訊情況:
一個集群3臺服務(wù)器,全部運(yùn)行正常,但是其中1臺裂開了,和另外2臺無法通訊。3臺機(jī)器里面2臺正常運(yùn)行過半票可以選出一個leader。
一個集群4臺服務(wù)器,全部運(yùn)行正常,但是其中2臺裂開了,和另外2臺無法通訊。4臺機(jī)器里面2臺正常工作沒有過半票以上達(dá)到3,無法選出leader正常運(yùn)行。
一個集群5臺服務(wù)器,全部運(yùn)行正常,但是其中2臺裂開了,和另外3臺無法通訊。5臺機(jī)器里面3臺正常運(yùn)行過半票可以選出一個leader。
一個集群6臺服務(wù)器,全部運(yùn)行正常,但是其中3臺裂開了,和另外3臺無法通訊。6臺機(jī)器里面3臺正常工作沒有過半票以上達(dá)到4,無法選出leader正常運(yùn)行。
四、服務(wù)的注冊和獲取
1. 先不管用什么服務(wù)測試,我們先使用mybatis-generator工具類自動生成pojo類和mapper類
這是mybatis-generator工具類的項目結(jié)構(gòu),與這個項目無關(guān)。

需要的jar包圖中有。
GeneratorSqlmap類:
package generator;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class GeneratorSqlmap {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
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>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自動生成的注釋 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--數(shù)據(jù)庫連接的信息:驅(qū)動類、連接地址、用戶名、密碼 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/yysc" userId="root"
password="root">
</jdbcConnection>
<!-- 默認(rèn)false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,為 true時把JDBC DECIMAL 和
NUMERIC 類型解析為java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO類的位置 -->
<javaModelGenerator targetPackage="com.yzy.yysc.manager.pojo"
targetProject="D:\IDEAWorkPlace\yysc\yy-parent\generatorSqlmapCustom\src\main\java">
<!-- enableSubPackages:是否讓schema作為包的后綴 -->
<property name="enableSubPackages" value="false" />
<!-- 從數(shù)據(jù)庫返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.yzy.yysc.manager.mapper"
targetProject="D:\IDEAWorkPlace\yysc\yy-parent\generatorSqlmapCustom\src\main\java">
<!-- enableSubPackages:是否讓schema作為包的后綴 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.yzy.yysc.manager.mapper"
targetProject="D:\IDEAWorkPlace\yysc\yy-parent\generatorSqlmapCustom\src\main\java">
<!-- enableSubPackages:是否讓schema作為包的后綴 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定數(shù)據(jù)庫表 -->
<table schema="" tableName="tb_content"></table>
<table schema="" tableName="tb_content_category"></table>
<table schema="" tableName="tb_item"></table>
<table schema="" tableName="tb_item_cat"></table>
<table schema="" tableName="tb_item_desc"></table>
<table schema="" tableName="tb_item_param"></table>
<table schema="" tableName="tb_item_param_item"></table>
<table schema="" tableName="tb_order"></table>
<table schema="" tableName="tb_order_item"></table>
<table schema="" tableName="tb_order_shipping"></table>
<table schema="" tableName="tb_user"></table>
</context>
</generatorConfiguration>
配置好以后,直接運(yùn)行GeneratorSqlmap類中的main方法就好了,會自動在src\main\java下生成。
碰到的bug說一下:
bug1:一直報一個找不到generatorConfig.xml文件的錯誤。
解決:修改working directory,讓他和Output path相同


bug2:運(yùn)行良好卻不生成文件
解決:targetProject的位置改為絕對位置

以前也用過,不過用的maven插件,想用插件的可以搜一下用法。
這些都是它自動生成我復(fù)制過來的:


2. 就決定測試獲取商品的服務(wù)好了
商品是這個表:

商品pojo類,因為涉及到跨系統(tǒng),必須實現(xiàn)序列化接口,它完全是根據(jù)數(shù)據(jù)庫表的字段自動生成的。
我畫蛇添足的把外鍵改成了該外鍵對應(yīng)的類,現(xiàn)在一想這是mybatis不是hibernate,沒有必要這樣做,完全可以使用多表聯(lián)查。這里改了那么待會mapper.xml文件中也要改。
TbItem類:
package com.yzy.yysc.manager.pojo;
import com.yzy.yysc.common.vo.PageVo;
import java.io.Serializable;
import java.sql.Timestamp;
public class TbItem extends PageVo implements Serializable{
private Long id;
private String title;
private String sellPoint;
private Long price;
private Integer num;
private String barcode;
private String image;
//我改動的地方
private TbItemCat itemCat;
private Byte status;
private Timestamp created;
private Timestamp updated;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title == null ? null : title.trim();
}
public String getSellPoint() {
return sellPoint;
}
public void setSellPoint(String sellPoint) {
this.sellPoint = sellPoint == null ? null : sellPoint.trim();
}
public Long getPrice() {
return price;
}
public void setPrice(Long price) {
this.price = price;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode == null ? null : barcode.trim();
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image == null ? null : image.trim();
}
public TbItemCat getItemCat() {
return itemCat;
}
public void setItemCat(TbItemCat itemCat) {
this.itemCat = itemCat;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Timestamp getCreated() {
return created;
}
public void setCreated(Timestamp created) {
this.created = created;
}
public Timestamp getUpdated() {
return updated;
}
public void setUpdated(Timestamp updated) {
this.updated = updated;
}
}
上面的pojo類我繼承了一個PageVo類,這個PageVo類是用來接收前臺的分頁數(shù)據(jù)的,我放在yysc-common項目中:
package com.yzy.yysc.common.vo;
import java.io.Serializable;
public class PageVo implements Serializable{
//封裝當(dāng)前頁碼
private Integer page;
//封裝每頁數(shù)據(jù)量
private Integer limit;
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
}
我又新建了一個yysc-manager-pojo項目中新建了一個TbItemVo類,并讓它繼承了TbItem類,這個類是用來封裝一些前臺查詢條件的,如關(guān)鍵字,日期等,我們與前臺的業(yè)務(wù)交互時,一般使用這個類。
package com.yzy.yysc.manager.vo;
import com.yzy.yysc.manager.pojo.TbItem;
import org.apache.commons.lang3.StringUtils;
import java.sql.Timestamp;
public class TbItemVo extends TbItem {
private String keywords;
private Timestamp createTime;
private Timestamp endTime;
public String getKeywords() {
return keywords;
}
public void setKeywords(String keywords) {
this.keywords = keywords;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
if (StringUtils.isNotBlank(createTime)) {
Timestamp ts = new Timestamp(System.currentTimeMillis());
try {
ts = Timestamp.valueOf(createTime + " 00:00:00");
endTime = Timestamp.valueOf(createTime + " 23:59:59");
System.out.println(ts);
} catch (Exception e) {
e.printStackTrace();
}
this.createTime = ts;
}else{
this.createTime = null;
}
}
public Timestamp getEndTime() {
return endTime;
}
}
mybatis-generator還自動生成了一個TbItemExample類,與數(shù)據(jù)庫做curd操作的時候就會使用它。
這個類太長了,就不上代碼了,但是我們后面需要在它里面添加這樣一個方法,方便模糊查詢。
public Criteria andOrItemsLike(String value){
value="%"+value+"%";
addCriterion("(title like \""+value+"\" or sell_point like \""+value+"\")");
return (Criteria) this;
}
商品類的mapper接口和mapper.xml文件,都是自動生成的,我沒有添加任何方法,沒有必要,因為與數(shù)據(jù)庫交互都是使用TbItemExample類來完成。
TbItemMapper接口:
package com.yzy.yysc.manager.mapper;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.pojo.TbItemExample;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TbItemMapper{
int countByExample(TbItemExample example);
int deleteByExample(TbItemExample example);
int deleteByPrimaryKey(Long id);
int insert(TbItem record);
int insertSelective(TbItem record);
List<TbItem> selectByExample(TbItemExample example);
TbItem selectByPrimaryKey(Long id);
int updateByExampleSelective(@Param("record") TbItem record, @Param("example") TbItemExample example);
int updateByExample(@Param("record") TbItem record, @Param("example") TbItemExample example);
int updateByPrimaryKeySelective(TbItem record);
int updateByPrimaryKey(TbItem record);
}
商品的mapper.xml文件也很長,我就不全上了,只上一個做了改動的地方,最后面。
TbItemMapper.xml:
<mapper namespace="com.yzy.yysc.manager.mapper.TbItemMapper" >
<resultMap id="BaseResultMap" type="com.yzy.yysc.manager.pojo.TbItem">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="sell_point" property="sellPoint" jdbcType="VARCHAR"/>
<result column="price" property="price" jdbcType="BIGINT"/>
<result column="num" property="num" jdbcType="INTEGER"/>
<result column="barcode" property="barcode" jdbcType="VARCHAR"/>
<result column="image" property="image" jdbcType="VARCHAR"/>
<result column="status" property="status" jdbcType="TINYINT"/>
<result column="created" property="created" jdbcType="TIMESTAMP"/>
<result column="updated" property="updated" jdbcType="TIMESTAMP"/>
<association property="itemCat" column="cid" javaType="com.yzy.yysc.manager.pojo.TbItemCat"
select="queryCatById"/>
</resultMap>
<select id="queryCatById" resultType="com.yzy.yysc.manager.pojo.TbItemCat">
SELECT * from tb_item_cat where id=#{cid}
</select>
商品的interface接口
package com.yzy.yysc.manager.service;
import com.yzy.yysc.common.model.DataGirdModel;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.vo.TbItemVo;
public interface ItemService {
public DataGirdModel<TbItemVo> getAllItems(TbItemVo itemVo);
}
這里要打個叉,因為我前段用的layui展示數(shù)據(jù),所以我們還需要建一個DataGirdModel類把數(shù)據(jù)封裝成layui框架可以就收的格式。
我們DataGirdModel類放在yysc-common項目中,記住也要實現(xiàn)序列化接口。
package com.yzy.yysc.common.model;
import java.io.Serializable;
import java.util.List;
public class DataGirdModel<T> implements Serializable {
private Integer code=0;
private String msg;
private Integer count;
private List<T> data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
商品的interface接口實現(xiàn)類:
package com.yzy.yysc.manager.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.yzy.yysc.manager.mapper.TbItemMapper;
import com.yzy.yysc.common.model.DataGirdModel;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.pojo.TbItemExample;
import com.yzy.yysc.manager.service.ItemService;
import com.yzy.yysc.manager.vo.TbItemVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private TbItemMapper itemMapper;
@Override
public DataGirdModel<TbItemVo> getAllItems(TbItemVo itemVo) {
//分頁查詢
Page page= PageHelper.startPage(itemVo.getPage(),itemVo.getLimit());
DataGirdModel<TbItemVo> dgm=new DataGirdModel<>();
TbItemExample example = new TbItemExample();
TbItemExample.Criteria criteria= example.createCriteria();
//模糊查詢
if(StringUtils.isNotBlank(itemVo.getKeywords())){
criteria.andOrItemsLike(itemVo.getKeywords());
}
//日期條件查詢
if(itemVo.getCreateTime() != null){
criteria.andCreatedBetween(itemVo.getCreateTime(),itemVo.getEndTime());
}
List<TbItem> demos = itemMapper.selectByExample(example);
List<TbItemVo> tivolist=new ArrayList<>();
System.out.println(demos);
for(TbItem item:demos){
TbItemVo tivo=new TbItemVo();
BeanUtils.copyProperties(item,tivo);
tivolist.add(tivo);
}
//注意這里,設(shè)置總條數(shù)一定要放在mapper查詢的后面,不然就是0
dgm.setCount(new Integer((int) page.getTotal()));
dgm.setData(tivolist);
return dgm;
}
}
下面是web層ItemController類
package com.yzy.yysc.manager.controller;
import com.yzy.yysc.common.model.DataGirdModel;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.pojo.TbItemCat;
import com.yzy.yysc.manager.service.ItemCatService;
import com.yzy.yysc.manager.service.ItemService;
import com.yzy.yysc.manager.vo.TbItemVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
@Autowired
private ItemCatService itemCatService;
@RequestMapping("/list")
public String list(){
return "item/list";
}
@RequestMapping("/new")
public String newItem(Model model){
return "item/add";
}
@RequestMapping("/pageData")
@ResponseBody
public DataGirdModel<TbItemVo> getItemByKeywords(TbItemVo itemVo){
System.out.println(itemVo.getKeywords());
System.out.println(itemService.getAllItems(itemVo).getCount());
return itemService.getAllItems(itemVo);
}
}
接下來配置service層和web層的spring配置文件,進(jìn)行服務(wù)的發(fā)布和獲取,測試系統(tǒng)間的通信是否順暢
- service層發(fā)布服務(wù):
注意添加文檔約束:
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
發(fā)布服務(wù):
<!-- 發(fā)布dubbo服務(wù) -->
<!-- 提供方應(yīng)用信息,用于計算依賴關(guān)系 -->
<dubbo:application name="yy-manager" />
<!-- 注冊中心的地址 -->
<dubbo:registry protocol="zookeeper"
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />
<!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 聲明需要暴露的服務(wù)接口 -->
<dubbo:service interface="com.yzy.yysc.manager.service.ItemService"
ref="itemServiceImpl" timeout="300000"/>

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- 將業(yè)務(wù)對象,那入spring容器 -->
<context:component-scan base-package="com.yzy.yysc.manager.service.impl">
</context:component-scan>
<!-- 聲明事務(wù)管理器對象 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 聲明事務(wù)切面 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
<tx:method name="load*" propagation="REQUIRED" read-only="true"/>
<tx:method name="user*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 進(jìn)行aop配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.yzy.yysc.manager.service.impl.*.*(..))" id="pc"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
<!-- 發(fā)布dubbo服務(wù) -->
<!-- 提供方應(yīng)用信息,用于計算依賴關(guān)系 -->
<dubbo:application name="yy-manager" />
<!-- 注冊中心的地址 -->
<dubbo:registry protocol="zookeeper"
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />
<!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 聲明需要暴露的服務(wù)接口 -->
<dubbo:service interface="com.yzy.yysc.manager.service.ItemService"
ref="itemServiceImpl" timeout="300000"/>
</beans>
- web層獲取服務(wù)
同樣注意添加文檔約束:
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
引用服務(wù):
<!-- 引用dubbo服務(wù) -->
<dubbo:application name="yy-manager-web"/>
<dubbo:registry protocol="zookeeper"
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />
<dubbo:reference
interface="com.yzy.yysc.manager.service.ItemService" id="itemService" />

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<context:component-scan base-package="com.yzy.yysc.manager.controller"></context:component-scan>
<!--
springmvc的注解驅(qū)動
-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置二進(jìn)制流解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!– 設(shè)置允許上傳的文件最大的尺寸 –>
<property name="maxUploadSize" value="10240000"></property>
<!– 設(shè)置文件上傳過程中,中文的編碼 –>
<property name="defaultEncoding" value="UTF-8"></property>
<!– 設(shè)置文件上傳的保存目錄 –>
<property name="uploadTempDir" value="/upload"></property>
</bean>-->
<mvc:resources location="/WEB-INF/js/" mapping="/js/**"/>
<mvc:resources location="/WEB-INF/css/" mapping="/css/**"/>
<mvc:resources location="/WEB-INF/images/" mapping="/images/**"/>
<mvc:resources location="/WEB-INF/zTree/" mapping="/zTree/**"/>
<mvc:resources location="/WEB-INF/layui/" mapping="/layui/**"/>
<mvc:resources location="/WEB-INF/jsp/" mapping="/jsp/**"/>
<!-- 配置視圖解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 引用dubbo服務(wù) -->
<dubbo:application name="yy-manager-web"/>
<dubbo:registry protocol="zookeeper"
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />
<dubbo:reference
interface="com.yzy.yysc.manager.service.ItemService" id="itemService" />
</beans>
這是前端的結(jié)構(gòu),記住導(dǎo)入layui和zTree樹

商品類的列表頁面和js文件

list.jsp
<%--
Created by IntelliJ IDEA.
User: yangoctopus
Date: 2018/10/11
Time: 21:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>商品管理</title>
<link rel="stylesheet" href="/layui/css/layui.css">
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/jquery.form.min.js"></script>
<script type="text/javascript" src="/layui/layui.js"></script>
<script type="text/javascript" src="/js/item/itemManager.js"></script>
</head>
<body>
<div class="layui-card">
<div class="layui-card-body">
<form class="layui-form" id="queryForm" method="post">
<div class="layui-form-item">
<div class="layui-col-md11">
<label class="layui-form-label" style="width: auto">模糊查詢</label>
<div class="layui-input-inline">
<input style="width: 250px" type="text" name="keywords" id="key" class="layui-input" placeholder="請輸入商品標(biāo)題或賣點(diǎn)">
</div>
<div class="layui-inline" style="margin-left: 50px">
<label class="layui-form-label" style="width: auto">入庫日期</label>
<div class="layui-input-inline" style="width: 130px;">
<input type="text" name="createTime" id="createTime" class="layui-input" autocomplete="off">
</div>
</div>
<div class="layui-btn-group">
<button type="button" lay-submit class="layui-btn" lay-filter="sub" lay-submit="" id="queryPage" >查詢</button>
<button type="button" class="layui-btn" id="new"><i class="layui-icon"></i>新建</button>
</div>
</div>
</div>
</form>
<div class="contain-img-table"><table id="dataTable" lay-filter="dataTable"></table></div>
</div>
</div>
</body>
<script type="text/html" id="barDemo">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">查看</a>
<a class="layui-btn layui-btn-xs" lay-event="edit">編輯</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
</script>
</html>
itemManager.js文件
/*
通過加載事件,發(fā)送ajax請求,加載商品列表
*/
$(document).ready(function(){
layui.use(['laydate', 'table', 'layer', 'form','jquery'], function () {
var laydate = layui.laydate, table = layui.table, layer = layui.layer, form = layui.form,$= layui.jquery;
form.render();
//日期
laydate.render({
elem: '#createTime',
type: 'date'
});
var dataTable = table.render({
elem: '#dataTable',
url: '/item/pageData', //數(shù)據(jù)接口
page: true, //開啟分頁
method:'post',
cols: [[ //表頭
{field: 'id', align: 'center',width : 80, title: '商品id'},
{field: 'title', align: 'center',width : 120, title: '商品標(biāo)題'},
{field: 'sellPoint', align: 'center',width : 120, title: '商品賣點(diǎn)'},
{field: 'price', align: 'center', width : 100,title: '商品價格'},
{field: 'num', align: 'center',width : 80, title: '庫存'},
{align: 'center',width : 100, title: '所屬類目',templet:function(d){
return d.itemCat.name;
}},
{field: 'status', align: 'center', width : 90,title: '商品狀態(tài)',templet: function (d) {
if(d.status===1){
return '正常';
}else if(d.status===2){
return '下架';
}else{
return '刪除';
}
}},
{field: 'created', align: 'center',width : 120, title: '入庫時間',templet:function(d){
return createTime(d.created);
}},
{field: 'updated', align: 'center',width : 120, title: '最后更新時間',templet:function(d){
return createTime(d.updated);
}},
{fixed: 'right',title: '操作', align: 'center',width : 160, toolbar:'#barDemo'}
]]
});
window.reloadTable = function() {
var fields = {};
$.each($('#queryForm').serializeArray(), function(i, field){
fields[field.name] = field.value;
});
dataTable.reload({
where: fields
});
//$("#detai").click(tableOn())
}
window.tableOn = function(){
table.on('tool(dataTable)', function(obj){ //注:tool是工具條事件名,test是table原始容器的屬性 lay-filter="對應(yīng)的值"
var data = obj.data; //獲得當(dāng)前行數(shù)據(jù)
var layEvent = obj.event; //獲得 lay-event 對應(yīng)的值(也可以是表頭的 event 參數(shù)對應(yīng)的值)
var tr = obj.tr; //獲得當(dāng)前行 tr 的DOM對象
if(layEvent === 'edit'){ //編輯
} else if(layEvent === 'del'){ //刪除
layer.confirm('真的刪除行么', function(index){
$.ajax({
url: '/traceMushrootDibgrow/delete',
data: {id: data.id},
async: false,
success: function (data, status, xhr) {
if (data.success) {
layer.close(index);
}
reloadTable();
}
});
});
} else if(layEvent === 'detail'){ //查看
var img = "<div><img src='/images/11.jpg' height='330'/><br/>data.barcode</div>"
layer.open({
type: 1,
title: '商品圖片',
offset: ['10px', '230px'],
area:['600px', '400px'],
content: img //注意,如果str是object,那么需要字符拼接。
});
//同步更新緩存對應(yīng)的值
// obj.update({username: '123',title: 'xxx'});
}
});
}
$('#new').click(function () {
$.ajax({
url: '/item/new',
async: false,
success: function (data, status, xhr) {
layer.open({
type: 1,
title:"商品入庫",
offset: ['10px', '50px'],
area: ['1000px', '500px'],
content: data //注意,如果str是object,那么需要字符拼接。
});
}
});
});
$('#queryPage').click(reloadTable);
// form.render('table','data');
tableOn()
});
});
function createTime(v){
var date = new Date(v);
var y = date.getFullYear();
var m = date.getMonth()+1;
m = m<10?'0'+m:m;
var d = date.getDate();
d = d<10?("0"+d):d;
var h = date.getHours();
h = h<10?("0"+h):h;
var M = date.getMinutes();
M = M<10?("0"+M):M;
var str = y+"-"+m+"-"+d+" "+h+":"+M;
return str;
}
最后,順序啟動項目進(jìn)行測試。
一、啟動yysc-manager-service項目,進(jìn)行服務(wù)注冊。
二、啟動yysc-manager-web項目,進(jìn)行服務(wù)獲取。
效果:





