JoyLau's Blog

JoyLau 的技术学习与思考

问题

  • 用jackson 作为json转换器的时候,如果传入的json的key 比接收对象多的话,就会报错

解决

先看下SpringMVC原来的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>

这里的json转换器配置的是:org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

我们进入到这个类中发现,这个类是继承的 AbstractJackson2HttpMessageConverter

AbstractJackson2HttpMessageConverter 继承的是 AbstractHttpMessageConverter<Object>
找到这个包下面 有一个类 GsonHttpMessageConverter 同样继承的 AbstractHttpMessageConverter<Object>
OK,就是他了

1
2
3
4
5
6
7
8
9
10
11
12
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>

这样,参数就随便你整吧,多点少点杜无所谓,完全匹配不上就返回个{}给你

来看下fastjson

fastjson下面有这个一个 package : com.alibaba.fastjson.support.spring

根据字面意思可知,这里是对spring的支持

找到下面这个class FastJsonHttpMessageConverter

1
public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object>

OK,这个类同样也是继承了 AbstractHttpMessageConverter

只要把这个类注入进去就可以了

SpringBoot使用FastJSON解析数据

  • 第一种继承WebMvcConfigurerAdapter,重写configureMessageConverters方法:
1
2
3
4
5
6
7
8
9
10
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
FastJsonHttpMessageConverter converter=new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig= new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
converter.setFastJsonConfig(fastJsonConfig);
converters.add(converter);

}
  • 第二种方式bean注入HttpMessageConverters:
1
2
3
4
5
6
7
8
9
@Bean  
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}

2017年普通高等学校招生全国统一考试

程序员的高考试卷(A卷) `考生类别:码农`

1、程序员A:借我1000元吧。程序员B:给你凑个整数。程序员B借给程序员A多少钱?()

   A. 1000元
   B. 1024元
   C. 1111元

2、程序员A:嘿 //是什么意思啊?程序员B:嘿.程序员A:呃 我问你//是什么意思?程序员B:问吧.程序员A:我刚才不是问了么?程序员B:啊?程序员A到底问了程序员B什么问题?()

   A. 嘿
   B. 呃 我问你
   C. //是什么意思

3、为什么程序员分不清万圣节和圣诞节?()

   A. 因为 31 OCT == 25 DEC
   B. 程序员只有加班/不加班,不过节
   C. 程序员没有女朋友,不过节

4、程序员最怕弹出的窗口是()

   A.   选项A

   B.   选项B

   C.   选项C

5、程序员:哎,太累了日子没法过了,怎么才能换行啊?()

   A. 打回车
   B. 不换行,日子不过了
   C. 除了敲代码,都不会,换行还是敲代码啊

6、程序员会给自己的孩子起什么名字?()

   A. 依依、灵灵、依灵、灵依、依初
   B. Ctrl、Alt 、Delete
   C. 程序员怎么会有女朋友?

7、如何快速挣到一百万?()

   A. while
   B. 买彩票
   C. 当乞丐

8、程序员下班前给老婆打电话:老婆,晚饭我带回来吃,你说买些啥?老婆:买1斤包子吧,如果遇到卖西瓜的,就买一个。程序员买包子时,看到旁边有人在卖西瓜。那么,程序员带了什么晚饭回家?()

   A. 1斤包子
   B. 1个包子
   C. 1个西瓜

9、我GET不到你的笑点,怎么办?()

   A. 智商不在一条线
   B. 太矮了,踩凳子上
   C. 用Post试试

10、为什么吸烟的程序员不在乎香烟盒上的那个警告?()

   A. 字太小
   B. 程序员眼中只有程序
   C. 不关心Warning,只关心Error

11、一对程序员恋人面对面坐着,他们在做什么?()

   A. 面向对象编程
   B. 喝咖啡
   C. 抱怨产品经理

12、老板:小程,下班前新版本一定要上线!小程:好的。第二天,老板上班,问小程:新版本怎么还没上线? 小程怎么回答的?()

   A. 版本出问题了
   B. 版本上线前需求又改了
   C. 我还没下班呢

