0%

工作中大部分Java项目都是基于maven来进行项目的构建等工作,但是对于maven这个高频用到的工具其实了解程度还不够,下面主要是学习《Maven实战》中记录的关于生命周期与插件相关的笔记

maven生命周期与插件

maven的生命周期是从大量项目和构建工作中总结抽象出来的对所有构建过程进行的抽象和统一,这个生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤

虽然maven抽象出了生命周期的概念,但是它并没有对相关的功能进行实现,就像它是负责定义了接口,但是具体实现是由插件来完成,这样可以很好的保证自身的轻量和扩展性

阅读全文 »

如果我们有想固定间隔时间执行的任务等,自己实现的一种方式是可以新启动一个线程,在其中sleep固定的时间后执行,但是这种方式在任务多的时候肯定是不行的。现在已经有很多现成的工具我们可以直接使用,这里主要介绍一下JDK的ScheduledThreadPoolExecutor与Netty的HashedWheelTimer,看一下它们的实现原理

阅读全文 »

之前简单介绍过 ThreadLocal,但是其中有个问题就是当一个请求中使用到线程池时,无法将主线程中ThreadLocal中的值传递进去,这次我们就看下怎么解决这个问题

比较直接的的方法就是包装一下Runnable或Callable,在创建的时候将主线程中ThreadLocal对应内容传递保存进去,之后执行的时候再取出来重新赋值到对应ThreadLocal中,使用之后再清理掉即可,大致样子如下

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
public class SimpleThreadLocalTest {
// 创建一个线程池及姓名的ThreadLocal
private Executor executor = Executors.newSingleThreadExecutor();
public static ThreadLocal<String> USER_NAME_THREAD_LOCAL = new ThreadLocal<>();

@Test
public void test() {
USER_NAME_THREAD_LOCAL.set("zheng");
executor.execute(new ThreadLocalRunnable());
}

static class ThreadLocalRunnable implements Runnable {
private String userName;
// 自己定义一个Runnable实现,在创建的时候,记录主线程在ThreadLocal中设置的值
public ThreadLocalRunnable() {
this.userName = USER_NAME_THREAD_LOCAL.get();
}

@Override
public void run() {
try {
// 在线程池中执行时,重新设置值到ThreadLocal中供后续业务逻辑使用
USER_NAME_THREAD_LOCAL.set(userName);
// todo 业务逻辑
System.out.println("userName: " + USER_NAME_THREAD_LOCAL.get());
} finally {
// 使用后进行清理
USER_NAME_THREAD_LOCAL.remove();
}
}
}
}

这里很明显可以看出来,自定义的Runnable实现与系统中定义的ThreadLocal进行了强耦合,当有更多的ThreadLocal时会使代码很难维护,比较幸运的是,这种工具已经有了比较好的开源实现,这里就介绍下transmittable-thread-local

阅读全文 »

Java注解大家都了解,平时我们使用最多的就是在运行时也有效的注解:@Retention(RetentionPolicy.RUNTIME),可以根据这类注解在运行时进行一些特殊的逻辑处理,如Spring中的AOP使用。但是除了这类在运行时存在的注解,还有两种会保留到源码@Retention(RetentionPolicy.SOURCE)和字节码@Retention(RetentionPolicy.CLASS)中的注解,这种注解有什么作用呢?

这里介绍一种比较常见的用法-Java注解处理器(Java Annotation Process),它可以在运行时获取注解信息,生成一些额外的文件信息,如我们常用的lombokmapstruct都是使用这种技术

阅读全文 »

我们在日常写代码的过程中,经常会有一个扩展点接口,同时会有多种实现,类似策略模式,在运行时动态获取具体的实现

如果想在不需要重新部署项目的情况下,新增一种扩展点的实现并且能够生效使用,有什么方式呢?

先想个简单的例子来说明一下上面说的场景,比如价格计算

