JoyLau's Blog

JoyLau 的技术学习与思考

实验步骤

  • 新建一个项目
  • 可先分别在码云和 GitHub 上建好仓库<可选>
  • 将项目提交的码云上
  • 项目提交到另一个仓库的时候重新 define remote <可选>
  • 之后每次先提交到本地仓库,可以根据每次提交到本地仓库的不同,来选择定义的 remote 来分别提交
  • 每次 pull 也可以选择仓库

遇到个问题

问题

  • 在我新建好码云的仓库后,提交项目,遇到 Git Pull Failed: fatal: refusing to merge unrelated histories

原因

  • 原因:git拒绝合并两个不相干的东西

解决

  • 此时在命令行输入 : git pull origin master –allow-unrelated-histories
  • 要求我输入提交信息
  • 输入完成后,按一下Esc,再输入:wq,然后回车就OK了
  • 再回来提交就可以了

系统工具

  • BetterZip : mac上面的最好的解压工具
  • CHM View : 查看chm类型的开发文档
  • Easy New File Free : 右击桌面,可以像win一样新建文件
  • Bartender 2 : 任务栏menu图标整理
  • iStat Menus : 系统网速、cpu、内存监控工具
  • SwitchResX : 外接显示器,调节DPI
  • Go2Shell : 在finder的任意文件夹下打开终端
  • Aria2GUI : 突破百度限速
  • Alfred 3 : 效率神器,谁用谁知道
  • PDF Expert : 查看pdf
  • 远程桌面连接 : mac电脑上远程连接windows,网址: https://rink.hockeyapp.net/apps/5e0c144289a51fca2d3bfa39ce7f2b06 (2017年10月26日加)

播放器

  • 网易云音乐 :这个必备啊
  • 优酷 :这个可以免费看1080P视频,没广告,有时候出抽风的时候还可以看会员视频
  • OBS : 视频直播、录制软件
  • Movist : 视频播放器,支持的格式很多

小工具

  • CleanMyMac 3 : 清理mac电脑垃圾
  • ShadowsocksX : 翻墙必备
  • TeamView : 桌面远程软件
  • MacDown : 开源的markdown编辑器
  • Path Finder : Finder增强版
  • Parallels Desktop : 虚拟机
  • FileZilla : ftp工具
  • Foxmail : 邮箱客户端
  • Folx : 下载工具

开发工具

  • FireFox : 火狐
  • Google Chrome : 必备
  • IntelliJ IDEA : 必备IDE
  • WebStorm : web开发必备
  • DataGrip : 数据库管理软件
  • Navicat Premium : 已经使用习惯的MySQL连接工具,也支持其他数据库
  • XShell : SSH远程连接工具,我还是比较喜欢终端下的ssh命令连接,虽然有一个家族的系列产品
  • Sublime Text3 : 文本编辑器
  • Beyond Compare : 文本比较工具
  • GitHub Desktop : github GUI客户端
  • rdm : redis可视化GUI界面
  • HBuilder : h5开发工具
  • iTerm : 终端

自己暂时使用的工具都已归纳出来,以后有新的好用的工具,会加上的,Mac下大部分工具都是收费的,你可以偷偷点一下 xclient.info

官方视频

开始拆箱

MacBook Pro

先来看一下刚拿到手的包装是什么样的

一台主机

我在官网订购了一个 USB-typeC 转 USB 的转接口

那个小盒子就是

MacBook Pro

打开主机纸盒

MacBook Pro

掰开这个直接就可以把里面的主机盒抽出来,很方便

两边都是这样设计的

MacBook Pro

就2样东西

都摆放好了

准备拿剪刀拆开

MacBook Pro

来一张侧面照

MacBook Pro

拆开盒子保护膜

打开镂空设计的上盖,看到我们的主机真容

MacBook Pro

这样一看,真的很薄,起码比我以前用过得笔记本都要薄多了

MacBook Pro

2端都是 USB-C 接口的充电线

适配器感觉好大啊

MacBook Pro

靠近点看下USB-C的充电线

MacBook Pro

然后就什么都没有了

底下的盒子也打不开

MacBook Pro

苹果的LOGO贴纸

说明书

三包凭证

MacBook Pro

