书接上文 Reactive编程 ,我们已经了解了基础的API,现在我们开始编写实际的应用。Reactive对并发编程进行了很好的抽象,也有很多底层的特性需要我们去关注。当使用这些特性时,我们可以对之前隐藏在容器、平台、框架中的细节进行控制。

Spring MVC由阻塞转向Reactive

Reactive要求我们以不同的思路来看待问题。区别于传统的request->response模式,所有的数据都是发布为一个序列 (Publisher) 然后进行订阅(Subscriber)。区别于同步等待返回结果,改为注册一个回调。只要我们习惯了这种方式,就不会觉得很复杂。但是没办法让整个环境都同时变为Reactive模式,所以避免不了要跟老式的阻塞API打交道。

假设我们有一个返回HttpStatus的阻塞方法:

private RestTemplate restTemplate = new RestTemplate();

private HttpStatus block(int value) {
    return this.restTemplate.getForEntity("http://example.com/{value}", String.class, value)
            .getStatusCode();
}
Continue Reading ...

书接上文 Reactive编程 ,我们继续用真实的代码来解释一些概念。我们会更进一步理解Reactive的与众不同以及它的功能。这些例子很抽象,但能够让我们更进一步理解用到的API和编程风格,真实的感受它的与众不同。我们将会看到Reactive的核心元素,学习如何控制数据流,如果需要的话还会用到后台线程进行处理。

建立项目

我们用Reactor库来进行演示。当然也可以用其它的工具。如果不想拷贝黏贴代码,可以直接使用 github 上的示例项目。

通过 https://start.spring.io 来建立一个空的项目并添加Reactor Core依赖。

可以使用Maven:

<dependency>
  <groupId>io.projectreactor</groupId>
  <artifactId>reactor-core</artifactId>
  <version>3.0.0.RC2</version>
</dependency>

也可以使用Gradle:

compile 'io.projectreactor:reactor-core:3.0.0.RC2'

工作原理

Reactive由一系列事件以及发布和订阅这些事件的2个参与方组成的一个序列。我们也可以称之为stream。如果需要,我们使用streams这个名词,但是java8有一个java.util.Stream库,与我们在这儿要讲的概念是不同的,不要将这2个概念混淆。我们尽量集中阐述publisher和subscriber(Reactive Streams的行为)。

我们会使用Reactor库,把publisher称为 Flux(实现了Reactive Streams的Publisher接口),在RxJava库中的名称是Observable ,代表的是类似的概念。(Reactor2.0中的名称为Stream,很容易跟Java 8 的 Streams混淆,因此我们只使用Reactor 3.0中的新定义)。

Continue Reading ...

Reactive编程很有趣,现在也有各种各样的讨论,概念上不是很容易理解。本文会以具体的形式介绍相关的概念。Reactive编程跟并发和高性能在概念上有一些重合,但在原理上完全不同。Reactive编程跟函数式编程是非常类似的。一些人认为Reactive编程并不是什么新概念,他们在日常工作中经常使用(例如javascript)。另一些人认为这是微软做出的新发明(Reactive这个名字最早来源于C#)。在java编程中最近也有了类似的技术(参见Reactive Streams initiative)。在何时和何处使用Reactive编程这个问题上很容易犯错误。

1.是什么?

Reactive编程是一种通过将智能路由和事件消费组合起来改变行为的微架构风格。这个定义有些抽象,你在网上会接触到其它的各种各样的定义。 Reactive编程的概念可以追溯到1970年或者更早,因此并不是什么新鲜概念,但是因为有了微服务和多核处理器,在现在的企业应用中有了新的用法。 下面有几个简单明了的解释:

Reactive编程背后的基本思想是指表示随时间变化的值的特定数据类型。处理这些随时间变化的值的方法里本身也包含着随时间变化的值。

以及

可以简单的把程序当作是一个电子表格,把变量当作是表格里的单元格。当表格里的某些单元格的值变化后,如果其它的单元格引用了这些单元格的值,那么其它的单元格的值也会跟着改变。这跟函数式编程是一样的。

函数式编程往往代表着高性能、并发、异步、非阻塞IO。开始时我们可以不必非用函数式编程,Reactive模型可以很自然的处理这些问题。我们真正关心的是解决这些问题的实现。我们可以用同步和单线程的方式来实现一个有效的函数式编程框架,但是这么做并没什么意义。

2. Reactive编程的应用场景

类似于“有什么好处?”这样的问题对于新手来说是很难回答的。有几个例子来解释通用的适用场景:

Continue Reading ...

1.概述

将通用的逻辑用AOP技术实现可以极大的简化程序的编写,例如验签、鉴权等。Spring的声明式事务也是通过AOP技术实现的。

具体的代码参照 示例项目 https://github.com/qihaiyan/springcamp/tree/master/spring-aop

Spring的AOP技术主要有4个核心概念:

  1. Pointcut: 切点,用于定义哪个方法会被拦截,例如 execution(* cn.springcamp.springaop.service.*.*(..))

  2. Advice: 拦截到方法后要执行的动作

  3. Aspect: 切面,把Pointcut和Advice组合在一起形成一个切面

  4. Join Point: 在执行时Pointcut的一个实例

  5. Weaver: 实现AOP的框架,例如 AspectJ 或 Spring AOP

2. 切点定义

常用的Pointcut定义有 execution 和 @annotation 两种。execution 定义对方法无侵入,用于实现比较通用的切面。@annotation 可以作为注解加到特定的方法上,例如Spring的Transaction注解。

execution切点定义应该放在一个公共的类中,集中管理切点定义。

示例:

public class CommonJoinPointConfig {
    @Pointcut("execution(* cn.springcamp.springaop.service.*.*(..))")
    public void serviceLayerExecution() {}
}

这样在具体的Aspect类中可以通过 CommonJoinPointConfig.serviceLayerExecution()来引用切点。

public class BeforeAspect {
    @Before("CommonJoinPointConfig.serviceLayerExecution()")
    ...
}

当切点需要改变时,只需修改CommonJoinPointConfig类即可,不用修改每个Aspect类。

Continue Reading ...

虽然jvm有垃圾回收机制,如果程序编写不注意某些特定规则,仍然会导致java程序内存泄漏,最终可能出现OutOfMemory异常。

1.Java内存泄漏的原因

java中的对象从使用上分为2种类型,被引用(referenced)的和不被引用(unreferenced)的。垃圾回收只会回收不被引用的对象。被引用的对象,即使已经不再使用了,也不会被回收。因此如果程序中有大量的被引用的无用对象时,就是出现内存泄漏。

2.java堆内存(Heap)泄漏

jvm堆内存的大小是通过 -Xms 和 -Xmx两个参数指定的。

2.1 对象被静态成员引用

当大对象被静态成员引用时,会造成内存泄漏。

示例:

private Random random = new Random();

public static final ArrayList<Double> list = new ArrayList<Double>(1000000);

for (int i = 0; i < 1000000; i++) { list.add(random.nextDouble()); }

ArrayList是在堆上动态分配的对象,正常情况下使用完毕后,会被gc回收,但是在此示例中,由于被静态成员list引用,而静态成员是不会被回收的,所以会导致这个很大的ArrayList一直停留在堆内存中。

因此需要特别注意静态成员的使用方式,避免静态成员引用大对象或集合类型的对象(如ArrayList等)。

Continue Reading ...