Emma Wang


  • Home

  • Tags

  • Categories

  • Archives

  • Sitemap

  • record

  • rss

每日感想-2019-5-3

Posted on 2019-05-03

进来几个月,由于金钱与父母产生了巨大的分歧,我变得十分愤怒,断绝联系长达两个月。期间我曾多次想与他们联系,但又十分不甘、不情愿,一直没有行动。直到前天表姐与我联系,我心中怒火还是没有平息。想到父母没有太多收入,就汇了1000块钱给他们用作生活费,晚上父亲与我打电话,母亲向我哭诉,说十分想念我,并不是想要我的钱。我又重新愤怒、难过,一直无法排解。

我生长在山东的一个小农村,家庭情况一般,父母一直很支持我念书,我还算争气,一直读到研究生,到江苏参加工作,已经毕业几年了。父母是普通的中国式父母,努力为我和弟弟提供更好的物质生活,却从来没有关注过我们的精神生活,同时又具备了刀子嘴豆腐心,遇到问题只会责备、埋怨、自恋,“还能怎么办呢?只能这样了”,之类的话我听了无数遍。

我渐渐长大、遇到更多的人和事、了解更广阔的世界,与家里的生活越来越远,内心却充满迷茫。我不知道我要往哪里去,只知道我再也不能回到我的故乡。我更加向往自由,受不了三姑六婆的唠叨、指责和无知。我考上大学的时候,大舅妈拿她侄女的三本院校和我妈说,都是大学。

jdk 8 stream 详解

Posted on 2019-04-26 | In java

最近在做性能优化的工作,很多同事使用了jdk8提供的stream,但是并没有争取正确使用它们,反而造成性能的下降和代码的冗余,并且难以调试和跟踪。这里,为了涨到正确的用法,需要详细了解stream。

Stream介绍

1
2
public interface Stream<T>
extends BaseStream<T,Stream<T>>

据java api中的说明, stream is a sequence of elements supporting sequential and parallel aggregate operations,是支持顺序和并发操作的一组元素。

  • Sequence of elements: a stream provides an interface to a sequenced set of values of a spectific element type. However, streams don’t actually store elements; they are computed on demand.提供使用一个有序集合的接口,不存储,按需计算。

  • Source: Streams consume from a data-providing source such as collections, arrays, or I/O resources. 数据来自集合、数组或I/O。

  • Aggregate operations: Streams support SQL-like operations and common operations from functional programming languages, such as filter, map , reduce, find, match, sorted, and so on。

1
2
3
4
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();

为了执行操作,stream 操作会被组成一个stream pipeline,这个stream pipeline包含:

  1. 一个数据源(可以是数组、集合、生产者、I/O等)

  2. 零个或多个立即执行的操作(将一个stream转换为另一个stream,比如filter(Perdicate))

  3. 一个结束操作,产生一个结果或side-effect,如count()和forEach(Consumer)

    直到结束操作被初始化,stream中的数据才会被计算,源中的元素被按需消费。

集合和stream,有一些非常相似的地方,但是它们的目的是不同的。集合主要关系的是如何有效管理和获取其中的而数据,而stream不提供方法获取或操作内部的数据,只关心对源的描述和对整个源的计算操作。如果已有的stream操作不满足需求,而可以使用BaseStream.iterator()和BaseStream.spliterator()将stream转换成iterator。

一个stream pipeline,可以被看作对stream source的查询。除非source被明确声明为concurrent modification,如ConcurrentHashMap,否则如果一边查询一边修改stream,会有一些不可预料或错误的后果。

大多数stream操作接受描述用户特定行为的参数,如w->w.getWeight()作为mapToInt的参数。为了保证结果正确,这些行为参数需要满足以下条件:

  1. 不能修改stream source;
  2. 大多数情况下必须是无状态的,他们的结果不能依赖于在执行stream pipeline时会改变的状态。

这些参数大多数情况下时一个funtional interface,比如Function,或lambda表达式或方法引用。除非特别声明,这些参数不能为null。

一个stream只能被操作一次,不能同时做为两个或多个pipeline的源,可能会抛出IllegalStateException。然而,由于一些流操作可能返回其接收器而不是新的流对象,所以在所有情况下可能无法检测到重用。

使用

在工作中,大多数情况下,开发人员根据一个list或set生成一个stream并进行一些操作,如

1
list.stream.map(entity->entity.getId()).collect(toSet());

或执行一些并发操作:

1
2
3
list.parallelStream().forEach(entity-> {
/*操作entity*/
});

有时候需要异步执行并等待支持完成,这时候容易写下以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Integer[] integers = new Integer[1000];
for (int i = 0; i < integers.length; i ++){
integers[i] = Integer.valueOf(i);
}
Arrays.asList(integers).stream()
.map(integer -> {
CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
logger.info("begin to process integer:{}", integer);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
logger.error("interupt", e);
}
logger.info("end to process integer:{}", integer);
});
return completableFuture;
})
.forEach(CompletableFuture::join);