开始正式拆开主机的包装纸

MacBook Pro

一睹真容
15.6寸的

进入系统

MacBook Pro

盖子一打开就开机了

屏幕与键盘之间隔了一张纸

让我们拿开他

MacBook Pro

很快就进入了系统

MacBook Pro

重新设计的蝴蝶键盘

键程很短

按键很紧凑

MacBook Pro

来一张键盘的整体照

上面是全新的 Multi-Touch Bar ,替换了以前的一排功能按键,许多mac内置的应用在Touch Bar上都有支持

MacBook Pro

触摸板的占比实在是太大了

看我一只手放上去,刚好差不多

手有点丑,请忽略

MacBook Pro

迫不及待的想进入系统尝试一下了

先来连接家里的WIFI

MacBook Pro

老套路了

都是下一步

再下一步

MacBook Pro

在电源键上提供了和iPhone上一个的指纹支持

MacBook Pro

来录入我自己的指纹

不知道用的是什么材料,在这个TouchBar上面滑来滑去很舒服,很有感觉

MacBook Pro

正在设置指纹

稍等一下

MacBook Pro

终于正式进入系统了

屏幕的显示效果很震撼

特效动画的帧数很高,给人感觉很流畅

系统信息

MacBook Pro

看一下系统信息

MacBook Pro

显示器信息

2G独立显存

个人感受

使用它也有一周多了,说一下自己的整体感受吧

  • Retina显示器的显示效果真的很好,真是惯坏了眼睛,现在再去看普通的显示器,就感觉有很强的颗粒感
  • macOS High Sierra字体渲染的很棒,系统中有很多适合编程的字体,在 IntelliJ IDEA 中编码很爽
  • 更大的分辨率能看到更多的内容
  • 系统安装软件什么的很方便,没有想Windows下那么碎片化
  • Multi-Touch Bar 有很多有意思的功能,除了官方宣传的和MacOS本身自带的,想滑动查看照片,添加emoji小表情,控制亮度。。。之类的,大量第三方的软件也进行了适配,网易云音乐,搜狗输入法就适配的很不错
  • 系统触摸板真的是Windows平台无法比拟的,有很多手势,编码什么的,完全可以不用鼠标
  • 键盘旁边2个喇叭的音质效果很震撼,而且声音特别大,看电影,听音乐很有感觉
  • 耗电也比Windows系统的笔记本少多了,充满电的话,就拿我平时工作情况来说,开多个IDEA,起多个服务,多个浏览器,多个编辑器。。。什么什么的,大概能撑个8,9个小时,上班一天不充电….
  • 颜值好,很符合现代化审美

缺点也还是有的

  • 太贵

前言

  • 使用很简单
  • 关注业务开发
  • 熟悉提供的注解

开始

引入依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>

配置启动

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
@SpringBootApplication
@EnableSwagger2
public class JoylauSwagger2Application {

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

@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.joylau.code"))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot构建RESTful APIs")
.description("将每一个注解的@RestController和@ResponseBody的类和方法生成API,点击即可展开")
.termsOfServiceUrl("http://blog.joylau.cn")
.contact(new Contact("joylau","http://blog.joylau.cn","2587038142@qq.com"))
.license("The Apache License, Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.version("1.0")
.build();
}
}

注解说明

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
@RestController
@RequestMapping(value="/users") // 通过这里配置使下面的映射都在/users下,可去除
public class UserController {

static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());

@ApiOperation(value="获取用户列表", notes="")
@RequestMapping(value={""}, method= RequestMethod.GET)
public List<User> getUserList() {
List<User> r = new ArrayList<User>(users.values());
return r;
}

@ApiOperation(value="创建用户", notes="根据User对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
@RequestMapping(value="", method=RequestMethod.POST)
public String postUser(@RequestBody User user) {
users.put(user.getId(), user);
return "success";
}

@ApiOperation(value="获取用户详细信息", notes="根据url的id来获取用户详细信息")
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public User getUser(@PathVariable Long id) {
return users.get(id);
}

@ApiOperation(value="更新用户详细信息", notes="根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
})
@RequestMapping(value="/{id}", method=RequestMethod.PUT)
public String putUser(@PathVariable Long id, @RequestBody User user) {
User u = users.get(id);
u.setName(user.getName());
u.setAge(user.getAge());
users.put(id, u);
return "success";
}