![Title](//s3.joylau.cn:9000/blog/gaokao-title.jpg)

2017年普通高等学校招生全国统一考试

程序员的高考试卷(B卷) `考生类别:码神`

1、以下哪个概念和公孙龙的《指物论》中的“指”字含义相近?()

   A. 变量
   B. 数组
   C. 对象
   D. 指针

2、蔺相如,司马相如;魏无忌,长孙无忌。下列哪一组对应关系与此类似( )

   A. PHP,Python
   B. JSP,servlet
   C. java,java script
   D. C,C++

3、秦始皇吞并六国采用了以下哪种算法思想?( )

   A. 递归
   B. 分治
   C. 迭代
   D. 模拟

4、雅典王子忒修斯勇闯克里特岛斩杀米诺牛的时候采用了以下哪种算法?( )

   A. 动态规划
   B. 穷举
   C. 记忆化搜索
   D. Dijkstra算法

5、众里寻他千百度,蓦然回首,那人却在灯火阑珊处(辛弃疾《青玉案》)。所体现的算法是:( )

   A. 贪心
   B. 回溯
   C. 穷举
   D. 分治

6、《公孙龙子》记载:“齐王之谓尹文曰:‘寡人甚好士,以齐国无士,何也?’尹文曰:‘愿闻大王之所谓士者。’齐王无以应。”这说明了齐王:( )

   A. 昏庸无道
   B. 是个结巴
   C. 不会下定义
   D. 不会定义自己的需求

7、惠施曾提出过“卵有毛”的命题,以下哪一项是导致这个错误命题的原因:( )

   A. 混淆了命名空间
   B. 引入了错误的包
   C. 衍生类未重载
   D. 调用了危险的指针

8、下面哪种面向对象的方法可以让你变得富有?( )

   A. 继承
   B. 封装
   C. 多态
   D. 抽象

那么你能答对几题呢? 下期发布标准答案 滑稽

先来一张集合的

java-skill-tree1

Java核心技术总结

java-skill-tree2

J2EE技术总结

java-skill-tree3

工作学习总结

java-skill-tree4

大数据相关技术总结

java-skill-tree5

来看看Java工程师技能表

java-skill-tree6
java-skill-tree8
java-skill-tree9

恐怖的Linux大法

java-skill-tree7

Im Back

今天被问了一道题,是这样的:

求解:一筐鸡蛋:
1个1个拿,正好拿完
2个2个拿,还剩1个
3个3个拿,正好拿完
4个4个拿,还剩1个
5个5个拿,还差1个
6个6个拿,还剩3个
7个7个拿,正好拿完
8个8个拿,还剩1个
9个9个拿,正好拿完
问筐里最少有多少鸡蛋

能算出这道题的智商不一般!求答案?有高手没,算算吧!

”5个5个拿,是还差1个“,也就是还剩下4个,这是这个题目的一个小陷阱…

我第一反应想到的是这个数一定是63的倍数,但是后来就没有什么想法了。

再后来,我想到了一个残暴的方法,穷举法

1
2
3
4
5
6
7
8
9
10
int i = 1;
while (true) {
System.out.println(i);
if (i % 2 == 1 && i % 3 == 0 && i % 4 == 1 && i % 5 == 4 && i % 6 == 3 && i % 7 == 0
&& i % 8 == 1 && i % 9 == 0) {
System.out.println("鸡蛋数=" + i);
break;
}
i++;
}

执行后正确答案是1449;

能被7整除,能被9整除,所以肯定是63的倍数
如果利用63的倍数来做写的话:

1
2
3
4
5
6
7
8
9
int i = 1;
while (true) {
int num = 63 *i;
if (num%5==4&&num%6==3&&num%8==1) {
System.out.println("鸡蛋数=" + num);
break;
}
i++;
}

答案依旧是1449,稍微显得动了点头脑,但还是穷举法,有什么高大上的解法么???在下默默献上膝盖!

IntelliJIDEA-Plugins

说明

我现在用的这个插件时ECTranslation,是用于做中英文翻译的,可以在看文档和注释的是方便的使用,然而近期变得不好用了

  • 翻译的内容有时能出来,有时出不来,有时甚至没有反应
  • 查看了该款插件的源代码,发现是调用的有道翻译的API接口,而且在代码里写死了APIkey和KeyFrom
  • 调用了有道的API,加上上面作者提供的Key,再传入翻译的文本内容,发现返回值居然是请求次数过多,被封禁了…..
  • 明白了,很多使用这个插件的开发者都是用的作者提供的默认Key,默认情况下1小时请求的限制次数是1000次
  • 肯定是次数超了
  • 但是他的配置信息是写在代码里的,能配置到IDEA的面板上供使用者自己配置就好了
  • 于是我有了自己动手的想法

开始项目

第一步创建IDEA插件项目:
IntelliJIDEA-Build
第二步目录结构如下图所示:
IntelliJIDEA-Folder

项目配置

plugin.xml

看代码,相信能看懂的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<idea-plugin>
<id>cn.joylau.plugins.translation</id>
<name>joylau-translation</name>
<version>1.0</version>
<vendor email="2587038142.liu@gmail" url="http://www.joylau.cn">JoyLau</vendor>

<description><![CDATA[
Plugin for translate English to Chinese.<br>
<li>1. Choose the word you want translate.</li>
<li>2. Press Ctrl + NUMPAD0.</li>
<li>3. Fork ECTranslation Change ApiKey and KeyFrom</li>

]]></description>

<change-notes><![CDATA[
<li>Change ApiKey and KeyFrom for myself</li>
<li>Change KeyMap to Ctrl + NumPad 0</li>
]]>
</change-notes>

<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
<idea-version since-build="141.0"/>

<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->

<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
</extensions>

<actions>
<!-- Add your actions here -->
<action id="ECTranslation" class="cn.joylau.plugins.translation.ECTranslation" text="Translate">
<add-to-group group-id="EditMenu" anchor="first"/>
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
<keyboard-shortcut keymap="$default" first-keystroke="ctrl NUMPAD0"/>
</action>
</actions>

</idea-plugin>

只有一个action ,调用的类是ECTranslation,快捷键设置的ctrl + NumPad 0

最后

代码都是人家的,我就没好意思往IDEA的仓库里上传了…

如果你想使用这个插件: 点击查看点击下载

更新(2023-08-02)

最近发现有道 API 的接口不能用了,无法返回翻译数据,于是重新修改了下插件,发布最新版本

地址: https://github.com/JoyLau/joylau-translation/releases/tag/V2

目前有个问题就是,现在的有道 API 是收费的,刚开始注册送 50 元,不过很快会用完的, 到时再换其他的翻译 API 吧

Redis-Master&Slave

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
################################# REPLICATION #################################

# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
# 1) Redis replication is asynchronous, but you can configure a master to
# stop accepting writes if it appears to be not connected with at least
# a given number of slaves.
# 2) Redis slaves are able to perform a partial resynchronization with the
# master if the replication link is lost for a relatively small amount of
# time. You may want to configure the replication backlog size (see the next
# sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
# network partition slaves automatically try to reconnect to masters
# and resynchronize with them.
#
slaveof xx.xx.xx.xx 6379

# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
#
masterauth xx

# When a slave loses its connection with the master, or when the replication
# is still in progress, the slave can act in two different ways:
#
# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will
# still reply to client requests, possibly with out of date data, or the
# data set may just be empty if this is the first synchronization.
#
# 2) if slave-serve-stale-data is set to 'no' the slave will reply with
# an error "SYNC with master in progress" to all the kind of commands
# but to INFO and SLAVEOF.
#
slave-serve-stale-data yes

# You can configure a slave instance to accept writes or not. Writing against
# a slave instance may be useful to store some ephemeral data (because data
# written on a slave will be easily deleted after resync with the master) but
# may also cause problems if clients are writing to it because of a
# misconfiguration.
#
# Since Redis 2.6 by default slaves are read-only.
#
# Note: read only slaves are not designed to be exposed to untrusted clients
# on the internet. It's just a protection layer against misuse of the instance.
# Still a read only slave exports by default all the administrative commands
# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve
# security of read only slaves using 'rename-command' to shadow all the
# administrative / dangerous commands.
slave-read-only no

参数解释

  • slaveof : Slave库配置Master的ip地址和端口号
  • masterauth :如果Master配置了密码,那么这里设置密码
  • slave-serve-stale-data : 如果Master宕机了,Salve是否继续提供服务
  • slave-read-only : Slave 是否是只读模式,默认为是

部分配置项解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
daemonize yes #是否以后台进程运行,默认为no 
pidfile /var/run/redis.pid #如以后台进程运行,则需指定一个pid,默认为/var/run/redis.pid
bind 127.0.0.1 #绑定主机IP,默认值为127.0.0.1(注释)
port 6379 #监听端口,默认为6379
timeout 300 #超时时间,默认为300(秒)
loglevel notice #日志记slave-serve-stale-data yes:在master服务器挂掉或者同步失败时,从服务器是否继续提供服务。录等级,有4个可选值,debug,verbose(默认值),notice,warning
logfile /var/log/redis.log #日志记录方式,默认值为stdout
databases 16 #可用数据库数,默认值为16,默认数据库为0
save 900 1 #900秒(15分钟)内至少有1个key被改变
save 300 10 #300秒(5分钟)内至少有300个key被改变
save 60 10000 #60秒内至少有10000个key被改变
rdbcompression yes #存储至本地数据库时是否压缩数据,默认为yes
dbfilename dump.rdb #本地数据库文件名,默认值为dump.rdb
dir ./ #本地数据库存放路径,默认值为 ./

slaveof 10.0.0.12 6379 #当本机为从服务时,设置主服务的IP及端口(注释)
masterauth elain #当本机为从服务时,设置主服务的连接密码(注释)
slave-serve-stale-data yes #在master服务器挂掉或者同步失败时,从服务器是否继续提供服务。
requirepass elain #连接密码(注释)

maxclients 128 #最大客户端连接数,默认不限制(注释)
maxmemory #设置最大内存,达到最大内存设置后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理后,任到达最大内存设置,将无法再进行写入操作。(注释)
appendonly no #是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认值为no
appendfilename appendonly.aof #更新日志文件名,默认值为appendonly.aof(注释)
appendfsync everysec #更新日志条件,共有3个可选值。no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次(默认值)。

really-use-vm yes
vm-enabled yes #是否使用虚拟内存,默认值为no
vm-swap-file /tmp/redis.swap #虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-max-memory 0 #vm大小限制。0:不限制,建议60-80% 可用内存大小。
vm-page-size 32 #根据缓存内容大小调整,默认32字节。
vm-pages 134217728 #page数。每 8 page,会占用1字节内存。
vm-page-size #vm-pages 等于 swap 文件大小
vm-max-threads 4 #vm 最大io线程数。注意: 0 标志禁止使用vm
hash-max-zipmap-entries 512
hash-max-zipmap-value 64

list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
activerehashing yes

原理

  • 如果设置了一个Slave,无论是第一次连接还是重连到Master,它都会发出一个SYNC命令;
  • 当Master收到SYNC命令之后,会做两件事:
    a) Master执行BGSAVE,即在后台保存数据到磁盘(rdb快照文件);
    b) Master同时将新收到的写入和修改数据集的命令存入缓冲区(非查询类);
  • 当Master在后台把数据保存到快照文件完成之后,Master会把这个快照文件传送给Slave,而Slave则把内存清空后,加载该文件到内存中;
  • 而Master也会把此前收集到缓冲区中的命令,通过Reids命令协议形式转发给Slave,Slave执行这些命令,实现和Master的同步;
  • Master/Slave此后会不断通过异步方式进行命令的同步,达到最终数据的同步一致;
  • 需要注意的是Master和Slave之间一旦发生重连都会引发全量同步操作。但在2.8之后版本,也可能是部分同步操作。

