开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

没想到吧,Java开发 API接口可以不用写 Control

发表于 2021-04-08

本文案例收录在 github.com/chengxy-nds…

大家好,我是小富~

今天介绍我正在用的一款高效敏捷开发工具magic-api,顺便分享一点工作中使用它的心得

缘起

先说一下我为什么会使用这个工具?

最近新启动一个项目,业务并不算复杂,那种典型的管理系统,产品要求支持全局页面配置化,前端一切相关配置必须通过接口返回,比如:像查询下拉框(启用、禁用)这类简单的条件,国际化,必须做到全动态配置。

其实只要人手够时间够,这些都没问题,但问题就在于立项到上线周期就给十几天,而开发时间满打满算不到10来天,时间紧又不给加人,底层程序员的生活真是太难了。

不过办法总比困难多,前同事老哥给我推荐了这个工具,然后就真香了,哈哈~

magic-api 是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Controller、Service、Dao、Mapper、XML、VO等Java对象即可完成常见的HTTP API接口开发。

上边是官方对工具的介绍,但好像还是没明白它是干什么的,接下来咱们演示一下,你就会觉得它很哇塞了

环境

首先pom.xml 引入magic-api核心包magic-api-spring-boot-starter

1
2
3
4
5
6
7
8
9
10
11
java复制代码  <dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-spring-boot-starter</artifactId>
<version>0.7.1</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

application.yml 配置更简单,数据库(没数据库操作可以不写)和magic-api的基础信息

1
2
3
4
5
6
7
8
9
10
java复制代码magic-api:
web: /magic/web # UI请求的界面以及UI服务地址
server:
port: 9999
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
password: xinzhifu521
url: jdbc:mysql://47.93.6.5:3306/magic-api
username: root

好了~ 到这环境就搭建完成!


在具体演示之前先吐槽一下用Java开发API的缺点,首当其冲的就是啰嗦,尤其是在工期紧,功能需快速迭代的时候,既要严格执行开发规范,又不能耽误工期,即便最简单的一个API接口,也要写对应的 Controller、Service、Dao、Mapper、DTO、VO等类,尽管这些基础编码有对应的代码生成器,但维护起来还是相当麻烦,magic-api起到一个很好的辅助作用,少写了很多代码。

实践

直接访问http://127.0.0.1:9999/magic/web打开magic-api可视化界面,看到如下的界面。


创建一个分组,其中分组前缀为一组API接口的访问根目录,相当于@Controller("/order")注解。

接着在分组中创建接口 order_detail,页面配置接口的基础信息,接口名称、请求路径、请求方法、请求参数、请求header等,接口直接return返回内容

1
java复制代码return ‘小富最帅’

在页面访问刚刚创建接口的全路径 http://127.0.0.1:9999/order/order_detail ,发现已经成功返回数据。


也可以直接拼JSON格式数据直接返回

如果URL传参 /order_detail/{id},导入request模块获取参数

1
2
java复制代码import request;
a = path.id

到这一个简单的API接口就开发完了,而此时我们还未在项目中写一行代码


但上边只是静态数据,在实际开发中往往要与数据库打交道,magic-api提供了一些类似于python开发中的模块化组件,例如引入import db 模块,直接执行SQL语句会返回JSON格式数据,省略了很多中间步骤。

magic-api语法与Java的差异不大,不过更加精简了一些,只要写过Java对它学习成本并不高,比如常用得for循环,也会有普通和lambda多种写法。

1
2
3
4
5
6
7
java复制代码var sum = 0;
var list = [1,2,3,4,5];
for(val in list){
sum = sum + val;
}

list.each(it => sum+= it + 1)

这里我只简单的介绍了使用,还有很多高级特性,比如:调用Java API、集成redis、Mongo等,感兴趣的同学自己看下官方文档吧,它还提供了很多语法demo,拿来即用就好。

地址:http://140.143.210.90:9999/magic/web/index.html

心得

magic-api在我整个项目赶工期的过程中可谓是居功至伟,节省了一大半的开发时间,不仅后端开发接口效率显著提升,对前端联调帮助也很大。

前后端从开始就定义好数据结构,后端快速提供静态数据接口,前端用真实接口联调,后端补充完业务逻辑后无缝替换成真实数据,这样做到同步开发,前端也不用只写伪代码等接口联调了。

magic-api虽然可以提高开发效率,但是实际应用中我也只敢把它用在一些逻辑相对简单,偏配置类接口,再有就是为前端快速提供静态接口,核心业务还是要按“规矩”办事,毕竟系统稳定、安全才是最重要的。

整理了几百本各类技术电子书,有需要的同学公号[ 程序员内点事 ]内回复[ 666 ]自取。技术群快满了,想进的同学可以加我好友,和大佬们一起吹吹技术。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

程序猿的第一本互联网黑话指南

发表于 2021-04-08

文章持续更新,可以微信搜一搜「golang小白成长记」第一时间阅读,回复【教程】获golang免费视频教程。本文已经收录在GitHub https://github.com/xiaobaiTech/golangFamily , 有大厂面试完整考点和成长路线,欢迎Star。

很烦,小侄子最近不给我打电话了。

放下作为叔叔的面子,拨通电话。

打电话

打电话

他说,他最近很烦。

我以为他长大了,谈恋爱了。

他说,最近他们学校,换了个校长。

喜欢在早上升国旗之后发表演讲。

“发表演讲挺好的啊,德智体美。”

他说他听不懂。比如校长今天早上提到说:

“我们需要大力推进新型教育场景,尝试建立一些新的教学模型,用心打磨细节。

并重点关注其他教育形式的可能性。利用传统教育形式去驱动新型教育形式,打通底层逻辑,形成闭环。用传统教育理念赋能新型教育场景。让传统和新型教育形式作为一整套组合拳,加速学生更好更快的发展。”