@ApiOperation(value="删除用户", notes="根据url的id来指定删除对象")
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public String deleteUser(@PathVariable Long id) {
users.remove(id);
return "success";
}

}

常见注解

  • @Api:修饰整个类,描述Controller的作用
  • @ApiOperation:描述一个类的一个方法,或者说一个接口
  • @ApiParam:单个参数描述
  • @ApiModel:用对象来接收参数
  • @ApiProperty:用对象接收参数时,描述对象的一个字段
  • @ApiResponse:HTTP响应其中1个描述
  • @ApiResponses:HTTP响应整体描述
  • @ApiIgnore:使用该注解忽略这个API
  • @ApiClass
  • @ApiError
  • @ApiErrors
  • @ApiParamImplicit
  • @ApiParamsImplicit

最后

注意

  • Swagger2默认将所有的Controller中的RequestMapping方法都会暴露,然而在实际开发中,我们并不一定需要把所有API都提现在文档中查看,这种情况下,使用注解@ApiIgnore来解决,如果应用在Controller范围上,则当前Controller中的所有方法都会被忽略,如果应用在方法上,则对应用的方法忽略暴露API

或者重写方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Docket createRestApi() {
Predicate<RequestHandler> predicate = new Predicate<RequestHandler>() {
@Override
public boolean apply(RequestHandler input) {
Class<?> declaringClass = input.declaringClass();
if (declaringClass == BasicErrorController.class)// 排除
return false;
if(declaringClass.isAnnotationPresent(RestController.class)) // 被注解的类
return true;
if(input.isAnnotatedWith(ResponseBody.class)) // 被注解的方法
return true;
return false;
}
};

前言

本文说明

  • 使用之前rabbitMQ的介绍我就不说了,我认为你已经了解了
  • rabbitMQactiveMQ的对比区别我也不说了,我认为你已经查过资料了
  • rabbitMQ的安装,我也不说了,我认为你下载的时候已经看到了官网的安装说明,给一个Windows安装的链接:http://www.rabbitmq.com/install-windows.html
  • rabbitMQweb插件的启用,我也不说,我认为你已经会了
  • 那我们开始吧

入门使用

在使用之前先看一下rabbitMQ-client的使用

先引入依赖:

1
2
3
4
5
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.0</version>
</dependency>

在看代码:

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
public void product() throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ地址
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个频道
Channel channel = connection.createChannel();
//声明一个队列 -- 在RabbitMQ中,队列声明是幂等性的(一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同),也就是说,如果不存在,就创建,如果存在,不会对已经存在的队列产生任何影响。
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
//发送消息到队列中
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
System.out.println("P [x] Sent '" + message + "'");
//关闭频道和连接
channel.close();
connection.close();
}


public void consumer() throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置RabbitMQ地址
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个频道
Channel channel = connection.createChannel();
//声明要关注的队列 -- 在RabbitMQ中,队列声明是幂等性的(一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同),也就是说,如果不存在,就创建,如果存在,不会对已经存在的队列产生任何影响。
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println("C [*] Waiting for messages. To exit press CTRL+C");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,告诉服务器我们需要那个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("C [x] Received '" + message + "'");
}
};
//自动回复队列应答 -- RabbitMQ中的消息确认机制
channel.basicConsume(QUEUE_NAME, true, consumer);
}

代码的注释很详细

SpringBoot中的使用

引入依赖

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>

配置文件

1
2
3
4
5
6
7
8
9
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
output:
ansi:
enabled: always

生产者

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class Product {
@Autowired
private AmqpTemplate rabbitTemplate;

public void send() {
String context = "hello " + new Date();
System.out.println("生产者发送信息 : " + context);

new Queue("hello");
this.rabbitTemplate.convertAndSend("hello", context);
}
}

创建消息生产者Product。通过注入AmqpTemplate接口的实例来实现消息的发送,AmqpTemplate接口定义了一套针对AMQP协议的基础操作。在Spring Boot中会根据配置来注入其具体实现。在该生产者,我们会产生一个字符串,并发送到名为hello的队列中