部分复制

  • 2.8开始,当Master和Slave之间的连接断开之后,他们之间可以采用持续复制处理方式代替采用全量同步。
    Master端为复制流维护一个内存缓冲区(in-memory backlog),记录最近发送的复制流命令;同时,Master和Slave之间都维护一个复制偏移量(replication offset)和当前Master服务器ID(Master run id)。当网络断开,Slave尝试重连时:
    a. 如果MasterID相同(即仍是断网前的Master服务器),并且从断开时到当前时刻的历史命令依然在Master的内存缓冲区中存在,则Master会将缺失的这段时间的所有命令发送给Slave执行,然后复制工作就可以继续执行了;
    b. 否则,依然需要全量复制操作;
  • Redis 2.8 的这个部分重同步特性会用到一个新增的 PSYNC 内部命令, 而 Redis 2.8 以前的旧版本只有 SYNC 命令, 不过, 只要从服务器是 Redis 2.8 或以上的版本, 它就会根据主服务器的版本来决定到底是使用 PSYNC 还是 SYNC :
    如果主服务器是 Redis 2.8 或以上版本,那么从服务器使用 PSYNC 命令来进行同步。
    如果主服务器是 Redis 2.8 之前的版本,那么从服务器使用 SYNC 命令来进行同步。

同步机制