在代码中,使用map将integer进行异步处理,并返回Completable对象,在forEach中执行Completable.join。下面是日志,从日志上看,所有的数据都被顺序处理的,并没有真正被多线程处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[11:54:19:539] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:0
[11:54:19:592] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:0
[11:54:19:592] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:1
[11:54:19:643] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:1
[11:54:19:643] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:2
[11:54:19:694] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:2
[11:54:19:694] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:3
[11:54:19:745] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:3
[11:54:19:745] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:4
[11:54:19:796] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:4
[11:54:19:796] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:5
[11:54:19:846] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:5
[11:54:19:846] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:6
[11:54:19:897] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:6
[11:54:19:897] [INFO] - ForkJoinPool.commonPool-worker-1 - begin to process integer:7
[11:54:19:948] [INFO] - ForkJoinPool.commonPool-worker-1 - end to process integer:7

原因在于这是一个顺序的stream,在流被处理时,对每个一个数据执行所有的操作,如对于integer,先执行map操作,将数据放入异步线程池中处理,然后执行forEach中的CompletableFuture.join操作,所以只有当前一个数据被完全处理完,第二个数据才开始被执行。

修改代码,将CompletableFuture收集到集合中,然后对每个对象执行join,等待所有数据被处理完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Integer[] integers = new Integer[1000];
for (int i = 0; i < integers.length; i ++){
integers[i] = Integer.valueOf(i);
}
Set<CompletableFuture> completableFutureSet = Arrays.asList(integers).stream()
.map(integer -> {
CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
logger.info("begin to process integer:{}", integer);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
logger.error("interupt", e);
}
logger.info("end to process integer:{}", integer);
});
return completableFuture;
})
.collect(Collectors.toSet());
completableFutureSet.stream().forEach(CompletableFuture::join);

此时的日志显示数据被多线程处理了。代码中没有设置线程池,默认使用ForkJoinPool.commonPool,线程池的最大线程数为7。

操作

stream接口定义了很多操作,这些操作可以被分为两类:

streams-f1

  1. filter, sorted, map,可以被串联形成pipeline,这些操作被称为intermediat operations,他们的返回类型为stream。
  2. collect,结束pipeline,返回结果,被称为terminal operations,返回list, integer,甚至void,但不是stream。

intermedia operations不会执行任何处理,直到stream pipleline上被添加terminal operation。intermediate operations can usually be “merged” and processed into a single pass by the terminal operation.

1
2
3
4
5
6
7
8
9
10
11
12
13
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> twoEvenSquares =
numbers.stream()
.filter(n -> {
System.out.println("filtering " + n);
return n % 2 == 0;
})
.map(n -> {
System.out.println("mapping " + n);
return n * n;
})
.limit(2)
.collect(toList());
1
2
3
4
5
6
filtering 1
filtering 2
mapping 2
filtering 3
filtering 4
mapping 4

上例中limit(2)使用short-circuiting,因此实际运行时,只需要处理一部分流就可以返回结果。

maven-dependency

Posted on 2019-02-10

maven打包

Posted on 2019-02-10 | In maven

基本用法

一般常用的打包插件为maven-assembly-plugin,使用时可以在pom文件中添加如下配置,这里把配置文件dep.xml放在src/assembly下面,这个路径是assembly配置文件的标准路径。

其中executions中create-archive将assembly:single绑定到package阶段,因此在执行mvn package时会执行mvn assembly:single进行打包,日志中可以找到create-archive。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptor>src/assembly/dep.xml</descriptor>
</configuration>
<executions>
<execution>
<id>create-archive</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

典型的assembly配置文件如下:

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
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>bin</id>
<formats>
<format>tar.gz</format>
<format>tar.bz2</format>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>README*</include>
<include>LICENSE*</include>
<include>NOTICE*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/site</directory>
<outputDirectory>docs</outputDirectory>
</fileSet>
</fileSets>
</assembly>

配置好后,使用命令行打包:

1
mvn package

快捷配置

maven-assembly-plugin打包了四个长用的配置文件,可以使用descriptorRefs进行快捷配置。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugins>
<plugin>
<!-- NOTE: We don't need a groupId specification because the group is org.apache.maven.plugins ...which is assumed by default.
-->
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>create-archive</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>

插件中包含了四种配置:

  1. bin,打包项目的二进制版本,包含三种格式:tar.gz、tar.bz2和zip。
  2. jar-with-dependencies,打包项目和二进制版本和打包的依赖,生成文件格式为jar,jar包内所有依赖都作为class存在。只支持uber-jar(不明白)。
  3. src,将src的所有内容打包,包含三种格式:tar.gz、tar.bz2和zip。
  4. project,将打包项目中除target下的所有内容,包含三种格式:tar.gz、tar.bz2和zip。