好家伙,这么长的一句话,信息量竟然为0。

再仔细一想。

心里一惊,原来是同行啊,这老哥,一定是互联网出来的。

光听到赋能这个词,我就知道是哪个厂出来的了。

我劝小侄子,这个想听懂,得有一定的互联网阅历才行啊。

小侄子却表示:“叔叔,你不是在互联网公司上班吗?你教教我吧”

不行,这对于年幼的他,还太早了。

直到他告诉我,“我们班长现在已经学会这种超酷的说话方式了,现在班里的女生都超爱听他说话”

这???

不,这能忍?就算是这样的我,也有想要守护的东西。我必须教会。

那晚我打了3小时视频电话,畅快淋漓,让我差点忘了手机还欠费47块这件事。

三天后,侄子跟我说,好是挺好的,就是有点废纸,现在班里的女同学都喜欢找他要签名。

嗯,不是很环保。

考虑到身边还有很多连互联网黑话都不会说的胖友们,在这里简单写一份程序员必知必会的互联网黑话指南。赠与有缘人。

诚实质朴如你,实在无法低下高傲的头颅的话,希望你也能听懂别人在不讲人话的时候,到底背后是几层意思。

我不允许我的胖友们听不懂!更不允许你们被蒙在鼓里!

领导开口闭口都是商业模式,闭环?整个会听下来,就记住了赋能,抓住,推进,深入这些词?

产品让你跟她对齐一下?

其他部门同事说再不马上处理某个问题,她就要上升了,而这时候你以为她是要上天?

hr跟你说公司扁平化管理,一年有两次加薪机会的时候,你只听到了加薪,却听漏了这只是机会?

看完下面,你会对上面的场景有不一样的认识。

首先对于国内互联网大厂,我们盘一下常说的一些话。

比如抓手,是指手可以抓得到的地方,一般是指项目的切入点。

当提到项目需要努力寻找抓手的时候,说明项目现在比较蒙圈,还没有好的切入点,还在各种试错。

划重点了,项目怎么做都没想清楚,建议各位老哥 LeetCode 刷起来吧。

当你在职级答辩的时候,也可能被问到某件事情的抓手是什么。

一般是问你这个问题的切入点在哪,从哪里开始解决,

一般是要你说下从现象到本质的判断过程。

比如,机器内存暴涨导致报警,这时候现象切入点是某个进程,再定位代码更新部分,最后定位问题。

而一般项目的抓手都是从某个垂直领域里的细分领域里发力,

垂直领域和细分领域的区别大概可以理解为编程和用golang编程之间的区别。

领域不够垂直,不够细分,大概就是说,啥都干了点,不够专,不够精。

赋能是个啥,第一次接触这个词的时候,我还去搜了一下定义

赋能的定义

赋能的定义

好家伙,竟然拿一堆别人看不懂的词去解释一个别人看不懂的词,属实让人意外了。

后来搞明白了。说白了,别人没有某个东西,而你有,你主动找他跟你合作。重点在于主动这个词,有忽悠内味了。

还有,如果运营产品们喜欢探讨项目的盈利模式,你就要知道,他们其实心里也没底这玩意到底能不能赚钱。如果赚钱了,就会找出各种理由解释“这个项目为什么能赚钱”,这就是所谓的各种方法论了。

当然,如果项目赚钱了,又不给你发钱,就可以说是延迟满足感。

类似的不讲人话的词还有很多。

开个会,叫对齐一下。

找你领导,叫上升。

最容易pian到投资的领域,叫风口。

同时用多种方法去搞钱,就叫组合拳。

跟别的公司产生了合作,那就出现了生态,合作的公司多了,就叫生态链。

生态链产生闭环,这里就划重点了,意思是开始赚钱了。

还有另外一个需要敲黑板,划重点的考点。

当老板提到要向社会输送人才,就是要开始解雇咸鱼了。。不管读多少遍,我还是觉得说这话的人是真的厉害,这才是语!言!艺!术!请各位老哥把牛皮打在公屏上。

作为不讲人话的高发场地,面试那张方寸小桌,经常会出现一些让人觉得高深莫测的词语。

比如当你听到hr介绍公司弹性工作制,不打卡,也就是指下班时间不定,也没有加班这一说。

扁平化管理,在公司层面可能有各种积极意义。但对你个人而言,基本上约等于一般没有晋升。

能独立完成工作任务,当然就是产品的开发全都你一个人干。

很多时候hr会看着招聘简介上的信息问你问题。

问你有没有做过高并发相关,如果你给机器扩容过,大胆说有。

用了主备机器,就说是高可用。

用了缓存,那就是高性能了。

用了 Mysql,妥了,持久化。

虽然我很普通,但我很自信。

这时候你可能就会产生疑问了,这是不是国内互联网大厂才有的通病?

我去外企是不是就好了?

那欢迎来到快乐星球了。

上班等电梯的时候,你会听到旁边的 Lucy 端着她的美式卡布奇诺很惊讶的对 Emily 说

“Emiliy 你知道吗?我跟你说,昨天,Alice 他的手机在 meeting 的时候摔得粉粉碎,真的粉粉碎哦”

“哇哦,what a pity“

早上,老板会让大家开个 daily meeting。说一下大家今天的 todo list。还有 project 的进展。

可能这个 project 的 schedule 有些问题,尤其是buffer不多。另外,cost也偏高。

项目组没法 confirm 手上的 resource 能完全 take 得了。

anyway,我们可以先 argue 一下,再 follow up 最终的 output,先 run 起来,看work 不 work。

more importantly ,我们要尝试 cover 掉所有的 difficulty。

瞧瞧,瞬间觉得抓手闭环亲切了许多。

在这里我要正能量一波,否则对不起我这全日制本科的学历。