1
2
3
interface PriceCalculater {
BigDecimal calc(GoodsDetail detail);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class DirectReducePriceCalculator implements PriceCalculater {

@Resource
private ActivityService activityService;

public BigDecimal calc(GoodsDetail goods) {
// 随便写的,只是为了稍微贴近一点实际~~~~
Activity activity = activityService.getDirectReductActivity(goods);
if (activity != null) {
return detail.getPrice().subtract(activity.getReducePrice());
}
return detail.getPrice();
}
}

并且有一个对应的工厂类,用于获取对应的计算器

阅读全文 »

我们日常开发过程中,入参出参基本都是与后端对应的类结构完全一致,SpringMVC自动帮我们处理了参数转换的过程,GET 和 POST 的几种常见传参方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// HTTP请求参数名需要与属性名userName相同
@GetMapping("/listUser")
public List<User> listUser(String userName) {
return new ArrayList<>();
}

@GetMapping("/listUser")
// HTTP请求参数名需要与注解中的名称username相同相同
public List<User> listUser1(@RequestParam("username") String userName) {
return new ArrayList<>();
}

@GetMapping("/listUser")
// 参数太多则可以使用类接收,url中的参数会自动转化赋值到SearchUserParam类中的同名属性上
public List<User> listUser2(SearchUserParam searchUserParam) {
return new ArrayList<>();
}


// POST方式基本同上,post body中的json串会被反序列化到对应的参数user类
@PostMapping("/save")
public Long save(@RequestBody User user) {
return 1L;
}

上面的几种用法基本能满足我们大部分的需求,但是仍有一些特殊情况无法满足

阅读全文 »

之前通过《状态模式》介绍了一下状态模式的使用,这次我们来介绍一下有限状态机

维基百科中是这样定义的:有限状态机(FSM)又称有限状态自动机(FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型,在任何给定时间都可以恰好处于有限数量的状态之一

其实状态模式也可以算是状态机的一种实现,除了状态模式,还有一种实现方式就是创建一个描绘迁移的数据表,该表被一个处理事件的处理引擎解释。引擎查找与事件匹配的迁移,调用响应的动作并更改状态,这样状态机的逻辑全部集中在了一个地方

所以表驱动的状态机和状态模式的主要区别就是:状态模式对于状态相关的行为进行建模,而表驱动的方法着重于定义状态转换

阅读全文 »

单元测试是对我们写的代码的最小单元进行测试,一般来说就是对函数(方法)进行测试,大家都知道写单元测试的好处,但是具体要怎么写呢?被测试的类可能依赖外部的类或服务,这个依赖的外部接口如何Mock,依赖注入的类如何替换成自己Mock的类等等,下面就介绍一种常用的Mock方式

阅读全文 »

平时我们写代码一般使用Git等工具来进行版本管理,而Flyway可以理解为是用来进行数据库的版本管理的,它的功能是比较强大的,可以帮忙我们自动执行SQL,确保各个环境(开发、测试等)数据表的同步等,详细内容可以参考文档,这里简单介绍一下常用的使用方法

基本maven使用

引入依赖

1
2
3
4
5
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>7.0.0</version>
</dependency>

创建SQL脚本并执行

在src/resources下创建 db/migration目录,在其中创建对应的SQL文件,其中又分为不同的文件类型,基于约定由于配置的原则,通过文件命名方式进行区分

flyway

版本迁移以V开头,只会执行一次;回退迁移以U开头,一般不使用;可重复执行迁移以R开头,每次修改后都会重新执行

阅读全文 »

在我们日常开发中经常有这种类型的场景:

  1. 新建用户后,需要进行一些操作,如发送优惠券等(和创建用户本身无关的操作)
  2. 数据变更时,对应的展示表格等信息需要进行对应的更新

即当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

观察者模式(发布-订阅)就是这种情况下的一种解决方案,使用这种方式可以让解耦发布者和订阅者,互相不需要知道对方,之前一篇文档中简单介绍过Spring中的事件使用,这次介绍一种非Spring环境下Guava提供的EventBus的使用

阅读全文 »