消费者

1
2
3
4
5
6
7
8
@Component
@RabbitListener(queues = "hello")
public class Consumer {
@RabbitHandler
public void process(String hello) {
System.out.println("消费者接受信息 : " + hello);
}
}

创建消息消费者Consumer。通过@RabbitListener注解定义该类对hello队列的监听,并用@RabbitHandler注解来指定对消息的处理方法。所以,该消费者实现了对hello队列的消费,消费操作为输出消息的字符串内容。

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringRunner.class)
@SpringBootTest
public class JoylauSpringBootRabbitmqApplicationTests {

@Autowired
private Product product;

@Test
public void test() throws Exception {
product.send();
}

}

再来一张图

示例截图

exchange 多个消费者

当Exchange和RoutingKey相同、queue不同时,所有消费者都能消费同样的信息
Exchange和RoutingKey、queue都相同时,消费者中只有一个能消费信息,其他消费者都不能消费该信息。

下面示例的队列名称可以随意写个,启动时 @RabbitListener 的 bindings 会自动使用 key 绑定队列到exchange

1
2
3
4
5
6
7
8
9
10
@RabbitHandler
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = "${spring.application.name}"),
exchange = @Exchange(value = "${spring.rabbitmq.template.exchange}"),
key = "${spring.rabbitmq.template.routing-key}")
)
public void listenerTrafficMessage(Message message){
System.out.println(message.getClass().getName());
}

消息返回队列

需要处理完消息后在将消息返回队列的话需要配置 spring.rabbitmq.listener.simple.acknowledge-mode: manual
之后注解@RabbitListener 到方法上
Channel channel 进行返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RabbitHandler
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = "${spring.application.name}"),
exchange = @Exchange(value = "${spring.rabbitmq.template.exchange}"),
key = "${spring.rabbitmq.template.routing-key}")
)
public void listenerTrafficMessage(Message message, Channel channel){

System.out.println(message.getClass().getName());

try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
} catch (IOException e) {
e.printStackTrace();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
rabbitmq:
host: 192.168.10.224
port: 35672
username: guest
password: guest
virtual-host: /
listener:
simple:
acknowledge-mode: manual #设置消费端手动 ack
concurrency: 1 #消费者最小数量
max-concurrency: 1 #消费者最大数量
prefetch: 1 #在单个请求中处理的消息个数,他应该大于等于事务数量(unack的最大数量)
template:
exchange: SURVEY_CENTER
routing-key: trafficCongestionSituationBD

在属性配置文件里面开启了ACK确认 所以如果代码没有执行ACK确认 你在RabbitMQ的后台会看到消息会一直留在队列里面未消费掉 只要程序一启动开始接受该队列消息的时候 又会收到

1
2
3
// 告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了
// 否则消息服务器以为这条消息没处理掉 后续还会在发,true确认所有消费者获得的消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

丢弃消息

1
2
3
4
5
6
7
8
9
10
//最后一个参数是:是否重回队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
//拒绝消息
//channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
//消息被丢失
//channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
//消息被重新发送
//channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
//多条消息被重新发送
//channel.basicNack(message.getMessageProperties().getDeliveryTag(), true, true);

Windows下最适合编程的字体要数Consolas字体了,那么如何将命令提示符换成Consolas字体呢?我们只需要注册以下信息即可:

1
2
3
4
5
6
7
8
9
10
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe]
"WindowSize"=dword:00170058
"ScreenBufferSize"=dword:01170058
"WindowPosition"=dword:0079004b
"ColorTable01"=dword:00235600
"FontSize"=dword:00120000
"FontWeight"=dword:00000190
"FaceName"="Consolas"
"FontFamily"=dword:00000036

新建一个文本文件,将信息保存到此文本文件中
然后将文本文件重命名为*.reg
双击此文件将其注册

通常的属性注入

一般情况下我们使用Spring或者SpringMVC的时候会使用@Value()注入

使用SpringBoot的时候会使用@ConfigurationProperties(prefix = "xxxx")

注入自定义的呢?这样:@ConfigurationProperties(prefix = "xxx",locations = "classpath:config/xxxx.properties")

更复杂一点的注入