本来一些类似hr,kindle专有名词用英语,可以提升沟通效率,但是大量在不必要的场景下用这些中英文混杂的话,真的让人难受。

沟通,最 important 的是 efficiency,understand?

以前在网上看到过一个话题叫我想一巴掌扇si那个中英夹杂讲话的朋友!

当初年轻,高低也想说上那么两句。

现在成熟了,只会努力克制不要拿百宝袋里的四次元大嘴巴子扇人。

让更不成熟的年轻人去扇吧,要带响的哦。

国内的大厂和外企都不爱讲人话?

那我去小厂,总归可以了吧?

小了,

格局小了。

你忘了大厂给社会输送的人才了吗?

你猜他们都去哪了?

还是没学会怎么说黑话?

那就用数量堆叠的方式,让人在无数莫名其妙的动词名词形容词下沦陷。

让他一时之间眼花缭乱,抓不住任何重点。

反正,某厂周报,就是这么干的。

会说话就多说点吧。

我是小白,我们下期见。

文章推荐:

  • 给大家丢脸了,用了三年golang,我还是没答对这道内存泄漏题
  • 硬核!漫画图解HTTP知识点+面试题
  • 程序员防猝死指南
  • TCP粘包 数据包:我只是犯了每个数据包都会犯的错 |硬核图解
  • 硬核图解!30张图带你搞懂!路由器,集线器,交换机,网桥,光猫有啥区别?
别说了,一起在知识的海洋里呛水吧

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

别再面向 for 循环编程了,Spring 自带的观察者模式

发表于 2021-04-07

过完清明的我,开始默默期盼 51 长假。

距离 51 倒计时:还有 25 天!!!!

哎?学习一会,放松下心情。芜湖,起飞

本文在提供完整代码示例,可见 github.com/YunaiV/Spri… 的 lab-54 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

  1. 概述

在设计模式中,观察者模式是一个比较常用的设计模式。维基百科解释如下:

FROM zh.wikipedia.org/wiki/观察者模式

观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。

此种模式通常被用来实时事件处理系统。

在我们日常业务开发中,观察者模式对我们很大的一个作用,在于实现业务的解耦。以用户注册的场景来举例子,假设在用户注册完成时,需要给该用户发送邮件、发送优惠劵等等操作,如下图所示:

观察者模式

  • UserService 在完成自身的用户注册逻辑之后,仅仅只需要发布一个 UserRegisterEvent 事件,而无需关注其它拓展逻辑。
  • 其它 Service 可以自己订阅 UserRegisterEvent 事件,实现自定义的拓展逻辑。

友情提示:很多时候,我们会把观察者模式和发布订阅模式放在一起对比。

简单来说,发布订阅模式属于广义上的观察者模式,在观察者模式的 Subject 和 Observer 的基础上,引入 Event Channel 这个中介,进一步解耦。如下图所示:

对比

进一步的讨论,胖友可以瞅瞅《观察者模式和发布订阅模式有什么不同?》的讨论。

  1. Spring 事件机制

Spring 基于观察者模式,实现了自身的事件机制,由三部分组成:

  • 事件 ApplicationEvent:通过继承它,实现自定义事件。另外,通过它的 source 属性可以获取事件源,timestamp 属性可以获得发生时间。
  • 事件发布者 ApplicationEventPublisher:通过它,可以进行事件的发布。
  • 事件监听器 ApplicationListener:通过实现它,进行指定类型的事件的监听。

友情提示:JDK 也内置了事件机制的实现,考虑到通用性,Spring 的事件机制是基于它之上进行拓展。因此,ApplicationEvent 继承自 java.util.EventObject,ApplicationListener 继承自 java.util.EventListener。

  1. 入门示例

示例代码对应仓库:lab-54-demo 。

看完一些基础的概念,我们来撸个 Spring 事件机制的入门示例,具体的场景还是以用户注册为例子。新建 lab-54-demo 项目,最终项目如下图:项目结构

3.1 引入依赖

在 pom.xml 文件中,引入相关依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
XML复制代码<?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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-54-demo</artifactId>

<dependencies>
<!-- 实现对 Spring MVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

</project>

引入 spring-boot-starter-web 依赖的原因,是稍后会提供示例 API 接口,方便测试。

3.2 UserRegisterEvent

创建 UserRegisterEvent 事件类,继承 ApplicationEvent 类,用户注册事件。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Java复制代码public class UserRegisterEvent extends ApplicationEvent {

/**
* 用户名
*/
private String username;

public UserRegisterEvent(Object source) {
super(source);
}

public UserRegisterEvent(Object source, String username) {
super(source);
this.username = username;
}

public String getUsername() {
return username;
}

}

通过在 UserRegisterEvent 类中,定义成员变量 username,将用户名附带上。

3.3 UserService

创建 UserService 类,用户 Service。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Java复制代码@Service
public class UserService implements ApplicationEventPublisherAware { // <1>

private Logger logger = LoggerFactory.getLogger(getClass());

private ApplicationEventPublisher applicationEventPublisher;

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}

public void register(String username) {
// ... 执行注册逻辑
logger.info("[register][执行用户({}) 的注册逻辑]", username);

// <2> ... 发布
applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));
}

}

<1> 处,实现 ApplicationEventPublisherAware 接口,从而将 ApplicationEventPublisher 注入到其中。

<2> 处,在执行完注册逻辑后,调用 ApplicationEventPublisher 的 #publishEvent(ApplicationEvent event) 方法,发布「3.2 UserRegisterEvent」事件。

3.4 EmailService

创建 EmailService 类,邮箱 Service。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
Java复制代码@Service
public class EmailService implements ApplicationListener<UserRegisterEvent> { // <1>

private Logger logger = LoggerFactory.getLogger(getClass());

@Override
@Async // <3>
public void onApplicationEvent(UserRegisterEvent event) { // <2>
logger.info("[onApplicationEvent][给用户({}) 发送邮件]", event.getUsername());
}

}