web应用基础概念

Posted on 2019-02-10 | In web

web应用基础概念

web容器

也叫servlet容器,负责管理servlet的生命周期、将URL映射到特定的servlet,并确保URL请求者有正确的访问权限。web容器实现了Java EE体系结构的Web组件契约,此体系结构为其他web组建指定运行时环境,包括安全性、并发性、生命周期管理、事务、附属和其他服务。

其中比较常见的容器有tomcat、glassfish、jetty。

Servlet

Servlet是web容器中管理的、用于为客户端请求提供服务的类。部署web服务时,servlet由web容器进行管理。

servlet的生命周期有三个阶段,init(),service()和destroy()。它们由每个servlet实现,并在特定时间由web容器调用。

  • 在servlet 生命周期的初始化阶段,Web容器通过调用init()方法初始化servlet实例,并传递实现该javax.servlet.ServletConfig接口的对象。此配置对象允许servlet 从Web应用程序访问名称 - 值初始化参数。
  • 初始化之后,servlet实例可以为客户端请求提供服务。每个请求都在自己的独立线程中提供服务。Web容器service()为每个请求调用servlet 的方法。该service()方法确定正在进行的请求的类型,并将其分派给适当的方法来处理请求。servlet的开发人员必须为这些方法提供实现。如果对servlet未实现的方法发出请求,则调用父类的方法,通常会导致将错误返回给请求者。
  • 最后,Web容器调用destroy()使servlet停止服务的方法。这个destroy()方法init()在servlet的生命周期中只调用一次。

以下是这些方法的典型用户场景。

  1. 假设用户请求访问URL

    • 然后,浏览器为此URL生成HTTP请求。
    • 然后将此请求发送到适当的服务器。
  2. HTTP请求由Web服务器接收并转发到servlet容器。

    • 容器将此请求映射到特定的servlet。
    • 动态检索servlet并将其加载到容器的地址空间中。
  3. 容器调用init()

    servlet 的方法。

    • 仅当servlet首次加载到内存中时才会调用此方法。
    • 可以将初始化参数传递给servlet,以便它可以自行配置。
  4. 容器调用service()方法。

    • 调用此方法来处理HTTP请求。
    • servlet可以读取HTTP请求中提供的数据。
    • servlet还可以为客户端制定HTTP响应。
  5. servlet保留在容器的地址空间中,可用于处理从客户端收到的任何其他HTTP请求。

    • service()为每个HTTP请求调用该方法。
  6. 在某些时候,容器可能决定从其内存中卸载servlet。

    • 做出此决定的算法特定于每个容器。
  7. 容器调用servlet的destroy()方法来放弃任何资源,例如为servlet分配的文件句柄; 重要数据可以保存到持久性存储中。

  8. 然后可以对为servlet及其对象分配的内存进行垃圾回收。

WAR包

war包是Sun提出的一种web应用程序格式,与jar类似,是很多文件的压缩包。

war包中的文件按照一定目录结构来组织。通常其根目录下包含有html和jsp文件,或者包含有这两种文件的目录,另外还有WEB-INF目录。通常在WEB-INF目录下含有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的servlet类和jsp,或者servlet所依赖的其他类(如JavaBean)。通常这些所依赖的类也可以打包成jar包放在WEB-INF下的lib目录下。

以Tomcat来说,将war包放置在其\webapps\目录下,然后启动Tomcat,这个包就会自动解压,就相当于发布了。

maven插件入门

Posted on 2019-02-09 | In maven

必须配置

maven的本质是一个执行插件的框架,所有的工作都是由插件完成的。插件有两种类型:

  • 执行插件build plugins,在执行build时被调用,应该被配置在pom文件的build元素中。
  • 报告插件reporting plubins,在生成站点时被调用,应该被配置在pom文件的reporting元素中。因为reporting plugin的执行结果是站点的一部分,所以需要被国际化。

所有的插件都需要被设置groupId,artifactId和version。推荐对每个插件设置version。

最佳实践:在parent pom的build的pluginManagement中声明所有插件的version。

通用配置

背景

在每一个maven插件中,都有一个或多个Mojo类,Mojo是maven plain old java ojbect,继承org.apache.maven.plugin.AbstractMojo,这个接口保证插件可以被正确执行、输出日志、与控制台交互。

mojo中包含任意多个字段或字段的setters,这些字段可以被命令行、配置文件进行配置。插件的goal和phase也在Mojo类的注解中标志。