全量同步

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
  1)从服务器连接主服务器,发送SYNC命令;
  2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
  4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
  5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
  6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

增量同步

Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

Redis主从同步策略

主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

最后

emoji

来个简单的小例子

2个项目先来测试一下:

  • eureka-server
  • eureka-service

eureka-server

pom 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.joylau.cloud.eureka.server</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>eureka-server</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

application.properties

1
2
3
4
5
server.port=8080
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/

EurekaServerApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.joylau.cloud.eureka.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

eureka-service

pom 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.joylau.cloud.eureka.service</groupId>
<artifactId>eureka-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>eureka-service</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

application.properties

1
2
3
4
spring.application.name=eureka-service
server.port=8888
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/

EurekaServerApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.joylau.cloud.eureka.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class EurekaServiceApplication {

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

ComputeController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package cn.joylau.cloud.eureka.service;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* Created by JoyLau on 4/14/2017.
* cn.joylau.cloud.eureka.service
*/
@RestController
public class ComputeController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/add" ,method = RequestMethod.GET)
public Integer add(@RequestParam Integer a, @RequestParam Integer b) {
ServiceInstance instance = client.getLocalServiceInstance();
Integer r = a + b;
logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r);
return r;
}
}

配置说明

  • @EnableEurekaServer : 开启服务发现
  • eureka是一个高可用的组件,它没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成),在默认情况下eureka server也是一个eureka client ,必须要指定一个 server
  • 当client向server注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka server 从每个client实例接收心跳消息。 如果心跳超时,则通常将该实例从注册server中删除
  • @EnableDiscoveryClient : 注册一个微服务
  • spring.application.name :应用名