<1> 处,实现 ApplicationListener 接口,通过 E 泛型设置感兴趣的事件。

<2> 处,实现 #onApplicationEvent(E event) 方法,针对监听的 UserRegisterEvent 事件,进行自定义处理。

【可以不加】<3> 处,锦上添花,设置 @Async 注解,声明异步执行。毕竟实际场景下,发送邮件可能比较慢,又是非关键逻辑。

友情提示:对 @Async 注解感兴趣的胖友,可以阅读《芋道 Spring Boot 异步任务入门》文章。

3.5 CouponService

创建 CouponService 类,优惠劵 Service。代码如下:

1
2
3
4
5
6
7
8
9
10
11
Java复制代码@Service
public class CouponService {

private Logger logger = LoggerFactory.getLogger(getClass());

@EventListener // <1>
public void addCoupon(UserRegisterEvent event) {
logger.info("[addCoupon][给用户({}) 发放优惠劵]", event.getUsername());
}

}

<1> 处,在方法上,添加 @EventListener 注解,并设置监听的事件为 UserRegisterEvent。这是另一种使用方式!

3.6 DemoController

创建 DemoController 类,提供 /demo/register 注册接口。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Java复制代码@RestController
@RequestMapping("/demo")
public class DemoController {

@Autowired
private UserService userService;

@GetMapping("/register")
public String register(String username) {
userService.register(username);
return "success";
}

}

3.7 DemoApplication

创建 DemoApplication 类,应用启动类。代码如下:

1
2
3
4
5
6
7
8
9
Java复制代码@SpringBootApplication
@EnableAsync // 开启 Spring 异步的功能
public class DemoApplication {

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

}

3.8 简单测试

① 执行 DemoApplication 类,启动项目。

② 调用 http://127.0.0.1:8080/demo/register?username=yudaoyuanma 接口,进行注册。IDEA 控制台打印日志如下:

1
2
3
4
5
6
Java复制代码# UserService 发布 UserRegisterEvent 事件
2020-04-06 13:09:39.145 INFO 18615 --- [nio-8080-exec-1] c.i.s.l.eventdemo.service.UserService : [register][执行用户(yudaoyuanma) 的注册逻辑]
# CouponService 监听处理该事件
2020-04-06 13:09:39.147 INFO 18615 --- [nio-8080-exec-1] c.i.s.l.eventdemo.service.CouponService : [addCoupon][给用户(yudaoyuanma) 发放优惠劵]
# EmailService 监听处理该事件
2020-04-06 13:09:39.154 INFO 18615 --- [ task-1] c.i.s.l.eventdemo.service.EmailService : [onApplicationEvent][给用户(yudaoyuanma) 发送邮件]
  1. Spring 内置事件

在 Spring 框架中,自定义了非常多的自定义事件,让我们更容易的进行拓展。下面,我们来简单举一些例子。

4.1 ApplicationContextEvent

ApplicationContextEvent 是 Spring Context 相关的事件基类,如下图所示:

友情提示:Spring Context 可以简单理解成 IoC 容器。

ApplicationContextEvent 类图

  • ContextStartedEvent:Spring Context 启动完成事件。
  • ContextStoppedEvent:Spring Context 停止完成事件。
  • ContextClosedEvent:Spring Context 停止开始事件。
  • ContextRefreshedEvent:Spring Context 初始化或刷新完成事件。

也就是说,在 Spring Context 的整个生命周期中,会发布相应的 ApplicationContextEvent 事件。

SpringApplicationEvent 是 Spring Boot Application(应用)相关的事件基类,如下图所示:

SpringApplicationEvent 类图

  • ApplicationStartingEvent:Application 启动开始事件。
  • ApplicationEnvironmentPreparedEvent:Spring Environment 准备完成的事件。
  • ApplicationContextInitializedEvent:Spring Context 准备完成,但是 Bean Definition 未加载时的事件
  • ApplicationPreparedEvent:Spring Context 准备完成,但是未刷新时的事件。
  • ApplicationReadyEvent:Application 启动成功事件。
  • ApplicationFailedEvent:Application 启动失败事件。

也就是说,在 Application 的整个生命周期中,会发布相应的 SpringApplicationEvent 事件。

通过 ApplicationContextEvent 和 SpringApplicationEvent 事件,我们在《芋道 Spring Boot 持续交付 Jenkins 入门》文章的「3. 优雅上下线」小节中,实现了 Spring Boot + Nginx 的优雅上下线。

4.2 RouteRefreshListener

在《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》文章的「6. 基于配置中心 Nacos 实现动态路由」小节中,我们可以看到 Spring Cloud Gateway 通过监听 RefreshRoutesEvent 事件,结合 Nacos 作为配置中心,实现网关路由动态刷新的功能。

网关路由动态刷新

友情提示:Spring Cloud Zuul 也是通过监听 RoutesRefreshedEvent 事件,实现网关路由动态刷新的功能。

4.3 RefreshRemoteApplicationEvent

在《芋道 Spring Cloud 配置中心 Spring Cloud Config 入门》文章的「5. 自动配置刷新(第二弹)」小节中,我们可以看到 Spring Cloud Config Client 通过监听 RefreshRemoteApplicationEvent 事件,结合 RabbitMQ 作为 Spring Cloud Bus 消息总线,实现本地配置刷新的功能。

Spring Cloud Config

  1. 彩蛋

至此,我们已经完成了对 Spring 事件机制的学习。当然,还有一些功能,胖友可以自己在倒腾倒腾。

① 如果胖友想要多个监听器按照指定顺序执行,可以通过实现 Ordered 接口,指定其顺序。

② 如果胖友想要监听多种 ApplicationContext 事件,可以实现 SmartApplicationListener 接口,具体示例可以看看 SourceFilteringListener 类。

