0%

写代码我们都知道要抽象,要封装变化,要实现开闭原则,比如对于很多相似的功能,我们可以将通用的功能抽象出来,然后把变化的不同的地方提取出去,比如模版模式、策略模式等都是实现类似的效果

比如对于策略模式,我们通常是定义一个接口,然后有不同的实现,这种是可以的,但是如果通用流程中要扩展的点较多的话,这些不同的实现也需要管理,可以把他们合并到一个单独的包中,再进一步,我们甚至可以将包单独提取出来,支持运行时加载包实现新增功能的支持

JDK对此功能的支持就是 SPI,但是它的限制较多,也不够灵活,比如dubbo就是自己定义了一套SPI的实现,这次我们来看另一个实现,pf4j 提供一套在基本框架中定义扩展点接口,然后通过不同的插件来实现扩展点的功能,来支持对新增开放对修改关闭

这次我们就来学习一下它的使用

阅读全文 »

Thrift的关键其实就是在定义好结构后,通过某种序列化方式进行网络传输交互,一般定义结构可以使用IDL的方式来描述各个字段属性等信息,这种方式可以支持跨语言的使用。对于纯Java的应用,也可以通过在类中添加注解的方式来实现,即把IDL中需要定义的信息,通过注解和注解中的信息来进行描述,同样可以达到定义结构的目的

定义好结构之后,就可以通过对应的TProtocol将数据结构进行发送和接收的序列化和反序列化即可,这次我们主要看下TProtocol中的一个实现–TBinaryProtocol

阅读全文 »

之前介绍过thrift使用, 但是只知道使用总是不够满足,这次我们接着之前的代码,来看下这个具体的实现和处理流程,在介绍之前先看下几个重要的接口

传输层 TTransport : 负责数据传输(大部分场景为负责网络传输)

协议层 TProtocol: 负责进行数据结构的序列化与反序列化(依赖TTransport 进行数据传输及读取)

处理层 TProcessor : 使用协议和传输接口进行具体处理处理

阅读全文 »

Thrift是facebook开发的一款轻量级的跨语言的RPC实现,它为数据传输、数据序列化和应用层的处理提供了清晰的抽象和实现。它的代码生成器使用简单的定义语言来生成跨语言的代码,来构建可互操作的RPC客户端和服务端。这篇文章主要简单介绍一下thrift的使用

阅读全文 »

工作中大部分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;
}

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

阅读全文 »