比如:

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
/**
* @goal query
*/
public class MyQueryMojo
extends AbstractMojo
{
/**
* @parameter expression="${query.url}"
*/
private String url;

/**
* @parameter default-value="60"
*/
private int timeout;

/**
* @parameter
*/
private String[] options;

public void execute()
throws MojoExecutionException
{
...
}
}

一般配置

在pom文件中配置

在pom文件的插件中,通过configuration元素对插件进行配置,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-myquery-plugin</artifactId>
<version>1.0</version>
<configuration>
<url>http://www.foobar.com/query</url>
<timeout>10</timeout>
<options>
<option>one</option>
<option>two</option>
<option>three</option>
</options>
</configuration>
</plugin>
</plugins>
</build>
...
</project>

其中configuration的子元素与Mojo类中字段的名称相同。

在系统变量中配置

通过Mojo类字段上的@parameter expression,可在命令行中添加配置,如:

1
mvn myquery:query -Dquery.url=http://maven.apache.org

注意,在使用命令行配置前,到插件中确认配置的名称。可以使用help goal查询参数和类型,比如:

1
mvn javadoc:help -Ddetail -Dgoal=javadoc

配置Build插件

使用executions

使用executions标签,最常用于配置作用于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
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-myquery-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>execution1</id>
<phase>test</phase>
<configuration>
<url>http://www.foo.com/query</url>
<timeout>10</timeout>
<options>
<option>one</option>
<option>two</option>
<option>three</option>
</options>
</configuration>
<goals>
<goal>query</goal>
</goals>
</execution>
<execution>
<id>execution2</id>
<configuration>
<url>http://www.bar.com/query</url>
<timeout>15</timeout>
<options>
<option>four</option>
<option>five</option>
<option>six</option>
</options>
</configuration>
<goals>
<goal>query</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

这里有两个execution配置。其中,executions绑定到test阶段,execution2没有配置phase,使用默认阶段。如果这个goal有默认阶段,则插件会被执行,否则不会被执行。

注意,在一个pom文件的某个插件中,execution的id必须是独一无二的,在不同的pom文件的同一插件的exexution的id可以不一样,这些配置会被合并。这个规则同样适用于profile中定义的executions。

如果一个插件中的两个execution被绑定到不同的phase,如下所示,这个插件会被执行多次。

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
<project>
...
<build>
<plugins>
<plugin>
...
<executions>
<execution>
<id>execution1</id>
<phase>test</phase>
...
</execution>
<execution>
<id>execution2</id>
<phase>install</phase>
<configuration>
<url>http://www.bar.com/query</url>
<timeout>15</timeout>
<options>
<option>four</option>
<option>five</option>
<option>six</option>
</options>
</configuration>
<goals>
<goal>query</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

maven 3.3.1版本之后,可以在命令行中直接对某个execution进行配置:

1
mvn myqyeryplugin:queryMojo@execution1

使用dependencies

可以配置build插件的依赖,以使用更新版本的依赖。

比如maven antrun插件1.2依赖ant 1.6.5,如果想使用其他版本,可以这样配置:

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
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.2</version>
...
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
...
</project>

使用inherited

默认情况,插件配置会被继承到子pom中,为了打破这种继承关系,可以使用inherited。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.2</version>
<inherited>false</inherited>
...
</plugin>
</plugins>
</build>
...
</project>

搭建github博客

Posted on 2019-02-06 | In 环境

背景

  1. 很多博客网站的广告很多,或者不够自由。
  2. 博客相关的代码存放不方便
  3. 自己购买域名需要花钱
  4. github提供pages功能,用户可以免费创建博客空间。

hexo

hexo是一种静态网页开发平台,基于node.js等。开发人员提供markdown网页,hexo基于themes等资源生成静态网页并发布成网站。

hexo还支持git 方式发布,用户可直接将静态网页等资源直接发布到github上,基于github pages功能,我们可以直接在互联网上浏览相应网页。

搭建过程

网上有很多相关文章,这里简单说一下大概步骤。

  1. 安装node.js,基于node.js安装hexo。
  2. 安装git,代码的提交和网站的发布都需要git。
  3. 在github上创建一个仓库,并克隆到本地。
  4. 在一个空文件夹blog中执行hexo init,初始化网站资源。
  5. 将blog中的文件拷贝到git路径下,执行hexo d -g发布到github上的master分支上。
  6. 创建dev分支,删除.gitignore文件,将所有文件都提交到远程dev分支中。后续在dev分支上修改源码和文件,使用hexo d -g将网站发布到github的master分支上。
  7. 在hexo 的themes上找到喜欢的主题,克隆到themes中,修改_config.xml中的theme。
  8. 使用hexo new 创建新的文章,注意添加categories和tags,并严格遵守文件格式, - 后面只有一个英文空格。
1…34

Emma Wang

人生马拉松

37 posts
22 categories
58 tags
RSS
© 2021 Emma Wang
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4