如上图所示我注入了一个List<String>

拓展

那么同样的方式,是否可以注入Map,String[]….呢?

思考

properties的文件被读取的时候使用的就是Map,那么我们知道Map是无序了,这样就会导致我们原先要求的一致性可能达不到

解决方式

properties文件改成采用yml文件,或者升级SpringBoot的版本,貌似新版本采用的LinkedHashMap

引用本地包并打包部署的问题

背景

  • 在最近的开发中需要对接C++服务提供的ZeroC Ice接口,客户机环境安装了和服务环境相同的Ice,服务端的Ice比较老,是3.4.0的版本
    在maven的中央仓库中没有找到ice-3.4.0的jar包,只能引用安装路径下提供的jar了

那么常用的写法是这样的:(包括但不限于SpringBoot)

1
2
3
4
5
6
7
8
<!--Ice-->
<dependency>
<groupId>Ice</groupId>
<artifactId>Ice</artifactId>
<version>3.4.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/lib/Ice.jar</systemPath>
</dependency>

我是在src下新建的lib目录,在开发编译的是没有问题的。

在进行打包的时候发现Ice.jar没有被打进去

相对于这个应用来说,打成jar包是最合适的做法了

这里说一下,用package打包,不要用SpringBoot插件的jar打包

解决

build里加上这一段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
..............
<resources>
<resource>
<directory>src/lib</directory>
<targetPath>BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<targetPath>BOOT-INF/classes/</targetPath>
</resource>
</resources>
</build>

之后,再打包,再解压一看,果然是打进去了,完美~

然后,遇到了新问题……..

以jar运行时没有主清单属性

之后便很愉快的使用 java -jar xxxxx.jar

提示:没有主清单属性

再解压一看,有Application.java类,但是jar包的大小明显不对,光SpringBoot父项目依赖的jar至少也有10+M了,这个大小明显不对

在结合没有主属性的错误,知道了错误的原因在这:

1
2
3
4
5
6
7
8
9
10
11
   <dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependencyManagement>

我用的项目是多模块依赖

解决的方式是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-dependencies.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</build>

正如我文章截图的那样,解决问题!

父项目依赖,打包成jar

同时加入以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

2017年9月19日更新

SpringBoot 项目打包时修改 MANIFEST.MF 文件

一般情况下我们的 MANIFEST.MF内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Manifest-Version: 1.0
Implementation-Title: joylau-media
Implementation-Version: 1.7-RELEASE
Archiver-Version: Plexus Archiver
Built-By: JoyLau
Implementation-Vendor-Id: cn.joylau.code
Spring-Boot-Version: 1.5.4.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: cn.joylau.code.JoylauMediaApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_45
Implementation-URL: http://projects.spring.io/spring-boot/joylau-media
/

解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--fork : 如果没有该项配置,肯呢个devtools不会起作用,即应用不会restart -->
<fork>true</fork>
</configuration>
</plugin>

//修改版本号,一般为pom文件的版本
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifestEntries>
<Manifest-Version>${version}</Manifest-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>

SpringBoot 项目中引入缓存

  • 引入依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

@EnableCaching 开启缓存

@CacheConfig(cacheNames = “api_cache”) 配置一个缓存类的公共信息

@Cacheable() 注解到方法上开启缓存

@CachePut() 根据使用的条件来执行具体的方法

@CacheEvict() 根据配置的参数删除缓存

SpringBoot默认支持很多缓存,spring.cache.type就可以知道,默认的是实现的是SimpleCacheManage,这里我记一下怎么设置缓存的超时时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@EnableCaching
@EnableScheduling
public class CachingConfig {
public static final String CACHENAME = "api_cache";
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager(CACHENAME);
}
@CacheEvict(allEntries = true, value = {CACHENAME})
@Scheduled(fixedDelay = 120 * 1000 , initialDelay = 500)
public void reportCacheEvict() {
System.out.println("Flush Cache " + dateFormat.format(new Date()));
}
}

这里巧妙的使用了 定时任务,再其加上注解CacheEvict来清除所有cache name 为 api——cache 的缓存,超时时间是120s

在说说我比较喜欢的使用方式

单独写了篇文章,戳下面:

持续更新中…

0%