③ @TransactionalEventListener 注解,可以声明在当前事务“结束”时,执行相应的监听逻辑。

④ 可以通过实现 ApplicationEventMulticaster 接口,定义自定义的事件广播器,可以往里面添加和移除监听器,并发布事件给注册在其中的监听器。使用比较少,基本可以忽略。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

不想写throws? 使用SneakcyThrows偷偷抛

发表于 2021-04-06

前言

Java编译中的异常主要分为两类:

  1. 受查异常,是指编译器要求必须处理的异常,否则编译不通过。

比如IOException及其子类,ClassNotFoundException、FileNotFoundException等。
2. 非受查异常,是指编译器不强制要求处理的异常,可能会在运行时抛出。

比如NullPointerException、ArrayIndexOutOfBoundsException等。

场景

受查异常的设计目的是为了提醒开发者这里会发生异常,必须显示处理这些异常。

比如下面的new String()方法会抛出IO异常,我们要么使用try catch处理,要么就抛出异常。

1
2
3
4
java复制代码// 抛出异常会使代码变长,好处是能明显看出异常类型
public String utf8ToString(byte[] bytes) throws UnsupportedEncodingException {
return new String(bytes, "UTF-8");
}

牛逼的地方来了,我们可以使用@SneakcyThrows来代替上面抛出异常的形式:

1
2
3
4
5
java复制代码// 干净清爽
@SneakyThrows
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}

所以,@SneakyThrows可用于在不声明throws的情况下偷偷地抛出已检查的异常。

注意!Lombok生成的代码并不会忽略,包装,替换或以其他方式修改引发的检查异常,该抛出什么还是会抛出什么。它只是骗过了编译器,这样javac就不会报错了。

原理

为什么@SneakyThrows可以骗过编译器?
实际上,编译过后的类长这样:

1
2
3
4
5
6
7
java复制代码public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}

我们看一下Lombok.sneakyThrow(e)方法:

1
2
3
4
5
6
7
8
9
10
java复制代码// JVM实际上并不了解或关心 "检查异常 "的概念
// 这个方法所做的只是将抛出一个检查异常的行为隐藏在java编译器中
public static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
return Lombok.<RuntimeException>sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T)t;
}

这个方法的核心逻辑是throw (T)t,利用泛型将父类型Throwable转换为子类型RuntimeException。

虽然事实上它不是RuntimeException,这样写只是为了骗过javac编译器,由于泛型擦除,实际并没有转换类型,而且JVM压根不关心这个,抛异常就是抛异常,不区分受查/非受查。

总结

@SneakyThrows不是万金油,应当谨慎使用。

Lombok一直以来都备受争议,有人觉得自动生成代码很方便,有人觉得是一种破坏。

个人认为只要开发团队达成共识,Lombok可以一定程度上提升代码可读性和开发效率。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

有哪些可以提高代码质量的书籍推荐?

发表于 2021-04-06

这篇文章的内容其实很早就写了,并且,我也已经同步在了我的 Github 的一个仓库中(仓库内容还在继续完善中),地址:github.com/CodingDocs/… 。对应的 Gitee地址:gitee.com/SnailClimb/… (Github无法访问或者访问速度比较慢的小伙伴可以看码云上的对应内容)。

考虑到还未发过类似的文章,所以,今天晚上就来一篇!下面推荐都是我看过并且我觉得值得推荐的书籍。

不过,这些书籍都比较偏理论,只能帮助你建立一个写优秀代码的意识标准。如果你想要编写更高质量的代码、更高质量的软件,还是应该多去看优秀的源码,多去学习优秀的代码实践(比如设计模式、设计原则)

代码整洁之道

《重构》

必看书籍!无需多言。编程书籍领域的瑰宝。

世界顶级、国宝级别的 Martin Fowler 的书籍,可以说是软件开发领域最经典的几本书之一。目前已经出了第二版。

这是一本值得你看很多遍的书籍。

《Clean Code》

《Clean Code》是 Bob 大叔的一本经典著作,强烈建议小伙伴们一定要看看。

Bob 大叔将自己对整洁代码的理解浓缩在了这本书中,真可谓是对后生的一大馈赠。

《代码大全》

其实,《代码大全(第 2 版)》这本书我本身是不太想推荐给大家了。但是,看在它的豆瓣评分这么高的份上,还是拿出来说说吧!

这也是一本非常经典的书籍,第二版对第一版进行了重写。

我简单地浏览过全书的内容,感觉内容总体比较虚,对于大部分程序员的作用其实不大。如果你想要切实地提高自己的代码质量,《Clean Code》和 《编写可读代码的艺术》我觉得都要比《代码大全》这本书更好。

不过,最重要的还是要多看优秀的源码,多学习优秀的代码实践。

《编写可读代码的艺术》

《编写可读代码的艺术》这本书要表达的意思和《Clean Code》很像,你看它俩的目录就可以看出来了。

在我看来,如果你看过 《Clean Code》 的话,就不需要再看这本书了。当然,如果你有时间和精力,也可以快速过一遍。

另外,我这里还要推荐一个叫做 write-readable-code 的仓库。这个仓库的作者免费分享了一系列基于《编写可读代码的艺术》这本书的视频。这一系列视频会基于 Java 语言来教你如何优化咱们的代码。

在实践中学习的效果肯定会更好!推荐小伙伴们都抓紧学起来啊!

《Effective java 》

Effective Java中文版(第3版)

Java 程序员必看!

又是一本 Java 领域国宝级别的书,非常经典。这本书主要介绍了在 Java 编程中很多极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。这篇文章能够非常实际地帮助你写出更加清晰、健壮和高效的代码。本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。

程序员职业素养

《 The Clean Coder》

《 The Clean Coder》是 Bob 大叔的又一经典著作。