关于joylau-mybatis的说明

  • 该项目来源自 https://github.com/abel533/Mapper 详细信息和源代码可fork查看
  • 我封装之后项目地址 https://github.com/JoyLau/joylau-mybatis
  • 我自己整合通用Mapper,分页,以及排序功能,使用起来无缝结合,丝般顺滑
  • 我对其封装了所有的通用mapper,并整合本项目添加了自己的方法,详细请查看下文或者在线查看api文档: http://api.joylau.cn/
  • 文档你主要需要查看function的类注释
  • 下面我来逐一介绍:

BaseController

继承FunctionController,目前有2个抽象方法,getSession()和getContextPath(),一看就知道是干嘛的,不多说。想要扩展很简单,继续写自己的方法即可

BaseMapper

  • 集成了MySQL所使用的绝大部分通用Mapper,包括BaseMapper,ExampleMapper,RowBoundsMapper,MySqlMapper,IdsMapper…等等,详细可查看API文档,或者下载源码查看
  • 所有的单表及简单的多表操作都在这里面啦,基本上你是不需要扩展啦,好不好用,敲起mapper再点一下你就知道了

BaseService

  • 得益于Spring项目的强大支持,在Spring4.x后,支持泛型注入,这使得我们封装的更加简单了
  • 现在,不必再调用到Mapper层,现在在Service层就可以完美使用,封装了3个插入方法,4个更新方法,5个删除方法,13个查询方法
  • 内容涵盖了单条记录CRUD;根据ID或者属性或者条件CRUD;批量删除,插入;分页查询
  • 说下分页查询怎么使用:调用selectPage可以进行单表分页查询,调用selectPageByExample可以进行条件分页查询