《Clean Code》和《 The Clean Coder》这两本书在国内都翻译为 《代码整洁之道》,我觉得这个翻译还是不够优雅的。

另外,两者的内容差异也很大。《Clean Code》这本书从代码层面来讲解如何提高自己的代码质量。而《The Clean Coder》这本书则是从如何成为一名更优秀的开发者的角度来写的,比如这书会教你如何在自己的领域更专业、如何说不、如何做时间管理、如何处理压力等等。

架构整洁之道

《架构整洁之道》

你没看错,《架构整洁之道》这本书又是 Bob 大叔的经典之作。

这本书我强烈安利!认真读完之后,我保证你对编程本质、编程语言的本质、软件设计、架构设计可以有进一步的认识。

国内的很多书籍和专栏都借鉴了《架构整洁之道》 这本书。毫不夸张地说,《架构整洁之道》就是架构领域最经典的书籍之一。

正如作者说的那样:

如果深入研究计算机编程的本质,我们就会发现这 50 年来,计算机编程基本没有什么大的变化。编程语言稍微进步了一点,工具的质量大大提升了,但是计算机程序的基本构造没有什么变化。

虽然我们有了新的编程语言、新的编程框架、新的编程范式,但是软件架构的规则仍然和 1946 年阿兰·图灵写下第一行机器代码的时候一样。

这本书就是为了把这些永恒不变的软件架构规则展现出来。

项目管理

《人月神话》

这本书主要描述了软件开发的基本定律:一个需要 10 天才能干完的活,不可能让 10 个人在 1 天干完!

看书名的第一眼,感觉不像是技术类的书籍。但是,就是这样一个看似和编程不沾边的书名,却成了编程领域长久相传的经典。

这本书对于现代软件尤其是复杂软件的开发的规范化有深刻的意义。

《领域驱动设计:软件核心复杂性应对之道》

这本领域驱动设计方面的经典之作一直被各种推荐,但是我还来及读。

软件质量其他书籍推荐

  • 《代码的未来》 :这本书的作者是 Ruby 之父松本行弘,算是一本年代比较久远的书籍(13 年出版),不过,还是非常值得一读。这本书的内容主要介绍是编程/编程语言的本质。我个人还是比较喜欢松本行弘的文字风格,并且,你看他的文章也确实能够有所收获。
  • 《深入浅出设计模式》 : 比较有趣的风格,适合设计模式入门。
  • 《软件架构设计:大型网站技术架构与业务架构融合之道》 : 内容非常全面。适合面试前突击一些比较重要的理论知识,也适合拿来扩充/完善自己的技术广度。
  • 《微服务架构设计模式》 :这本书是世界十大软件架构师之一、微服务架构先驱 Chris Richardson 亲笔撰写,豆瓣评分 9.6。示例代码使用 Java 语言和 Spring 框架。帮助你设计、实现、测试和部署基于微服务的应用程序。

最后再推荐两个相关的文档:

  • 阿里巴巴 Java 开发手册 :github.com/alibaba/p3c
  • Google Java 编程风格指南: www.hawstein.com/posts/googl…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Java之synchronized和Lock区别

发表于 2021-04-06

一、synchronized和Lock区别

1.存在层次

  • synchronized是Java一个关键字
  • Lock是一个接口

2.锁的释放

  • synchronized:1)获取锁的线程执行完同步代码,释放锁;2)线程执行发生异常,jvm让线程释放锁。
  • Lock:必须在finally中释放锁,不然容易造成线程死锁。

3.是否响应中断

  • synchronized只能等待锁的释放,不能响应中断
  • Lock等待锁的过程中可以用interrupt来中断等待,当一个线程获取了锁之后,是不会被interrupt()方法中断的。单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。

4.获取锁的状态

  • synchronized不能获取锁的状态
  • Lock可以通过trylock判断是否获取锁

5.性能

  • 资源竞争不激烈:synchronized性能稍微优于ReetrantLock
  • 资源竞争非常激烈时:Lock性能远远优于synchronized,Lock可以提高多个线程进行读操作的效率(通过readWriteLock实现读写分离)

6.调度机制

  • synchronized:通过Object对象本身的wait、notify、notifyAll实现调度
  • Lock:使用Condition进行线程之间的调度

7.锁的机制

  • synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。
  • Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作。

8.锁的类型

  • synchronized:可重入,不可中断,非公平
  • Lock:可重入,可中断,可公平

二、锁的类型

1.公平锁/非公平锁

  • 公平锁:指多个线程按照申请锁的顺序来获取锁。
  • 非公平锁:指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
  • ReentrantLock:通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
  • synchronized:非公平锁。

2.可重入锁

  • 可重入锁:在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
  • ReentrantLock和synchronized都是可重入锁。可重入锁的一个好处是可一定程度避免死锁。

3.独享锁/共享锁

  • 独享锁:该锁一次只能被一个线程所持有。
  • 共享锁:该锁可被多个线程所持有。
  • ReentrantLock和synchronized:独享锁。
  • ReadWriteLock:读锁共享,保证并发读高效;写锁独享锁,独享锁与共享锁是通过AQS来实现的

4.互斥锁/读写锁

  • 独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
  • 互斥锁:ReentrantLock
  • 读写锁:ReadWriteLock

5.乐观锁/悲观锁

乐观锁与悲观锁不是指具体的锁类型,而是指看待并发同步的角度。

  • 悲观锁:认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。适用于写多的场景。
  • 乐观锁:则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。适用于读操作多的场景。
  • 悲观锁:在Java中的使用,就是利用各种锁。
  • 乐观锁:在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

6.分段锁

  • 分段锁:是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
  • ConcurrentHashMap中的分段锁称为Segment,类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承了ReentrantLock)。put操作时,对segment加锁。
  • 在统计size的时候,需要获取hashmap全局信息,就需要获取所有的分段锁才能统计。

7.偏向锁/轻量级锁/重量级锁

  • 这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
  • 偏向锁:指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。(一个线程)
  • 轻量级锁:指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。(多个线程)
  • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。(自旋一定次数没获取锁)

8.自旋锁

自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Java-queue(队列)与deque(双端队列)

发表于 2021-04-06

Queue:

队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。

LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。

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
arduino复制代码import java.util.LinkedList;
import java.util.Queue;

public class Main {
public static void main(String[] args) {
//add()和remove()方法在失败的时候会抛出异常(不推荐)
Queue<String> queue = new LinkedList<String>();
//添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("poll="+queue.poll()); //返回第一个元素,并在队列中删除
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("element="+queue.element()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("peek="+queue.peek()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
}
}

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接 口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

下表显示了jdk1.5中的阻塞队列的操作:

add

增加一个元索

如果队列已满,则抛出一个IIIegaISlabEepeplian异常

remove

移除并返回队列头部的元素

如果队列为空,则抛出一个NoSuchElementException异常

element

返回队列头部的元素

如果队列为空,则抛出一个NoSuchElementException异常

offer

添加一个元素并返回true

如果队列已满,则返回false

poll

移除并返问队列头部的元素

如果队列为空,则返回null

peek

返回队列头部的元素

如果队列为空,则返回null

put

添加一个元素

如果队列满,则阻塞

take

移除并返回队列头部的元素

如果队列为空,则阻塞

Deque:

deque(也称为双端队列)是与队列类似的项的有序集合。它有两个端部,首部和尾部,并且项在集合中保持不变。deque 不同的地方是添加和删除项是非限制性的。可以在前面或后面添加新项。同样,可以从任一端移除现有项。如下展示了一个 Python 数据对象的 deque 。

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
scss复制代码from python_basic_deque.deque_definition import Deque

# 新建一个deque队列
d = Deque()

# 判断队列是否为空
print(d.isEmpty())

# 在队头添加元素1,并查看该队列
d.addFront(1)
print(d.list1())

# 在队头添加元素2,并查看该队列
d.addFront(2)
print(d.list1())

# 在队头添加元素3,并查看该队列
d.addFront(3)
print(d.list1())

# 在队尾添加元素4,并查看该队列
d.addRear(4)
print(d.list1())

# 在队尾添加元素5,并查看该队列
d.addRear(5)
print(d.list1())

# 队列大小为,判断是否为空
print(d.size())
print(d.isEmpty())

# 移除队头
d.removeFront()
print(d.list1())

# 移除队尾
d.removeRear()
print(d.list1())
//by dongge-destiny

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

使用Jenkins配置SpringBoot的自由成长之路

发表于 2021-04-06

一、相关阅读

1、使用Jenkins配置Git+Maven的自动化构建

blog.csdn.net/xlgen157387…

2、Jenkins部署Maven多环境项目(dev、beta、prod)的参数设置

3、使用Generic Webhook Trigger插件实现Jenkins+WebHooks(码云)持续集成

4、使用Generic Webhook Trigger插件实现Jenkins+WebHooks(码云)持续集成–指定具体分支集成

二、项目结构

使用Jenkins配置SpringBoot的自由成长之路

这是一个SpringBoot项目,码云地址为:gitee.com/xuliugen/uf…

这里假设你已经配置好了Jenkins的环境,如果还没有配置的话可以参考上文中的相关阅读进行配置。

三、配置Jenkins

1、新建Job

使用Jenkins配置SpringBoot的自由成长之路

这里是因为我已经新建了一个相同名字的所以会报已经存在,忽略即可!

2、设置具体内容

使用Jenkins配置SpringBoot的自由成长之路

这里指定项目名称和描述,以及源码管理中的Git地址和用户名密码。

使用Jenkins配置SpringBoot的自由成长之路

Build指定源代码拉下来之后需要做的事情,Post Steps指定编译好之后需要做的事情。

其他没有截图的默认即可!

3、具体分析:

(1)Build是当把代码拉下来之后需要做的操作,由于是一个maven项目,因此我们需要指定编译打包的命令,这里是:

1
ini复制代码clean package -Dmaven.test.skip=true

注意这里没有mvn,因为他是默认使用maven编译的!完整的命令是:

1
ini复制代码mvn clean package -Dmaven.test.skip=true

其中:-Dmaven.test.skip=true 是跳过测试。

(2)关键是Post Steps的脚本,这里详细的解释:

首先,我们应该要明白Jenkins的原理,他是这样的,如果我们在新建的时候指定了Maven项目和代码的Git地址,Jenkins首先会通过Git将代码clone到本地,然后执行在Build中指定的pom.xml文件和指定的命令。

下边是Jenkins的工作区间详细信息(默认位置是:~/.jenkins):

使用Jenkins配置SpringBoot的自由成长之路

这里的workspace就是我们创建的任务的工作区间:

使用Jenkins配置SpringBoot的自由成长之路

可以看到就有我们上述创建的ufind-server,如下:

使用Jenkins配置SpringBoot的自由成长之路

最后的编译好的jar的位置就是:

1
arduino复制代码/home/xuliugen/.jenkins/workspace/ufind-server/ufind-web/target

然后,我们可以根据需要将编译好的jar移到另外一个位置,然后启动即可,在启动的时候是后台启动,不然的话他的日志会一直显示在Jenkins的任务界面(有兴趣的可以试一下效果!),后台启动的时候需要把进程ID记录到一个文件中,这里是:ufind-web.pid

因此,上图中的脚本的完整解释如下:

使用Jenkins配置SpringBoot的自由成长之路

这里需要注意的是设置export BUILD_ID=dontKillMe,另一个是每次启动的时候先要杀掉以前的进程,不然的话不会启动还会报错!

可以看出,只要明白了Jenkins的工作机制,尽管目前Jenkins上SpringBoot相关的插件还没有,但是我们可以一步步的通过脚本的方式进行运行!

四、运行结果

使用Jenkins配置SpringBoot的自由成长之路

使用Jenkins配置SpringBoot的自由成长之路

通过游览器访问服务是否可以正确的运行!

五、将编译好的jar文件传到另一个服务器

上述中我们只是通过cp命令将编译好的jar移动到同一个服务器中的另外一个位置,正常情况肯定不会是这样的,他应该是其他服务器上指定的位置,并且服务器的数量可能还不止一个。因此,我们下边研究一下!

将一个文件从一个服务器移动到另一个服务器使用的是scp命令,例如:

使用Jenkins配置SpringBoot的自由成长之路

scp是一个基于ssh的Linux环境下传输文件的好工具,但是使用shell脚本调用scp时会面临一个问题,即scp强制要求通过交互方式输入密码,而不像mysql等拥有-u -p选项。

下面有两种方法帮助shell脚本跨过输入密码这个障碍!

1、建立机器间完全信任关系

假设需要从机器A传输文件至机器B

(1)在机器A上运行

1
复制代码ssh-keygen -t rsa

上述命令会在~/.ssh/目录生成私钥证书id_rsa和公钥证书id_rsa.pub;

(2)将公钥证书id_rsa.pub复制到机器B的用户根目录的.ssh子目录中,再将文件内容append到文件authorized_keys中。

其实只要用一条单行命令就可以完成步骤2,它被commandlinefu.com的用户投票选为十大最酷的Linux单行命令之一:

1
css复制代码ssh-copy-id [-i [identity_file]] [user@]machine

identity_file是公钥证书的路径,默认情况下是~/.ssh/id_rsa.pub.

如果要建立双方向的完全信任关系,还要从机器B到机器A再重复一遍上面的操作。

不过这样的方法并不完美,一是运维成本太高,二是机器间的安全屏障完全消失,安全代价太大,所以本人强烈推荐第二种方法。

2、expect脚本

expect脚本是一种建立在tcl基础上的脚本语言,曝光率不高,却堪称shell脚本的好基友。expect脚本为交互而生,被设计为专门针对交互式程序的工具,常与对telnet、ftp、fsck、rlogin、tip、scp等配合使用。使用之前要先安装expect,安装过程(Ubuntu Server):

使用Jenkins配置SpringBoot的自由成长之路

Expect中最关键的四个命令是send、expect、spawn、interact。

1
kotlin复制代码send:用于向进程发送字符串expect:从进程接收字符串spawn:启动新的进程interact:允许用户交互

示例代码例如:

使用Jenkins配置SpringBoot的自由成长之路

运行结果:

使用Jenkins配置SpringBoot的自由成长之路

可以发现文件已经上传成功!

上面是一个独立的expect脚本文件,如果像把这段脚本嵌入其它shell脚本中就要用到expect -c

使用Jenkins配置SpringBoot的自由成长之路

使用Jenkins配置SpringBoot的自由成长之路

使用Jenkins配置SpringBoot的自由成长之路

简单测试之后,那我们的脚本应该改成如下方式:

使用Jenkins配置SpringBoot的自由成长之路

这里执行了远程主机192.168.1.241上的一个脚本startup.sh,脚本如下:

使用Jenkins配置SpringBoot的自由成长之路

为什么,执行这个远程脚本而不是直接通过expect执行哪?哈哈,你可能没有想到,这是因为本人能力有限,多次尝试使用expect执行都没有成功,所以,不得已才使用这么愚蠢的方式!哈哈,不要打我!

看执行的结果:

使用Jenkins配置SpringBoot的自由成长之路

使用Jenkins配置SpringBoot的自由成长之路

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

IDEA 常用快捷键

发表于 2021-04-06

Ctrl 快捷键

  • Ctrl + F 在当前文件中查找
  • Ctrl + H 查看当前类的继承关系列表
  • Ctrl + F12 查看当前类的结构列表
  • Ctrl + Y 删除当前光标所在的代码行
  • Ctrl + P 方法参数提示
  • Ctrl + Q 展示注释文档
  • Ctrl + Shift + V 复制/剪切的历史记录

Alt 快捷键

  • Alt + Up/Down 跳转到当前文件的前一个/后一个方法名位置
  • Alt + Insert 自动生成代码
  • Alt + Enter 快速修复

Ctrl + Alt 快捷键

  • Ctrl + Alt + L 格式化代码
  • Ctrl + Alt + O 优化导入的类
  • Ctrl + Alt + Left/Right 跳转到上次光标所在位置
  • Ctrl + Alt + H 查看当前光标所处方法的被调用链

Ctrl + Shift 快捷键

  • Ctrl + Shift + U 选中内容大小写转换
  • Ctrl + Shift + ]/[ 选中当前光标至其所在代码段的起始/结束位置

Alt + Shift 快捷键

  • Alt + Shift + C 查看项目文件的最近改变

Shift 快捷键

  • Shift + Enter 另起一行
  • Shift + F6 重命名

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

一种方便的golang调用python库的方式

发表于 2021-04-06

由于一些原因,python库不方便使用golang重构实现,因此我们可以通过go调用python库的方式来达到实现需求的目的

  • 通过shell命令组装的方式调用python

主要使用到了go的os/exec包,以及python的-c参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
go复制代码func main(point int) {
var err error
var cmd *exec.Cmd
var datas []byte
py := "python"
flag := "-c"
importmodule := "import sys"
Target := "print(sys.path)"
params := fmt.Sprintf("%s;%s", importmodule, Target)
fmt.Println(params)
cmd = exec.Command(py, flag, params)
if datas, err = cmd.Output(); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(string(datas))

}
}

同样的,在使用其他自写python库时,先将python代码路径加入到sys.path路径下,再进行调用即可。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…690691692…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%