BaseServiceImpl

  • 继承的FunctionServiceImpl已经实现了上述所有的通用CURD方法
  • 在继承的FunctionServiceImpl类里我提供了获取mapper的方法,由此方法,可以进行很方便的扩展,你懂得~~

BaseModel

  • 添加每个实体都会用到的id属性
  • 添加了createTime和updateTime属性,虽然在业务上可能没有什么用处,但是对于开发和运维的作用相当大,谁用谁知道

我的接口解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 保存一个实体,null的属性也会保存,不会使用数据库默认值
*/
int insert(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 保存一个实体,null的属性不会保存,会使用数据库默认值
*/
int insertSelective(T model);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 批量插入,支持批量插入的数据库可以使用,另外该接口限制实体包含`id`属性并且必须为自增列
*/
int insertList(List<T> list);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键更新实体全部字段,null值会被更新
*/
int updateByPrimaryKey(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键更新属性不为null的值
*/
int updateByPrimaryKeySelective(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件更新实体`model`包含的全部属性,null值会被更新
*/
int updateByExample(T model, Object example);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件更新实体`model`包含的不是null的属性值
*/
int updateByExampleSelective(T model, Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体属性作为条件进行删除,查询条件使用等号
*/
int delete(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体id删除
*/
int deleteById(int id);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件删除数据
*/
int deleteByExample(Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段
*
* @param ids 如 "1,2,3,4"
*/
int deleteByIds(String ids);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字段进行删除,方法参数必须包含完整的主键属性
*/
int deleteByPrimaryKey(Object key);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的属性值进行查询,查询条件使用等号
*/
List<T> select(T model);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的id查询实体
*/
T selectById(int id);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 查询全部结果
*/
List<T> selectAll();

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件进行查询
*/
List<T> selectByExample(Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据example条件和RowBounds进行分页查询
*/
List<T> selectByExampleAndRowBounds(Object example, RowBounds rowBounds);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字符串进行查询,类中只有存在一个带有@Id注解的字段
*
* @param ids 如 "1,2,3,4"
*/
List<T> selectByIds(String ids);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
*/
T selectByPrimaryKey(Object key);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的属性查询总数,查询条件使用等号
*/
int selectCount(T model);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件进行查询总数
*/
int selectCountByExample(Object example);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
*/
T selectOne(T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据实体属性和RowBounds进行分页查询
*/
List<T> selectByRowBounds(T model, RowBounds rowBounds);


/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 单表分页查询
*/
PageInfo selectPage(int pageNum, int pageSize, T model);

/**
* Created by JoyLau on 4/6/2017.
* 2587038142.liu@gmail.com
* 根据Example条件进行分页查询
*/
PageInfo selectPageByExample(int pageNum, int pageSize, Object example);

怎么使用?

很简单

  • 你的Mapper继承BaseMapper
  • 你的Service继承BaseService
  • 你的ServiceImpl实现你的Service借口,再继承BaseServiceImpl
  • 你的Model继承BaseModel

来试一下

  • 在你的ServiceImpl里点一下方法试试? 是不是很棒???
  • 在你的Mapper里再点一下方法试试?? 6666…

最后

  • 能想到的我都写了,BaseMapper和BaseServiceImpl基本上不需要扩展了,有不明白的可以联系我
  • 欢迎指正,共同学习
0%