diff --git a/axon-cqrs-example/.gitignore b/axon-cqrs-example/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2af7cefb0a3f1e7df2fc27b8421f0e16b460e680 --- /dev/null +++ b/axon-cqrs-example/.gitignore @@ -0,0 +1,24 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/axon-cqrs-example/.mvn/wrapper/maven-wrapper.jar b/axon-cqrs-example/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 Binary files /dev/null and b/axon-cqrs-example/.mvn/wrapper/maven-wrapper.jar differ diff --git a/axon-cqrs-example/.mvn/wrapper/maven-wrapper.properties b/axon-cqrs-example/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..c315043703752ef4d11cf7d93f2c324852b2ebff --- /dev/null +++ b/axon-cqrs-example/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip diff --git a/axon-cqrs-example/mvnw b/axon-cqrs-example/mvnw new file mode 100755 index 0000000000000000000000000000000000000000..5bf251c0774593ca4f5335acf0f7483eaa162e8f --- /dev/null +++ b/axon-cqrs-example/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/axon-cqrs-example/mvnw.cmd b/axon-cqrs-example/mvnw.cmd new file mode 100644 index 0000000000000000000000000000000000000000..019bd74d766ebd4c033528112148d866555b5c9e --- /dev/null +++ b/axon-cqrs-example/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/axon-cqrs-example/pom.xml b/axon-cqrs-example/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ef90ef4e9f131d8c08865e88becf6d473c551677 --- /dev/null +++ b/axon-cqrs-example/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + com.brook.example + axon-cqrs-example + 1.0-SNAPSHOT + jar + axon-cqrs-example + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.5.4.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-web + + + + mysql + mysql-connector-java + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/axon-cqrs-example/src/main/java/com/brook/example/axon/AxonApplication.java b/axon-cqrs-example/src/main/java/com/brook/example/axon/AxonApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..970b2bd2d0e6dc4b16d7561f5c6e3582f7a95907 --- /dev/null +++ b/axon-cqrs-example/src/main/java/com/brook/example/axon/AxonApplication.java @@ -0,0 +1,12 @@ +package com.brook.example.axon; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AxonApplication { + + public static void main(String[] args) { + SpringApplication.run(AxonApplication.class, args); + } +} diff --git a/axon-cqrs-example/src/main/resources/application.yml b/axon-cqrs-example/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..e8d4692132ed2cad5299f9cb9b49318312d622b5 --- /dev/null +++ b/axon-cqrs-example/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + datasource: + url: jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1 + username: sa + password: + type: org.apache.tomcat.jdbc.pool.XADataSource + driver-class-name: org.h2.Driver + jpa: + database: h2 + hibernate: + ddl-auto: update \ No newline at end of file diff --git a/axon-cqrs-example/src/test/java/com/brook/example/axon/AxonApplicationTests.java b/axon-cqrs-example/src/test/java/com/brook/example/axon/AxonApplicationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..24602a0f625f37c19684e23137c1f7d15e49bd64 --- /dev/null +++ b/axon-cqrs-example/src/test/java/com/brook/example/axon/AxonApplicationTests.java @@ -0,0 +1,16 @@ +package com.brook.example.axon; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class AxonApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/learn-java8-example/README.md b/learn-java8-example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..20cc319298f8b1a5d8ff6a323426e32a3cb5d5c6 --- /dev/null +++ b/learn-java8-example/README.md @@ -0,0 +1,147 @@ +## 带你实战Java8 + +> Java8 于2014年发布,是自Java1.0发布18年来最大变化的版本,完全向前兼容的版本。 +新功能中提供了更多的语法和设计,帮助开发者编写更清楚、简洁的代码。 + Java8把函数式编程里一些最好的思想融入到大家熟知的Java语法中,让你用更少的时间写出高效代码。 + +![](http://biezhi.me/static/img/article/java8-banner.png) + +
开启JAVA8之路
+ +### 新特性 +- 语言新特性 + - Lambda 表达式 与函数式接口 + - 流式API(可读性更好) + - 接口默认与静态方法 + - 方法引用 + - 重复注解 + - 更好的类型推测机制 +- Java 编译器新特性 + - 参数名称 +- 官方库的新特性 + - Optional 防止null + - Date/Time 更友好的日期API + - 增强并行和并发处理 + - Nashorn JavaScript引擎,可以在jvm上运行js + - Base64 (不需第三方库) +- 新的java工具 + - jjs 命令行工具 + - jdeps 类分析其 +- JVM新特性 + 使用Metaspace(JEP 122)代替持久代(PermGen space)。 + 在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-X + +### lambda表达式 +`java8` 出现后,`lambda`赋予了java的语言的魅力。很长一段时间java被吐槽是冗余和缺乏函数式编程能力的语言, +随着函数式编程的流行java8种也引入了 这种编程风格。我们再也不用写那么多的内部类, +下面我们介绍如何使用lambda, 带你体验函数式编程的魔力。 + +![](http://biezhi.me/static/img/article/lambda-expression.png) + +##### 什么是lambda +lambda表达式是一段可以传递的代码,它的核心思想是将面向对象中的传递数据变成传递行为。 java8 以前编写一个线程时这样的: + +```java +// 匿名内部类的写法,有的人会去实现`Runnable`接口或者继承`Thread`类 +Runnable r = new Runnable() { + @Override + public void run() { + System.out.println("do something."); + } +} +``` +lambda 只用一行就实现了这个需求 +```java +Runnable r = () -> System.out.println("do something."); + +``` +#### 语法 +```java +expression = (var) -> action +``` +- `var`: 这是一个变量,一个占位符。像x,y,z,可以是多个变量。 +- `action`: 这里我称它为action, 它可以是一行代码或者代码片段 + +lambda 的多个参数 +```java +int f2 = (x, y) -> x + y; +``` + +#### 函数式接口 +> 函数式接口是只有一个方法的接口,用作lambda表达式的类型。上个例子中`Runnable`其实就是一个函数式接口。 +``` java +@FunctionalInterface +public interface Runnable { + /** + * When an object implementing interface Runnable is used + * to create a thread, starting the thread causes the object's + * run method to be called in that separately executing + * thread. + *

+ * The general contract of the method run is that it may + * take any action whatsoever. + * + * @see java.lang.Thread#run() + */ + public abstract void run(); +} +``` + +例子: +```java +public class FunctionInterfaceDemo { + @FunctionalInterface + interface Predicate { + boolean test(T t); + } + /** + * 执行Predicate判断 + * + * @param age 年龄 + * @param predicate Predicate函数式接口 + * @return 返回布尔类型结果 + */ + public static boolean doPredicate(int age, Predicate predicate) { + return predicate.test(age); + } + + public static void main(String[] args) { + boolean isAdult = doPredicate(20, x -> x >= 18); + System.out.println(isAdult); + } +} +``` +#### 函数式接口分类 + +函数接口大致有四大类: +- Consumer 消费类型接口 +```java +Consumer greeter = (p) -> System.out.println("Hello, " + p.getName()); +greeter.accept(new Person(1L, "tom")); +``` + +- Supplier 生产类型接口 +``` java +Supplier create = Person::new; +Person p = create.get(); // new Person +``` + +- Function 函数类型接口 +```java +Function toInteger = Integer::valueOf; +Function backToString = toInteger.andThen(String::valueOf); +backToString.apply("123"); // "123" +``` + +- Predicate 断言类型接口 +```java +Predicate predicate = (s) -> s.length() > 0; +predicate.test("foo"); // true +predicate.negate().test("foo"); // false + +Predicate nonNull = Objects::nonNull; +Predicate isNull = Objects::isNull; + +Predicate isEmpty = String::isEmpty; +Predicate isNotEmpty = isEmpty.negate(); +``` \ No newline at end of file diff --git a/learn-java8-example/pom.xml b/learn-java8-example/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..414f742e7deb3a621708555e1f9d7dc078d341e4 --- /dev/null +++ b/learn-java8-example/pom.xml @@ -0,0 +1,62 @@ + + + example + com.brook.example + 1.0-SNAPSHOT + + 4.0.0 + learn-java8-example + 1.0-SNAPSHOT + jar + + learn-java8-example + http://maven.apache.org + + + UTF-8 + + + + + + + + + + + + + + org.junit.jupiter + junit-jupiter-params + 5.0.0-M6 + test + + + + org.junit.platform + junit-platform-console-standalone + 1.0.0-M6 + test + + + org.junit.platform + junit-platform-launcher + 1.0.0-M6 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.0.0-M6 + test + + + org.junit.vintage + junit-vintage-engine + 4.12.0-M6 + test + + + diff --git a/learn-java8-example/src/main/java/com/brook/example/java8/App.java b/learn-java8-example/src/main/java/com/brook/example/java8/App.java new file mode 100644 index 0000000000000000000000000000000000000000..e39f6222526eb6ff6158928dfd60af0954cc37cd --- /dev/null +++ b/learn-java8-example/src/main/java/com/brook/example/java8/App.java @@ -0,0 +1,13 @@ +package com.brook.example.java8; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/learn-java8-example/src/main/resources/log4j2.xml b/learn-java8-example/src/main/resources/log4j2.xml new file mode 100644 index 0000000000000000000000000000000000000000..b047371f05fbc2cc5f943e54164d467401062c4e --- /dev/null +++ b/learn-java8-example/src/main/resources/log4j2.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/AppTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/AppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c5552241d51042fcd970d530a6091941c45c820a --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/AppTest.java @@ -0,0 +1,63 @@ +package com.brook.example.java8; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.DynamicTest.stream; + +/** + * Unit test for simple App. + */ +@DisplayName("junit5 testcase.") +class AppTest { + + static IntStream range() { + return IntStream.range(0, 20).skip(10); + } + + @Test + @DisplayName("junit5 helloJUnit5.") + void helloJUnit5() { + assertEquals("hello junit5", "hello junit5"); + } + + /** + * use {@code {@link ParameterizedTest}} annotation , + * required dependency on the junit-jupiter-params artifact. + * @param argument + */ + @ParameterizedTest + @ValueSource(strings = { "Hello", "World" }) + void testWithStringParameter(String argument) { + assertNotNull(argument); + } + + @TestFactory + Stream streamDynamicTest() { + return stream( + Stream.of("Hello", "World").iterator(), + (word) -> String.format("Test - %s", word), + (word) -> { + assertTrue(word.length() > 4); + assertNotNull(word); + + }); + } + + @ParameterizedTest + @MethodSource("range") + void testWithRangeMethodSource(int argument) { + assertNotEquals(9, argument); + } + + +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/base/PersistentTree.java b/learn-java8-example/src/test/java/com/brook/example/java8/base/PersistentTree.java new file mode 100644 index 0000000000000000000000000000000000000000..c69704522a4ffc0773101845985e48e237a0cff2 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/base/PersistentTree.java @@ -0,0 +1,107 @@ +package com.brook.example.java8.base; + +import org.junit.jupiter.api.Test; + +class PersistentTree { + + @Test + void test() { + Tree t = new Tree("Mary", 22, + new Tree("Emily", 20, + new Tree("Alan", 50, null, null), + new Tree("Georgie", 23, null, null) + ), + new Tree("Tian", 29, + new Tree("Raoul", 23, null, null), + null + ) + ); + + // found = 23 + System.out.println(lookup("Raoul", -1, t)); + // not found = -1 + System.out.println(lookup("Jeff", -1, t)); + + Tree f = fupdate("Jeff", 80, t); + // found = 80 + System.out.println(lookup("Jeff", -1, f)); + + Tree u = update("Jim", 40, t); + // t was not altered by fupdate, so Jeff is not found = -1 + System.out.println(lookup("Jeff", -1, u)); + // found = 40 + System.out.println(lookup("Jim", -1, u)); + + Tree f2 = fupdate("Jeff", 80, t); + // found = 80 + System.out.println(lookup("Jeff", -1, f2)); + // f2 built from t altered by update() above, so Jim is still present = 40 + System.out.println(lookup("Jim", -1, f2)); + } + + + static class Tree { + private String key; + private int val; + private Tree left, right; + + @Override + public String toString() { + return "Tree{" + + "key='" + key + '\'' + + ", val=" + val + + ", left=" + left + + ", right=" + right + + '}'; + } + + public Tree(String k, int v, Tree l, Tree r) { + key = k; + val = v; + left = l; + right = r; + } + } + + public static int lookup(String k, int defaultval, Tree t) { + if (t == null) + return defaultval; + if (k.equals(t.key)) + return t.val; + return lookup(k, defaultval, k.compareTo(t.key) < 0 ? t.left : t.right); + } + + public static Tree update(String k, int newval, Tree t) { + if (t == null) + t = new Tree(k, newval, null, null); + else if (k.equals(t.key)) + t.val = newval; + else if (k.compareTo(t.key) < 0) + t.left = update(k, newval, t.left); + else + t.right = update(k, newval, t.right); + return t; + } + + /** + * 采用函数的方法更新 + * 和{@link #update(String, int, Tree)} 区别 + *

+ * 因为update试图对树进行原地更新,它返回的是跟传入的参数同样的树,但是 如果最初的树为空, + * 那么新的节点会作为结果返回。 + * @param k + * @param newval + * @param t + * @return + */ + public static Tree fupdate(String k, int newval, Tree t) { + return (t == null) ? + new Tree(k, newval, null, null) : + k.equals(t.key) ? + new Tree(k, newval, t.left, t.right) : + k.compareTo(t.key) < 0 ? + new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) : + new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right)); + } + +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/base/RepeateAnnoTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/base/RepeateAnnoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..24d8627e7906aa2ebf1da57574a5c3aedd125dcc --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/base/RepeateAnnoTest.java @@ -0,0 +1,33 @@ +package com.brook.example.java8.base; + +import com.brook.example.java8.domain.Author; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +public class RepeateAnnoTest { + @Test + void getAuthors(){ + Author [] authors = Book.class.getAnnotationsByType(Author.class); + List names = Arrays.asList(authors) + .stream() + .map(Author::name) + .collect(toList()); + assertIterableEquals(Lists.newArrayList("tom", "lucy", "小明"),names); + } +} +@Author(name = "tom") +@Author(name = "lucy") +@Author(name = "小明") +class Book{ + +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/collections/MapTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/collections/MapTest.java new file mode 100644 index 0000000000000000000000000000000000000000..10572fc4d60a65ce68c620284b3aad2e915b0f94 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/collections/MapTest.java @@ -0,0 +1,137 @@ +package com.brook.example.java8.collections; + +import com.brook.example.java8.domain.Person; +import com.google.common.collect.Maps; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestReporter; + +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.LongAccumulator; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * java8 Map test + * @author Shaojun Liu + * @create 2017/7/29 + */ +@Log4j2 +public class MapTest { + @Test + void test(TestReporter reporter){ + Map map = Maps.newHashMapWithExpectedSize(16); + map.putIfAbsent("a",1); + int val = map.putIfAbsent("a",2); + assertEquals(1,val); + Integer val3 = map.putIfAbsent("b",3); + assertNull(val3); + int val4 = map.put("b",4); + assertEquals(3,val4); + Integer val5 = map.put("c",5); + assertNull(val5); + map.computeIfAbsent("c", key -> 6 ); + assertNotNull(map.get("c")); + map.computeIfPresent("b",(k,v)-> v * 2); + assertEquals(8, map.get("b").intValue()); + int val6 = map.getOrDefault("d",-1); + assertEquals(-1,val6); + map.put("d",null); + int val7 = map.merge("d",6,(v,newVal)->newVal); + assertEquals(6,val7); + int val8 = map.merge("d", 8,(v,newVal)->newVal+v); + assertEquals(14,val8); + log.info("map is >>>{}",map); + } + @Test + void testCurrentHashMap(){ + ConcurrentHashMap map = new ConcurrentHashMap<>(); + String word = "test"; + // 以下这段代码不是原子性,因为另一个线程可能同时在更新相同的计数 + Long oldVal = map.get(word); + Long newVal = oldVal == null ?1 : oldVal +1; + map.put(word,newVal); + // 1. 可以使用replace + do { + oldVal = map.get(word); + newVal = oldVal == null ?1 : oldVal + 1; + }while (!map.replace(word,oldVal,newVal)); + // 2. 可以使用 java8 LongAdder + ConcurrentHashMap laMap = new ConcurrentHashMap<>(); + laMap.putIfAbsent(word,new LongAdder()); + laMap.get(word).increment(); + // 如果是复杂计算可以用, val 不能为null + map.compute(word, (k, v) -> v == null ? 1 : v + 1); + // putIfAbsent 区别,只有在需要计算时才会被调用 + laMap.computeIfAbsent(word, key -> new LongAdder()); + laMap.get(word).increment(); + // 或者 + map.merge(word,1L,Long::sum); + //注意 传递给 compute 和 merge 方法的函数不能为null,否则已有的数据项会从映射中删除 + // 小心使用compute 和 merge 方法,牢记不要进行大量操作 + } + + /** + * U searchKeys(long threshold, BiFunction f) + U searchValues(long threshold, BiFunction f) + U search(long threshold, BiFunction f) + U searchEntries(long threshold, BiFunction, ? extends U> f) + */ + @Test + void expand(){ + ConcurrentHashMap map = (ConcurrentHashMap) Stream.generate(new + RandomPerson()) + .limit(100) + .collect(Collectors.toConcurrentMap(Person::getName, + Person::getAge)); + // threshold 越小越快 + String result = map.search(1,(k, v) ->v > 50? k: null ); + System.out.println("result >>> " + result); + int sum = map.reduceValues(1,Integer::sum); + System.out.println("sum >>>" + sum); + long lsum = map.reduceValuesToLong(1,Long::valueOf,0,Long::sum); + System.out.println("lsum >>>" + lsum); + int keysLenSum = map.reduceKeys(2, String::length, Integer::sum); + System.out.println("keysLenSum >>>" + keysLenSum); + } + + /** + * LongAdder(加法器) 适合做统计 + * 类似的有 {@link java.util.concurrent.atomic.LongAccumulator} + */ + @Test + void adder(){ + LongAdder adder = new LongAdder(); + adder.increment(); + adder.increment(); + assertEquals(adder.sum(),2); + adder.increment(); + assertEquals("3",adder.toString()); + assertEquals(3,adder.sumThenReset()); + adder.add(12); + adder.increment(); + assertEquals(13,adder.sum()); + + LongAccumulator accumulator = new LongAccumulator(Long::sum,1); + accumulator.accumulate(2); + assertEquals(3,accumulator.get()); + } + + +} +class RandomPerson implements Supplier{ + + // LongAdder adder = new LongAdder(); + long i = 1; + Random r = new Random(); + @Override + public Person get() { + return new Person(i++,"user"+ i,10 +r.nextInt(90)); + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/currency/CompletableFutureTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/currency/CompletableFutureTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5cd0da75bbbd6d4b0f0d9131dbc28f90e984a70c --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/currency/CompletableFutureTest.java @@ -0,0 +1,166 @@ +package com.brook.example.java8.currency; + +import com.google.common.collect.ImmutableMap; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +public class CompletableFutureTest { + + // 合并 + private static String merge(List datas){ + return datas.parallelStream() + .reduce((s, s2) -> { + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return s + ";" + s2; + + }).orElse(""); + } + + /** + * 流水式执行 + * + * @throws ExecutionException + * @throws InterruptedException + */ + @Test + void sequentially() throws ExecutionException, InterruptedException { + // 如果是I/O密集型,使用Executor 增加线程池大小 + // 如果是CPU密集型计算,就不能增加太多的计算使用 parallel stream 比较好 + ExecutorService executor = Executors.newFixedThreadPool(5); + CompletableFuture db = CompletableFuture.supplyAsync(()->this.getDataFromDB("db"),executor); + db.thenApplyAsync(this::getDataFromRedis,executor) + .thenAcceptAsync(this::handler,executor) + // 异常捕获 + .exceptionally(e -> { + System.out.println("error :" + e.getMessage()); + return null; + }) + .whenComplete((aVoid, e) -> System.out.println("----> finish.")) + .get(); + executor.shutdown(); + + } + + /** + * 模拟从数据库获取数据 + * @return + */ + public String getDataFromDB(String data){ + System.out.println("-------> get data from db."); + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return data; + } + + /** + * 并行执行 + *

(7s) + * db -> redis-> \ + * => merge(2s) => finish (9s) + * net -> / + * (6s) + *

+ * @throws ExecutionException + * @throws InterruptedException + */ + @Test + void parallel() throws ExecutionException, InterruptedException { + ExecutorService executor = Executors.newFixedThreadPool(4); + + List> futures = getFutures(executor); + String result = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures + .size()])) + .thenApplyAsync((v)-> futures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList())) + // 合并 + .thenApplyAsync(CompletableFutureTest::merge,executor) + .exceptionally((e) -> { + System.out.println("error :" + e.getMessage()); + return null; + }) + .whenComplete(this::done) + .get(); + executor.shutdown(); + assertEquals("redis:data;net:data",result); + } + + private List> getFutures(Executor executor){ + CompletableFuture dbFuture = CompletableFuture + .supplyAsync(() -> this.getDataFromDB("db:data"),executor) + .thenApplyAsync(this::getDataFromRedis,executor) + .thenApplyAsync((map) -> map.values().toArray(new String[]{})[0]); + + CompletableFuture netFuture = CompletableFuture + .supplyAsync(this::getDataFromNet); + return Lists.newArrayList(dbFuture, netFuture); + + } + + void handler(Map data){ + System.out.println("-------> 处理数据"); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + data.forEach((k, v) -> System.out.printf("k = %s,v = %s \n\r",k,v)); + } + + private void done(String data,Throwable e){ + System.out.println("-----> " + data); + System.out.println("----> " + "finish."); + } + + /** + * 模拟从redis 获取数据 + * @return + */ + public Map getDataFromRedis(String key){ + System.out.println("-------> get data from redis."); + + System.out.println("key is "+key ); + if(key == null){ + throw new RuntimeException(String.format("key [%s]不存在!",key)); + } + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return ImmutableMap.of(key,"redis:data"); + } + + /** + * 模拟从网络获取数据 + * @return + */ + public String getDataFromNet(){ + System.out.println("-------> get data from net."); + + try { + TimeUnit.SECONDS.sleep(6); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "net:data"; + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/currency/FutureParallelTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/currency/FutureParallelTest.java new file mode 100644 index 0000000000000000000000000000000000000000..444b5faade82778d7154d49c72f68733c5b86561 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/currency/FutureParallelTest.java @@ -0,0 +1,128 @@ +package com.brook.example.java8.currency; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertTimeout; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +public class FutureParallelTest { + List tasks; + + @BeforeEach + void initTasks() { + tasks = IntStream.range(0, 10) + .mapToObj(i -> new MyTask(1)) + .collect(toList()); + + } + + @DisplayName("串行流") + @Test + void seq() { + assertTimeout(Duration.ofSeconds(11), () -> MyTask.runSequentially(tasks)); + } + + @DisplayName("并行流") + @Test + void ps() { + MyTask.useParallelStream(tasks); + } + + @DisplayName("CompletableFuture 并行") + @Test + void cf() { + MyTask.useCompletableFutureWithExecutor(tasks); + } + + @DisplayName("CompletableFuture With Executor 并行") + @Test + void cfe() { + MyTask.useCompletableFutureWithExecutor(tasks); + } + +} + +class MyTask { + private final int duration; + + public MyTask(int duration) { + this.duration = duration; + } + + public static void runSequentially(List tasks) { + long start = System.nanoTime(); + List result = tasks.stream() + .map(MyTask::calculate) + .collect(toList()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration); + System.out.println(result); + } + + public static void useParallelStream(List tasks) { + long start = System.nanoTime(); + List result = tasks.parallelStream() + .map(MyTask::calculate) + .collect(toList()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration); + System.out.println(result); + } + + public static void useCompletableFuture(List tasks) { + long start = System.nanoTime(); + List> futures = + tasks.stream() + .map(t -> CompletableFuture.supplyAsync(() -> t.calculate())) + .collect(Collectors.toList()); + + List result = + futures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration); + System.out.println(result); + } + + public static void useCompletableFutureWithExecutor(List tasks) { + long start = System.nanoTime(); + ExecutorService executor = Executors.newFixedThreadPool(Math.min(tasks.size(), 10)); + List> futures = + tasks.stream() + .map(t -> CompletableFuture.supplyAsync(t::calculate, executor)) + .collect(Collectors.toList()); + List result = + futures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration); + System.out.println(result); + executor.shutdown(); + } + + public int calculate() { + System.out.println(Thread.currentThread().getName()); + try { + Thread.sleep(duration * 1000); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + return duration; + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/currency/StampedLockTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/currency/StampedLockTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a075efeea604092d53d8b96fc84c66f1c1845686 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/currency/StampedLockTest.java @@ -0,0 +1,117 @@ +package com.brook.example.java8.currency; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.locks.StampedLock; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * StampedLock + *

+ * java8 新出的读写锁,比{@link java.util.concurrent.locks.ReentrantReadWriteLock}性能更好。
+ * {@code ReentrantReadWriteLock} 在沒有任何读写锁时,才可以取得写入锁, + * 这可用于实现了悲观读取(Pessimistic Reading),即如果执行中进行读取时, + * 经常可能有另一执行要写入的需求,为了保持同步, {@code ReentrantReadWriteLock} 的读取锁定就可派上用场。 + * 然而,如果读取执行情况很多,写入很少的情况下,使用 {@code ReentrantReadWriteLock} 可能 + * 会使写入线程遭遇饥饿(Starvation)问题,也就是写入线程迟迟无法竞争到锁定而一直处于等待状态。 + * {@code StampedLock}控制锁有三种模式: 乐观读。 + * 一个{@code StampedLock} 状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据 + * stamp,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。

+ * 在读锁上分为悲观锁和乐观锁。 + *

+ * + * @author Shaojun Liu + * @create 2017/7/30 + */ +public class StampedLockTest { + + @Test + void deposit(){ + AccountWithSampedLock account = new AccountWithSampedLock(); + account.deposit(1000d); + double amount = account.getBalance(); + assertEquals(1000d,amount); + } +} + +class AccountWithSampedLock { + private final StampedLock lock = new StampedLock(); + private double balance; + + void deposit(double amount) { + long stamp = lock.writeLock(); + try { + balance = balance + amount; + } finally { + lock.unlockWrite(stamp); + } + } + + double getBalance() { + long stamp = lock.readLock(); + try { + return balance; + } finally { + lock.unlockRead(stamp); + } + } +} + +/** + * java doc提供的StampedLock一个例子 + */ +class Point { + private double x, y; + private final StampedLock sl = new StampedLock(); + + void move(double deltaX, double deltaY) { + long stamp = sl.writeLock(); + try { + x += deltaX; + y += deltaY; + } finally { + sl.unlockWrite(stamp); + } + } + + //乐观读锁案例 + double distanceFromOrigin() { + long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁 + double currentX = x, currentY = y; //将两个字段读入本地局部变量 + if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生? + stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁 + try { + currentX = x; // 将两个字段读入本地局部变量 + currentY = y; // 将两个字段读入本地局部变量 + } finally { + sl.unlockRead(stamp); + } + } + return Math.sqrt(currentX * currentX + currentY * currentY); + } + + //悲观读锁案例 + void moveIfAtOrigin(double newX, double newY) { + // upgrade + // Could instead start with optimistic, not read mode + long stamp = sl.readLock(); + try { + while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合 + long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁 + if (ws != 0L) { + // 成功转为写锁 + stamp = ws; //如果成功 替换票据 + x = newX; //进行状态改变 + y = newY; //进行状态改变 + break; + } else { + sl.unlockRead(stamp); //我们显式释放读锁 + stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试 + } + } + } finally { + sl.unlock(stamp); // 释放读写锁 + } + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/currency/WordCountTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/currency/WordCountTest.java new file mode 100755 index 0000000000000000000000000000000000000000..fed918ab31c44498db01e1dd1a4ec8474b00d7d0 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/currency/WordCountTest.java @@ -0,0 +1,127 @@ +package com.brook.example.java8.currency; + +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static org.assertj.core.api.Assertions.assertThat; + +public class WordCountTest { + + public static final String SENTENCE = + " Nel mezzo del cammin di nostra vita " + + "mi ritrovai in una selva oscura" + + " che la dritta via era smarrita "; + + public static void main(String[] args) { + int words = countWordsIteratively(SENTENCE); + assertThat(words).isEqualTo(countWords(SENTENCE)); + + } + + /** + * 普通循环 + * @param s + * @return + */ + public static int countWordsIteratively(String s) { + int counter = 0; + boolean lastSpace = true; + for (char c : s.toCharArray()) { + if (Character.isWhitespace(c)) { + lastSpace = true; + } else { + if (lastSpace) counter++; + lastSpace = Character.isWhitespace(c); + } + } + return counter; + } + + // 使用流 + public static int countWords(String s) { +// 直接用并行流会导致统计不正确,默认的Spliterator在并行时并不知道整个字符串从哪里开始切割 +// Stream stream = IntStream.range(0, s.length()) +// .mapToObj(SENTENCE::charAt).parallel(); + Spliterator spliterator = new WordCounterSpliterator(s); + Stream stream = StreamSupport.stream(spliterator, true); + + return countWords(stream); + } + + private static int countWords(Stream stream) { + WordCounter wordCounter = stream.reduce(new WordCounter(0, true), + WordCounter::accumulate, + WordCounter::combine); + return wordCounter.getCounter(); + } + + private static class WordCounter { + private final int counter; + private final boolean lastSpace; + + public WordCounter(int counter, boolean lastSpace) { + this.counter = counter; + this.lastSpace = lastSpace; + } + + public WordCounter accumulate(Character c) { + if (Character.isWhitespace(c)) { + return lastSpace ? this : new WordCounter(counter, true); + } else { + return lastSpace ? new WordCounter(counter+1, false) : this; + } + } + + public WordCounter combine(WordCounter wordCounter) { + return new WordCounter(counter + wordCounter.counter, wordCounter.lastSpace); + } + + public int getCounter() { + return counter; + } + } + + private static class WordCounterSpliterator implements Spliterator { + + private final String string; + private int currentChar = 0; + + private WordCounterSpliterator(String string) { + this.string = string; + } + + @Override + public boolean tryAdvance(Consumer action) { + action.accept(string.charAt(currentChar++)); + return currentChar < string.length(); + } + + @Override + public Spliterator trySplit() { + int currentSize = string.length() - currentChar; + if (currentSize < 10) { + return null; + } + for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) { + if (Character.isWhitespace(string.charAt(splitPos))) { + Spliterator spliterator = new WordCounterSpliterator(string.substring(currentChar, splitPos)); + currentChar = splitPos; + return spliterator; + } + } + return null; + } + + @Override + public long estimateSize() { + return string.length() - currentChar; + } + + @Override + public int characteristics() { + return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE; + } + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/domain/Author.java b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Author.java new file mode 100644 index 0000000000000000000000000000000000000000..1a5622ed0fd19319f9d6133004b5309754afab1d --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Author.java @@ -0,0 +1,13 @@ +package com.brook.example.java8.domain; + +import java.lang.annotation.*; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +@Retention(RetentionPolicy.RUNTIME) +@Repeatable(Authors.class) +public @interface Author { + String name() default ""; +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/domain/Authors.java b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Authors.java new file mode 100644 index 0000000000000000000000000000000000000000..7529d09181b11edfb77ad4e173b1e25b95e1fb9c --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Authors.java @@ -0,0 +1,13 @@ +package com.brook.example.java8.domain; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface Authors { + Author[] value() default {}; +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/domain/Car.java b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Car.java new file mode 100644 index 0000000000000000000000000000000000000000..3bb3b3d3b35d9b68a201d97ba0b46937377f9129 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Car.java @@ -0,0 +1,28 @@ +package com.brook.example.java8.domain; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Optional; + +/** + * @author Shaojun Liu + * @create 2017/7/30 + */ +@Data +@NoArgsConstructor +public class Car { + private String name; + public Car(String name){ + this.name = name; + } + private Optional insurance; + @Data + @NoArgsConstructor + public static class Insurance { + private String name; + public Insurance(String name){ + this.name = name; + } + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/domain/Person.java b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Person.java new file mode 100644 index 0000000000000000000000000000000000000000..b92c5ff774e9b416c29027631db0682ccae94462 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/domain/Person.java @@ -0,0 +1,31 @@ +package com.brook.example.java8.domain; + +import lombok.*; + +import java.util.Optional; + +/** + * @author Shaojun Liu + * @create 2017/7/28 + */ +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Person { + @Setter + Optional car; + public Person(Long id,String name){ + this.id = id; + this.name = name; + } + + public Person(Long id, String name,int age) { + this(id,name); + this.age = age; + } + private Long id; + private String name; + private Integer age; + +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/functions/ConsumerTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/functions/ConsumerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e63735fa90cc7fe057c7ce9b8715c572e9dcb083 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/functions/ConsumerTest.java @@ -0,0 +1,8 @@ +package com.brook.example.java8.functions; + +/** + * @author Shaojun Liu + * @create 2017/7/30 + */ +public class ConsumerTest { +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/functions/DefaultMethodTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/functions/DefaultMethodTest.java new file mode 100644 index 0000000000000000000000000000000000000000..409ffc2da761cacb2e4c950d4dd575a053e519d1 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/functions/DefaultMethodTest.java @@ -0,0 +1,81 @@ +package com.brook.example.java8.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * 默认方法 + *

+ * 1. 默认方法 + * 2. 多继承 + * @author Shaojun Liu + * @create 2017/7/30 + */ +public class DefaultMethodTest { + + @Test + void getName(){ + TomPlayer player = new TomPlayer(); + assertEquals("tom",player.getName()); + assertTrue(player.isMale()); + Player.foo(); + } + + /** + * 多重继承 + */ + @Test + void foobar(){ + Foobar foobar = new Foobar(); + assertEquals("bar",foobar.name()); + } +} +interface Player { + String getName(); + default boolean isMale(){ + return true; + } + + static void foo(){ + System.out.println("我是静态方法"); + } +} +class TomPlayer implements Player{ + @Override + public String getName() { + return "tom"; + } +} +class LucyPlayer implements Player{ + @Override + public boolean isMale() { + return false; + } + + @Override + public String getName() { + return "lucy"; + } +} + +interface Foo{ + default String name() { + return "foo"; + } +} + +interface Bar{ + default String name() { + return "bar"; + } +} +class Foobar implements Foo, Bar { + // 菱形问题 + // 如果覆盖 name() 编译器就会报错, + @Override + public String name() { + return Bar.super.name(); + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/functions/FunctionsTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/functions/FunctionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f484937e4e4012c2b6337523f1766d915500d1ae --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/functions/FunctionsTest.java @@ -0,0 +1,77 @@ +package com.brook.example.java8.functions; + +import com.brook.example.java8.domain.Person; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Shaojun Liu + * @create 2017/7/30 + */ +@Log4j2 +public class FunctionsTest { + /** + * @see com.brook.example.java8.pattern.StrategyPattern + */ + @Test + void predicate(){ + Predicate predicate = (s) -> s.length() > 0; + assertTrue(predicate.test("foo")); + assertFalse(predicate.negate().test("foo")); + Predicate nonNull = Objects::nonNull; + assertTrue(nonNull.test(Boolean.FALSE)); + Predicate isNull = Objects::isNull; + assertTrue(isNull.test(null)); + + Predicate isEmpty = String::isEmpty; + assertTrue(isEmpty.test("")); + Predicate isNotEmpty = isEmpty.negate(); + assertTrue(isNotEmpty.test("Not Empty.")); + } + + /** + * 案例请看 + * @see com.brook.example.java8.pattern.ChainPattern + */ + @Test + void function() { + Function toInteger = Integer::valueOf; + Function backToString = toInteger.andThen(String::valueOf); + assertEquals("123", backToString.apply("123")); + // andThen 和 compose 区别 + Function times2 = e -> e * 2; + Function sqr = e -> e * e; + int r1 = times2.compose(sqr).apply(3); // 18 + int r2 = times2.andThen(sqr).apply(3); // 36 + // 结果说明 compose 先执行参数,而andThen 先执行函数调用者 + assertEquals(r1,18); + assertEquals(r2,36); + } + + /** + * @see com.brook.example.java8.pattern.ObserverPattern + */ + @Test + void consumer() { + Consumer greeter = (p) -> log.info("Hello, " + p.getName()); + greeter.accept(new Person(1L, "tom")); + } + + /** + * @see com.brook.example.java8.pattern.FactoryPattern + */ + @Test + void supplier(){ + Supplier create = Person::new; + Person p = create.get(); + assertNotNull(p); + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/functions/MethodRefTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/functions/MethodRefTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4099afb72c7e483e39f92e2a00a789773176a96f --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/functions/MethodRefTest.java @@ -0,0 +1,45 @@ +package com.brook.example.java8.functions; + +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +/** + * @author Shaojun Liu + * @create 2017/7/30 + */ +@Log4j2 +public class MethodRefTest { + @Test + void test(){ + new ConcurrentGreeter().greet(); + } +} +@Log4j2 +class Greeter { + public void greet(){ + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("Greet !"); + } +} +@Log4j2 +class ConcurrentGreeter extends Greeter{ + @Override + public void greet() { + + Thread t = new Thread(super::greet); // 原型就是 ()->super.greet(); + t.start(); + log.info("------ Output Greet !-------"); + try { + t.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("-------- End !--------"); + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/io/FilesTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/io/FilesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c646790a25f4693215af489dcd299b4d91bf465b --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/io/FilesTest.java @@ -0,0 +1,45 @@ +package com.brook.example.java8.io; + +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Files test case. + * @author Shaojun Liu + * @create 2017/7/28 + */ +@Log4j2 +class FilesTest { + static String home = System.getProperty("user.home"); + + @Test + void list() throws IOException { + Files.list(Paths.get(home, "/Desktop")) + .map(path -> path.getFileName() + " -->" + (path.toFile().isDirectory() + ? "目录" : "文件")) + .forEach(System.out::println); + + } + + @Test + void walk() throws IOException { + String currentDir = getClass().getResource(".").getPath(); + String filesTest = Files.walk(Paths.get(currentDir), 2) + .map(Path::getFileName) + .map(Object::toString) + .filter(fileName ->fileName.startsWith("FilesTest")) + .findFirst() + .orElse(""); + assertEquals("FilesTest.class",filesTest); + + } + + +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/optional/OptionalTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/optional/OptionalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cad6fb2c42d66125a4bea845271ce1cfba36d12b --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/optional/OptionalTest.java @@ -0,0 +1,58 @@ +package com.brook.example.java8.optional; + +import com.brook.example.java8.domain.Car; +import com.brook.example.java8.domain.Person; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @see java.util.Optional + * @author Shaojun Liu + * @create 2017/7/29 + */ +class OptionalTest { + + @DisplayName("Optional testcase.") + @Test + void create() { + String isNull = null; + isNull = Optional.ofNullable(isNull) + .orElse("is null"); + List strs = null; + strs = Optional.ofNullable(strs) + .orElseGet(Collections::emptyList); + assertNotNull(strs); + assertEquals(isNull, "is null"); + assertThrows(NullPointerException.class, () -> Optional.of(null)); + Assertions.assertThat(Optional.ofNullable("")).isPresent(); + assertThrows(IllegalArgumentException.class, + () -> Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new)); + } + + @Test + void testGetName() { + Person tom = new Person(1L, "tom"); + tom.setCar(Optional.empty()); + assertEquals("unknown",getInsuranceName(tom)); + Car.Insurance insurance = new Car.Insurance("阳光车险"); + Car car = new Car("宝马"); + car.setInsurance(Optional.of(insurance)); + tom.setCar(Optional.of(car)); + assertEquals("阳光车险",getInsuranceName(tom)); + } + + private String getInsuranceName(Person person) { + return Optional.of(person) + .flatMap(Person::getCar) + .flatMap(Car::getInsurance) + .map(Car.Insurance::getName) + .orElse("unknown"); + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/pattern/ChainPattern.java b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/ChainPattern.java new file mode 100755 index 0000000000000000000000000000000000000000..76d461dcd2827164ba1e4b18129ee307153f00a5 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/ChainPattern.java @@ -0,0 +1,64 @@ +package com.brook.example.java8.pattern; + +import org.junit.jupiter.api.Test; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class ChainPattern { + + @Test + void test(){ + // 传统的设计模式 + ProcessingObject p1 = new HeaderTextProcessing(); + ProcessingObject p2 = new SpellCheckerProcessing(); + p1.setSuccessor(p2); + String result1 = p1.handle("Aren't labdas really sexy?!!"); + System.out.println(result1); + // 使用java8 函数编程重构 + UnaryOperator headerProcessing = + (String text) -> "From Raoul, Mario and Alan: " + text; + UnaryOperator spellCheckerProcessing = + (String text) -> text.replaceAll("labda", "lambda"); + Function pipeline = headerProcessing.andThen(spellCheckerProcessing); + String result2 = pipeline.apply("Aren't labdas really sexy?!!"); + assertEquals(result1,result2); + } + + static private abstract class ProcessingObject { + protected ProcessingObject successor; + + public void setSuccessor(ProcessingObject successor) { + this.successor = successor; + } + + public T handle(T input) { + T r = handleWork(input); + if (successor != null) { + return successor.handle(r); + } + return r; + } + + abstract protected T handleWork(T input); + } + + static private class HeaderTextProcessing + extends ProcessingObject { + public String handleWork(String text) { + return "From Raoul, Mario and Alan: " + text; + } + } + + static private class SpellCheckerProcessing + extends ProcessingObject { + public String handleWork(String text) { + return text.replaceAll("labda", "lambda"); + } + } +} + + diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/pattern/FactoryPattern.java b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/FactoryPattern.java new file mode 100644 index 0000000000000000000000000000000000000000..0f775e4c8fbfae4c5877e15c0c485102bffeb7ff --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/FactoryPattern.java @@ -0,0 +1,66 @@ +package com.brook.example.java8.pattern; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class FactoryPattern { + + final static private Map> map = new HashMap<>(); + + static { + map.put("cup", Cup::new); + map.put("vacuumCup", VacuumCup::new); + map.put("wine", Wine::new); + } + + @Test + void test() { + Product p1 = ProductFactory.createProduct("cup"); + assertThat(p1).isInstanceOf(Cup.class); + Supplier wineSupplier = Wine::new; + Product p2 = wineSupplier.get(); + assertThat(p2).isInstanceOf(Wine.class); + Product p3 = ProductFactory.createProductLambda("vacuumCup"); + assertThat(p3).isInstanceOf(VacuumCup.class); + + } + + private interface Product { + } + + static private class ProductFactory { + public static Product createProduct(String name) { + switch (name) { + case "cup": + return new Cup(); + case "vacuumCup": + return new VacuumCup(); + case "wine": + return new Wine(); + default: + throw new RuntimeException("No such product " + name); + } + } + + public static Product createProductLambda(String name) { + Supplier p = map.get(name); + if (p != null) return p.get(); + throw new RuntimeException("No such product " + name); + } + } + + static private class Cup implements Product { + } + + static private class VacuumCup implements Product { + } + + static private class Wine implements Product { + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/pattern/ObserverPattern.java b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/ObserverPattern.java new file mode 100644 index 0000000000000000000000000000000000000000..fe75bcf3ab85f85ccbe3d0c53d41277c23eeb04d --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/ObserverPattern.java @@ -0,0 +1,85 @@ +package com.brook.example.java8.pattern; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +public class ObserverPattern { + + @Test + void test() { + Feed f = new Feed(); + f.registerObserver(new NYTimes()); + f.registerObserver(new Guardian()); + f.registerObserver(new LeMonde()); + f.notifyObservers("The queen said her favourite book is Java 8 in Action!"); + + Feed feedLambda = new Feed(); + + feedLambda.registerObserver((String tweet) -> { + if (tweet != null && tweet.contains("money")) { + System.out.println("Breaking news in NY! " + tweet); + } + }); + feedLambda.registerObserver((String tweet) -> { + if (tweet != null && tweet.contains("queen")) { + System.out.println("Yet another news in London... " + tweet); + } + }); + + feedLambda.notifyObservers("Money money money, give me money!"); + + } + + + interface Observer { + void inform(String tweet); + } + + interface Subject { + void registerObserver(Observer o); + + void notifyObservers(String tweet); + } + + static private class NYTimes implements Observer { + @Override + public void inform(String tweet) { + if (tweet != null && tweet.contains("money")) { + System.out.println("Breaking news in NY!" + tweet); + } + } + } + + static private class Guardian implements Observer { + @Override + public void inform(String tweet) { + if (tweet != null && tweet.contains("queen")) { + System.out.println("Yet another news in London... " + tweet); + } + } + } + + static private class LeMonde implements Observer { + @Override + public void inform(String tweet) { + if (tweet != null && tweet.contains("wine")) { + System.out.println("Today cheese, wine and news! " + tweet); + } + } + } + + static private class Feed implements Subject { + private final List observers = new ArrayList<>(); + + public void registerObserver(Observer o) { + this.observers.add(o); + } + + public void notifyObservers(String tweet) { + observers.forEach(o -> o.inform(tweet)); + } + } + +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/pattern/StrategyPattern.java b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/StrategyPattern.java new file mode 100755 index 0000000000000000000000000000000000000000..24eb5997f59f27528babe4801a3ad080aaf0e727 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/pattern/StrategyPattern.java @@ -0,0 +1,55 @@ +package com.brook.example.java8.pattern; + + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class StrategyPattern { + + public final static String NUMBERIC ="\\d+"; + public final static String LOWER_CASE ="[a-z]+"; + @Test + void test() { + // old school + Validator v1 = new Validator(new IsNumeric()); + assertFalse(v1.validate("abc")); + Validator v2 = new Validator(new IsAllLowerCase()); + assertTrue(v2.validate("abc")); + + // with lambdas + Validator v3 = new Validator((String s) -> s.matches(NUMBERIC)); + assertFalse(v3.validate("abc")); + Validator v4 = new Validator((String s) -> s.matches(LOWER_CASE)); + assertTrue(v4.validate("abc")); + } + + interface ValidationStrategy { + boolean execute(String s); + } + + static private class IsAllLowerCase implements ValidationStrategy { + public boolean execute(String s) { + return s.matches(LOWER_CASE); + } + } + + static private class IsNumeric implements ValidationStrategy { + public boolean execute(String s) { + return s.matches(NUMBERIC); + } + } + + static private class Validator { + private final ValidationStrategy strategy; + + public Validator(ValidationStrategy v) { + this.strategy = v; + } + + public boolean validate(String s) { + return strategy.execute(s); + } + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/StreamTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/StreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6e3a4edd9d69e712b8c835672e93ffd181e6265a --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/StreamTest.java @@ -0,0 +1,255 @@ +package com.brook.example.java8.stream; + +import com.brook.example.java8.domain.Person; +import com.brook.example.java8.stream.forker.Dish; +import com.google.common.collect.Lists; +import com.google.common.math.IntMath; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.TestReporter; + +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.DynamicTest.stream; + +/** + * java8 stream example + * + * @author Shaojun Liu + * @create 2017/7/28 + */ +@Log4j2 +public class StreamTest { + + @TestFactory + Stream range(){ + // numeric ranges + long evenCount = IntStream.rangeClosed(1, 100) + .filter(n -> n % 2 == 0) + .count(); + + assertEquals(50,evenCount); + //毕氏三元数; + + Stream pythagoreanTriples = + IntStream.rangeClosed(1, 100) + .boxed() + .flatMap(a -> IntStream.rangeClosed(a, 100) + .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0) + .boxed() + .map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)})); + return stream( + pythagoreanTriples.iterator() + ,ints -> ints[0]+"² +"+ints[1]+"² = "+ints[2]+"²" + ,(ints)-> assertEquals(IntMath.pow(ints[2],2), IntMath.pow(ints[0],2)+ IntMath.pow(ints[1],2)) + ); + } + @Test + void min() { + int min = IntStream + .of(5, 3, 1, 6) + .min() + .orElse(-1); + assertEquals(1, min, "This' min element is 1"); + } + + /** + * 自然排序 + *

+ * {@link Stream#sorted()} + * + * @param reporter + */ + @Test + void sort(TestReporter reporter) { + List sorts = IntStream.of(5, 3, 1, 6) + .boxed() + .sorted() + .collect(Collectors.toList()); + assertIterableEquals(sorts, Lists.newArrayList(1, 3, 5, 6)); + reporter.publishEntry("排序(asc)后结果:", sorts.toString()); + } + + @Test + void sortWithComparator(TestReporter reporter) { + List sorts = IntStream.of(5, 3, 1, 6) + // .map(Integer::valueOf) + .boxed() // 源码就是 .map(Integer::valueOf) + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + assertIterableEquals(sorts, Lists.newArrayList(6, 5, 3, 1)); + reporter.publishEntry("排序(desc)后结果:", sorts.toString()); + } + + @Test + void sortWithComparing(TestReporter reporter) { + Person tom = Person + .builder() + .id(1L) + .age(20) + .name("tom") + .build(); + Person jack = Person.builder() + .id(2L) + .age(20) + .name("jack") + .build(); + List persons = Lists.newArrayList(tom, jack); + persons.stream() + .sorted(Comparator.comparing(Person::getName) + .thenComparing(Person::getAge)) + .forEach((p) -> reporter.publishEntry(p.getName(), p.getAge() + "")); + } + + @Test + void listToMap(TestReporter reporter) { + List languages = getLanguages(); + Map result = languages.parallelStream() + .collect(Collectors.toMap(Object::toString, String::toUpperCase)); + reporter.publishEntry(result); + } + + private List getLanguages() { + return Lists.newArrayList("java", "php", "python"); + } + + @Test + void flatMap(TestReporter reporter) { + List> lists = Lists.newArrayList(); + lists.add(Arrays.asList("apple", "banana", "pear")); + lists.add(Arrays.asList("email", "weibo", "qq", "wechat")); + lists.add(Arrays.asList("c#", "java", "php", "python")); + long total = lists.stream() + // flatMap 可以减少一次循环 + .flatMap(Collection::stream) + .filter(str -> str.length() > 3) + .count(); + assertEquals(8, total); + } + + @Test + void match() { + List words = Lists.newArrayList("abc", "acb", "ace"); + boolean allMathch = words.stream() + .allMatch(w -> w.startsWith("a")); + assertTrue(allMathch); + boolean noneMatch = words.stream() + .noneMatch(w -> w.contains("z")); + assertTrue(noneMatch, "words does'nt contains z!"); + } + + @Test + void counter() { + long count = getLanguages() + .stream() + .filter((s) -> s.contains("p")) + .count(); + assertEquals(2, count, "The number of languages containing p is 2"); + } + + @Test + void filter() { + // 过滤出素食的菜单 + List vegetarianMenu = + Dish.MENU.stream() + .filter(Dish::isVegetarian) + .collect(toList()); + assertEquals("french fries", vegetarianMenu.get(0).getName()); + // 过滤 能量大于300的3个菜单 + List dishesLimit3 = + Dish.MENU.stream() + .filter(d -> d.getCalories() > 300) + .limit(3) + .collect(toList()); + assertEquals(3, dishesLimit3.size()); + } + + /** + * 分组分片 + */ + @Test + void groupBy() { + Map> localsOfCountry = Stream.of(Locale.getAvailableLocales()) + .collect(Collectors.groupingBy(Locale::getCountry)); + localsOfCountry.get("CH") + .stream() + .map(Locale::getLanguage) + .forEach(System.out::println); + // 按国家代号统计语言总数 + Map countLocalsOfCountry = Stream.of(Locale.getAvailableLocales()) + .collect(Collectors.groupingBy(Locale::getCountry, Collectors.counting())); + long count = countLocalsOfCountry.get("CH"); + assertEquals(3, count); + } + + @Test + void distinct(TestReporter reporter) { + String result = getLanguages() + .stream() + .map(s -> s.split("")) + .flatMap(Stream::of) + .distinct() + .map(String::valueOf) // 注意: 这里尽可能较少的处理 + .reduce(String::concat) + .orElse(""); + assertEquals("javphyton", result); + reporter.publishEntry("result >>>", result); + + } + + @Test + void reduce(TestReporter reporter) { + String result = getLanguages().stream() + .reduce((a, b) -> a.concat(";").concat(b)) + .orElse(""); + assertEquals(result, String.join(";", getLanguages())); + reporter.publishEntry("result is >>>", result); + int totalWords = getLanguages().stream() + .map(String::length) + .reduce(Integer::sum) + .orElse(0); + assertEquals(13, totalWords); + } + + @Test + void generate() { + List result = Stream + .generate(new FibonacciSupplier()) + .peek(log::info) + .skip(5) + .limit(5) + .collect(Collectors.toList()); + log.info("result is >>> {}", result); + } + + @Test + void iterate() { + Stream.iterate(0, n -> n + 2) + .limit(10) + .map(n -> n + " ") + .forEach(System.out::print); + } + + + class FibonacciSupplier implements Supplier { + + long a = 0; + long b = 1; + + @Override + public Long get() { + long x = a + b; + a = b; + b = x; + return a; + } + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/Dish.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/Dish.java new file mode 100755 index 0000000000000000000000000000000000000000..74fc6a7b363227569811b03ca5d9fd68e307f378 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/Dish.java @@ -0,0 +1,40 @@ +package com.brook.example.java8.stream.forker; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; + +@Getter +public class Dish { + + private final String name; + private final boolean vegetarian; + private final int calories; + private final Type type; + + public Dish(String name, boolean vegetarian, int calories, Type type) { + this.name = name; + this.vegetarian = vegetarian; + this.calories = calories; + this.type = type; + } + + public enum Type { MEAT, FISH, OTHER } + + @Override + public String toString() { + return name; + } + + public static final List MENU = + Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), + new Dish("beef", false, 700, Dish.Type.MEAT), + new Dish("chicken", false, 400, Dish.Type.MEAT), + new Dish("french fries", true, 530, Dish.Type.OTHER), + new Dish("rice", true, 350, Dish.Type.OTHER), + new Dish("season fruit", true, 120, Dish.Type.OTHER), + new Dish("pizza", true, 550, Dish.Type.OTHER), + new Dish("prawns", false, 400, Dish.Type.FISH), + new Dish("salmon", false, 450, Dish.Type.FISH)); +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/StreamForker.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/StreamForker.java new file mode 100755 index 0000000000000000000000000000000000000000..d29b025aa5f11ba20ad69abdb60a674a497e9423 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/StreamForker.java @@ -0,0 +1,175 @@ +package com.brook.example.java8.stream.forker; + +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * 复制流 + *

+ * {@code StreamForker} 在一个流上执行多个操作。 + * 因为Java 8中,流有一个非常大的(也可能是最大的)局限性,使用时,对它操作一次仅能得到 + * 一个处理结果。 + */ +public class StreamForker { + + private final Stream stream; + private final Map, ?>> forks = new HashMap<>(); + + public StreamForker(Stream stream) { + this.stream = stream; + } + + + public StreamForker fork(Object key, Function, ?> f) { + forks.put(key, f); + return this; + } + + public Results getResults() { + ForkingStreamConsumer consumer = build(); + try { + stream.sequential().forEach(consumer); + } finally { + consumer.finish(); + } + return consumer; + } + + /** + * 构建一个消费流,把结果放到阻塞队列中 + * @return + */ + private ForkingStreamConsumer build() { + List> queues = new ArrayList<>(); + + Map> actions = + forks.entrySet().stream().reduce(new HashMap>(), + (map, e) -> { + map.put(e.getKey(), + getOperationResult(queues, e.getValue())); + return map; + }, + (m1, m2) -> { + m1.putAll(m2); // 经个人测试这的代码没有效果 + return m1; + }); + + return new ForkingStreamConsumer<>(queues, actions); + } + + /** + * 创建一个新的{@code BlockingQueue},并将其添加到队列的列表。 这个队列会被传递给一个 + * 新的{@code BlockingQueueSpliterator }对象,后者是一个延迟绑定的 {@code Spliterator}, + * 它会遍历读取队列中的每个元素; + * @param queues + * @param f + * @return + */ + private Future getOperationResult(List> queues, Function, ?> f) { + BlockingQueue queue = new LinkedBlockingQueue<>(); + queues.add(queue); + Spliterator spliterator = new BlockingQueueSpliterator<>(queue); + Stream source = StreamSupport.stream(spliterator, false); + return CompletableFuture.supplyAsync(() -> f.apply(source)); + } + + public interface Results { + R get(Object key); + } + + private static class ForkingStreamConsumer implements Consumer, Results { + static final Object END_OF_STREAM = new Object(); + + private final List> queues; + private final Map> actions; + + ForkingStreamConsumer(List> queues, Map> actions) { + this.queues = queues; + this.actions = actions; + } + + @Override + public void accept(T t) { + queues.forEach(q -> q.add(t)); + } + + @Override + public R get(Object key) { + try { + return ((Future) actions.get(key)).get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void finish() { + accept((T) END_OF_STREAM); + } + } + + private static class BlockingQueueSpliterator implements Spliterator { + private final BlockingQueue q; + + BlockingQueueSpliterator(BlockingQueue q) { + this.q = q; + } + + /** + * + * @implNote 依据 {@link #getOperationResult}方法创建 {@link Spliterator } + * 同样的方式,这些元素会被作为进一步处理流的源头传递给Consumer对象 (在流上要执行的 + * 函数会作为参数传递给某个fork方法调用)。 + * {@link #tryAdvance(Consumer)} 方法返回true通知调用方还有其他 + * 的元素需要处理,直到它发现由 {@code ForkingSteamConsumer} 添加的特殊对象 + * ,表明队列中已经没有更多需要处理的元素了。 + * @param action + * @return boolean + */ + @Override + public boolean tryAdvance(Consumer action) { + T t; + while (true) { + try { + t = q.take(); + break; + } catch (InterruptedException e) { + } + } + + if (t != ForkingStreamConsumer.END_OF_STREAM) { + action.accept(t); + return true; + } + + return false; + } + + /** + * 由于无法预测能从队列中取得多少个元素,所以{@code estimatedSize}方法也无法返回 + * 任何有意 义的值。更进一步,由于你没有试图进行任何切分,所以这时的估算也没什么用处 + * + * @return + */ + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/StreamForkerExample.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/StreamForkerExample.java new file mode 100755 index 0000000000000000000000000000000000000000..336ae631c9c335cd194790bf9e49eea48527bb06 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/forker/StreamForkerExample.java @@ -0,0 +1,43 @@ +package com.brook.example.java8.stream.forker; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class StreamForkerExample { + + @Test + void testProcessMenu(){ + processMenu(); + } + private static void processMenu() { + Stream menuStream = Dish.MENU.stream(); + + StreamForker.Results results = new StreamForker(menuStream) + // 获取菜单 + .fork("shortMenu", s -> s.map(Dish::getName).collect(joining(", "))) + // 统计卡路里 + .fork("totalCalories", s -> s.mapToInt(Dish::getCalories).sum()) + // 哪个菜单的热量最多 + .fork("mostCaloricDish", s -> s.reduce((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2) + .get()) + // 按类型分组 + .fork("dishesByType", s -> s.collect(groupingBy(Dish::getType))) + .getResults(); + + String shortMenu = results.get("shortMenu"); + int totalCalories = results.get("totalCalories"); + Dish mostCaloricDish = results.get("mostCaloricDish"); + Map> dishesByType = results.get("dishesByType"); + + assertEquals(Dish.MENU.size(),shortMenu.split(",").length); + assertEquals(4300, totalCalories); + assertEquals("pork",mostCaloricDish.getName()); + assertEquals(Dish.Type.values().length,dishesByType.size()); + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounter.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..c88838eb6c5421a8b5fbe113585993bad020c40b --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounter.java @@ -0,0 +1,36 @@ +package com.brook.example.java8.stream.spliterator; + +/** + * 字符串中的数字计算器实现 + */ +public class NumCounter { + + private int num; + private int sum; + // 是否是一个完整的数字 + private boolean isWholeNum; + + public NumCounter(int num, int sum, boolean isWholeNum) { + this.num = num; + this.sum = sum; + this.isWholeNum = isWholeNum; + } + + public NumCounter accumulate(Character c) { + if (Character.isDigit(c)) { + return isWholeNum ? new NumCounter(Integer.parseInt("" + c), + sum + num, false) + : new NumCounter(Integer.parseInt("" + num + c), sum, false); + } else { + return new NumCounter(0, sum + num, true); + } + } + + public NumCounter combine(NumCounter numCounter) { + return new NumCounter(numCounter.num, this.getSum() + numCounter.getSum(), numCounter.isWholeNum); + } + + public int getSum() { + return sum + num; + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounterSpliterator.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounterSpliterator.java new file mode 100644 index 0000000000000000000000000000000000000000..a79641dc4d3a0765bc753c418bf84c8839dd6871 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounterSpliterator.java @@ -0,0 +1,62 @@ +package com.brook.example.java8.stream.spliterator; + +import java.util.Spliterator; +import java.util.function.Consumer; + +/** + * Spliterator 详细参考 http://blog.csdn.net/anonymousprogrammer/article/details/76034365 + */ +class NumCounterSpliterator implements Spliterator { + + private String str; + private int currentChar = 0; + + public NumCounterSpliterator(String str) { + this.str = str; + } + + /** + * @param action + * @return + */ + @Override + public boolean tryAdvance(Consumer action) { + action.accept(str.charAt(currentChar++)); + return currentChar < str.length(); + } + + @Override + public Spliterator trySplit() { + + int currentSize = str.length() - currentChar; + if (currentSize < 10) return null; + + for (int pos = currentSize/2 + currentSize; pos < str.length(); pos++){ + if (pos+1 < str.length()){ + // 当前Character是数字,且下一个Character不是数字,才需要划分一个新的Spliterator + if (Character.isDigit(str.charAt(pos)) && !Character.isDigit(str.charAt(pos+1))){ + Spliterator spliterator = new NumCounterSpliterator(str.substring(currentChar, pos)); + currentChar = pos; + return spliterator; + } + }else { + if (Character.isDigit(str.charAt(pos))){ + Spliterator spliterator = new NumCounterSpliterator(str.substring(currentChar, pos)); + currentChar = pos; + return spliterator; + } + } + } + return null; + } + + @Override + public long estimateSize() { + return str.length() - currentChar; + } + + @Override + public int characteristics() { + return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE; + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounterTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fef022c4a0d9410142e5de074810b5ad52a9a158 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/stream/spliterator/NumCounterTest.java @@ -0,0 +1,36 @@ +package com.brook.example.java8.stream.spliterator; + +import org.junit.jupiter.api.Test; + +import java.util.Spliterator; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +public class NumCounterTest { + final static String arr = "12%3 21sdas s34d dfsdz45 R3 jo34 sjkf8 3$1P 213ikflsd fdg55 kfd"; + + private static int countNum(Stream stream) { + NumCounter numCounter = stream + .reduce(new NumCounter(0, 0, false) + , NumCounter::accumulate + ,NumCounter::combine); + return numCounter.getSum(); + } + + @Test + void counter() { + Stream stream = IntStream.range(0, arr.length()).mapToObj(arr::charAt); + System.out.println("ordered total: " + countNum(stream)); + + Spliterator spliterator = new NumCounterSpliterator(arr); + // 传入true表示是并行流 + Stream parallelStream = StreamSupport.stream(spliterator, true); + System.out.println("parallel total: " + countNum(parallelStream)); + } + +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/time/ClockTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/time/ClockTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7c68bcf631ac7afad7fe3fae02d80a8ace55286c --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/time/ClockTest.java @@ -0,0 +1,25 @@ +package com.brook.example.java8.time; + +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; + +import java.time.Clock; +import java.time.Instant; +import java.util.Date; + +/** + * @author Shaojun Liu + * @create 2017/7/29 + */ +@Log4j2 +public class ClockTest { + @Test + void clock() { + Clock clock = Clock.systemDefaultZone(); + log.info("current millis is {}",clock.millis()); + Instant instant = clock.instant(); + Date now = Date.from(instant); + log.info("current times is {}",now.getTime()); + + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/time/DateConvertUtils.java b/learn-java8-example/src/test/java/com/brook/example/java8/time/DateConvertUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..204f57ae221d11511115e29ea3ecc3fb69102cc4 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/time/DateConvertUtils.java @@ -0,0 +1,88 @@ +package com.brook.example.java8.time; + +import java.sql.Date; +import java.time.*; + +public class DateConvertUtils { + /** + * Calls {@link #asLocalDate(java.util.Date, ZoneId)} with the system default time zone. + */ + public static LocalDate asLocalDate(java.util.Date date) { + return asLocalDate(date, ZoneId.systemDefault()); + } + + /** + * Creates {@link LocalDate} from {@code java.util.Date} or it's subclasses. Null-safe. + */ + public static LocalDate asLocalDate(java.util.Date date, ZoneId zone) { + if (date == null) return null; + if (date instanceof java.sql.Date) return ((java.sql.Date) date).toLocalDate(); + else return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDate(); + } + + /** + * Calls {@link #asLocalDateTime(java.util.Date)} (Date, ZoneId)} with the system default time zone. + */ + public static LocalDateTime asLocalDateTime(java.util.Date date) { + return asLocalDateTime(date, ZoneId.systemDefault()); + } + + /** + * Creates {@link java.time.LocalDateTime} + * from {@code java.util.Date} or it's subclasses. Null-safe. + */ + public static LocalDateTime asLocalDateTime(java.util.Date date, ZoneId zone) { + if (date == null) return null; + if (date instanceof java.sql.Timestamp) + return ((java.sql.Timestamp) date).toLocalDateTime(); + else return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDateTime(); + } + + /** + * Calls {@link #asUtilDate(Object, ZoneId)} with the system default time zone. + */ + public static java.util.Date asUtilDate(Object date) { + return asUtilDate(date, ZoneId.systemDefault()); + } + + /** + * Creates a {@link java.util.Date} from various date objects. Is null-safe. Currently supports:

    *
  • {@link java.util.Date} *
  • {@link java.sql.Date} *
  • {@link java.sql.Timestamp} *
  • {@link java.time.LocalDate} *
  • {@link java.time.LocalDateTime} *
  • {@link java.time.ZonedDateTime} *
  • {@link java.time.Instant} *
* * @param zone Time zone, used only if the input object is LocalDate or LocalDateTime. * * @return {@link java.util.Date} (exactly this class, not a subclass, such as java.sql.Date) + */ + public static java.util.Date asUtilDate(Object date, ZoneId zone) { + if (date == null) return null; + if (date instanceof java.sql.Date || date instanceof java.sql.Timestamp) + return new java.util.Date(((java.util.Date) date).getTime()); + if (date instanceof java.util.Date) return (java.util.Date) date; + if (date instanceof LocalDate) + return java.util.Date.from(((LocalDate) date).atStartOfDay(zone).toInstant()); + if (date instanceof LocalDateTime) + return java.util.Date.from(((LocalDateTime) date).atZone(zone).toInstant()); + if (date instanceof ZonedDateTime) + return java.util.Date.from(((ZonedDateTime) date).toInstant()); + if (date instanceof Instant) return java.util.Date.from((Instant) date); + throw new UnsupportedOperationException("Don't know hot to convert " + date.getClass().getName() + " to java.util.Date"); + } + + /** + * Creates an {@link Instant} from {@code java.util.Date} or it's subclasses. Null-safe. + */ + public static Instant asInstant(Date date) { + if (date == null) return null; + else return Instant.ofEpochMilli(date.getTime()); + } + + /** + * Calls {@link #asZonedDateTime(Date, ZoneId)} with the system default time zone. + */ + public static ZonedDateTime asZonedDateTime(Date date) { + return asZonedDateTime(date, ZoneId.systemDefault()); + } + + /** + * Creates {@link ZonedDateTime} from {@code java.util.Date} or it's subclasses. Null-safe. + */ + public static ZonedDateTime asZonedDateTime(Date date, ZoneId zone) { + if (date == null) return null; + else return asInstant(date).atZone(zone); + } +} \ No newline at end of file diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/time/DateTimeTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/time/DateTimeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6a449001ccef30956b40d45e91851ef9343648b0 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/time/DateTimeTest.java @@ -0,0 +1,163 @@ +package com.brook.example.java8.time; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.chrono.JapaneseDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.*; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import static java.time.temporal.TemporalAdjusters.lastDayOfMonth; +import static java.time.temporal.TemporalAdjusters.nextOrSame; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Shaojun Liu + * @create 2017/7/31 + */ +public class DateTimeTest { + + public static final String DATE_PATTERN = "yyyy-MM-dd"; + public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; + private static final ThreadLocal formatters = + ThreadLocal.withInitial(() -> new SimpleDateFormat(DATETIME_PATTERN)); + + @Test + void useTemporalAdjuster() { + LocalDate date = LocalDate.of(2014, 3, 18); + date = date.with(nextOrSame(DayOfWeek.SUNDAY)); + assertThat(date.toString()).isEqualTo("2014-03-23"); + date = date.with(lastDayOfMonth()); + assertThat(date.toString()).isEqualTo("2014-03-31"); + + date = date.with(new NextWorkingDay()); + assertThat(date.toString()).isEqualTo("2014-04-01"); + date = date.with(nextOrSame(DayOfWeek.FRIDAY)); + assertThat(date).isEqualTo("2014-04-04"); + date = date.with(new NextWorkingDay()); + assertThat(date).isEqualTo("2014-04-07"); + + date = date.with(nextOrSame(DayOfWeek.FRIDAY)); + assertThat(date).isEqualTo("2014-04-11"); + date = date.with(temporal -> { + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); + int dayToAdd = 1; + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; + return temporal.plus(dayToAdd, ChronoUnit.DAYS); + }); + assertThat(date).isEqualTo("2014-04-14"); + } + + @Test + void useDateFormatter() { + LocalDate date = LocalDate.of(2017, 7, 7); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + DateTimeFormatter chinaFormatter = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.CHINA); + + assertThat(date.format(DateTimeFormatter.ISO_LOCAL_DATE)).isEqualTo("2017-07-07"); + assertThat(date.format(formatter)).isEqualTo("07/07/2017"); + assertThat(date.format(chinaFormatter)).isEqualTo("七月 2017"); + + DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder() + .appendText(ChronoField.DAY_OF_MONTH) + .appendLiteral(" ") + .appendText(ChronoField.MONTH_OF_YEAR) + .appendLiteral(" ") + .appendText(ChronoField.YEAR) + .parseCaseInsensitive() + .toFormatter(Locale.CHINESE); + + assertThat(date.format(complexFormatter)).isEqualTo("7 七月 2017"); + } + + @Test + void useOldDate() { + Date date = new Date(114, 2, 18); + System.out.println(formatters.get().format(date)); + Calendar calendar = Calendar.getInstance(); + calendar.set(2014, Calendar.FEBRUARY, 18); + assertThat(calendar.get(Calendar.MONTH)).isEqualTo(1); + } + + @Test + void useLocalDate() { + LocalDate date = LocalDate.of(2014, 3, 18); + int year = date.getYear(); + assertThat(year).isEqualTo(2014); + Month month = date.getMonth(); // MARCH + assertThat(month).isEqualTo(Month.MARCH); + int day = date.getDayOfMonth(); + assertThat(day).isEqualTo(18); + DayOfWeek week = date.getDayOfWeek(); + assertThat(week).isEqualTo(DayOfWeek.TUESDAY); + int len = date.lengthOfMonth(); + assertThat(len).isEqualTo(31); + boolean leap = date.isLeapYear(); + assertThat(leap).isFalse(); + + int y = date.get(ChronoField.YEAR); + int m = date.get(ChronoField.MONTH_OF_YEAR); + int d = date.get(ChronoField.DAY_OF_MONTH); + Assertions.assertAll("LocalDate ",()->{ + Assertions.assertEquals(2014, y); + Assertions.assertEquals(3, m); + Assertions.assertEquals(18, d); + }); + LocalTime time = LocalTime.of(13, 45, 20); + assertThat(time.toString()).isEqualTo("13:45:20"); + + LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); + LocalDateTime dt2 = LocalDateTime.of(date, time); + LocalDateTime dt3 = date.atTime(13, 45, 20); + LocalDateTime dt4 = date.atTime(time); + LocalDateTime dt5 = time.atDate(date); + assertThat(dt1).isEqualTo( "2014-03-18T13:45:20"); + assertThat(dt1).isEqualTo(dt2) + .isEqualTo(dt3) + .isEqualTo(dt4) + .isEqualTo(dt5); + Instant instant = Instant.ofEpochSecond(44 * 365 * 86400); + Instant now = Instant.now(); + + Duration d1 = Duration.between(LocalTime.of(13, 45, 10), time); + Duration d2 = Duration.between(instant, now); + // Duration 和 java.time.Period 区别 + // Duration 表示 LocalDateTime 而 Period 只是LocalDate + assertThat(d1.getSeconds()).isEqualTo(10); + + Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); + assertThat(threeMinutes.getSeconds()).isEqualTo(180); + JapaneseDate japaneseDate = JapaneseDate.from(date); + System.out.println(japaneseDate); + } + @Test + void convert(){ + LocalDateTime localDateTime = LocalDateTime.now(); + Date date = DateConvertUtils.asUtilDate(localDateTime); + Date date1 = new Date(); + assertThat(date).isEqualToIgnoringSeconds(date1); + String localDateTimeStr = DateConvertUtils.asLocalDateTime(date1) + .format(DateTimeFormatter.ofPattern(DATETIME_PATTERN)); + assertThat(localDateTimeStr).isEqualTo(localDateTime + .format(DateTimeFormatter.ofPattern(DATETIME_PATTERN))); + } + + private static class NextWorkingDay implements TemporalAdjuster { + @Override + public Temporal adjustInto(Temporal temporal) { + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); + int dayToAdd = 1; + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; + return temporal.plus(dayToAdd, ChronoUnit.DAYS); + } + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/time/TimezoneTest.java b/learn-java8-example/src/test/java/com/brook/example/java8/time/TimezoneTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11aa38a2bb93e674675ec5bf687f4f589634a9a9 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/time/TimezoneTest.java @@ -0,0 +1,22 @@ +package com.brook.example.java8.time; + +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; + +/** + * @author Shaojun Liu + * @create 2017/7/29 + */ +@Log4j2 +class TimezoneTest { + @Test + void test() { + log.info("zoneIds is {}", ZoneId.getAvailableZoneIds()); + ZoneId zone1 = ZoneId.of("Europe/Berlin"); + ZoneId zone2 = ZoneId.of("Brazil/East"); + log.info("zone1 rules is {} ", zone1.getRules()); + log.info("zone1 rules is {} ", zone2.getRules()); + } +} diff --git a/learn-java8-example/src/test/java/com/brook/example/java8/util/Base64Test.java b/learn-java8-example/src/test/java/com/brook/example/java8/util/Base64Test.java new file mode 100644 index 0000000000000000000000000000000000000000..33fb4f6609879c25561bcabb1828f8b9065f1f88 --- /dev/null +++ b/learn-java8-example/src/test/java/com/brook/example/java8/util/Base64Test.java @@ -0,0 +1,30 @@ +package com.brook.example.java8.util; + +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestReporter; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Shaojun Liu + * @create 2017/7/30 + */ +@Log4j2 +class Base64Test { + @Test + void base64(TestReporter reporter) { + final String text = "Base64 for Java 8!"; + final String encoded = Base64 + .getEncoder() + .encodeToString(text.getBytes(StandardCharsets.UTF_8)); + log.info("base64 encoded {}", encoded); + final String decoded = new String( + Base64.getDecoder().decode(encoded), + StandardCharsets.UTF_8); + assertEquals(text, decoded); + } +} diff --git a/pom.xml b/pom.xml index 6c99cabe9206ce13a53de2f97b4a365820f0fba8..4f58ea2a39a5ecb24561fe65de2cbafde72dcbd0 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ disruptor-example axon-cqrs-example utils + learn-java8-example diff --git a/utils/pom.xml b/utils/pom.xml index 53e2eeae8be122e2b167db19f083b441fe0736c3..65b883cd2172cd8f0b9a84aaa5214b2852090fc2 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -1,9 +1,10 @@ - example com.brook.example + example 1.0-SNAPSHOT + pom.xml 4.0.0 @@ -17,5 +18,16 @@ UTF-8 - + + + org.apache.commons + commons-io + 1.3.2 + + + com.google.guava + guava + 20.0 + + diff --git a/utils/src/main/java/com/brook/example/utils/io/FileCharset.java b/utils/src/main/java/com/brook/example/utils/io/FileCharset.java new file mode 100644 index 0000000000000000000000000000000000000000..17dc81cb544db18f7a209f651ad7633a684d74d4 --- /dev/null +++ b/utils/src/main/java/com/brook/example/utils/io/FileCharset.java @@ -0,0 +1,104 @@ +package com.brook.example.utils.io; + +import org.apache.commons.io.IOUtils; + +import java.io.*; + +/** + * + * 判断文件编码 不保证100%正确性 但测试结果是一般的文件没问题 + *

+ * 只能判断常见的GBK,UTF-16LE,UTF-16BE,UTF-8,其分别对应window下的记事本可另存为的编码类型ANSI,Unicode,Unicode big endian,UTF-8 + *

+ * @author Shaojun Liu + * @create 2017/7/28 + */ +public interface FileCharset { + String DEFAULT_CHARSET = "GBK"; + + static String getCharset(String fileName) { + return getCharset(new File(fileName)); + } + + /** + * 只能判断常见的GBK,UTF-16LE,UTF-16BE,UTF-8,
+ * 其分别对应window下的记事本可另存为的 + * 编码类型ANSI,Unicode,Unicode big endian,UTF-8 + * + * @param file + * @return + */ + static String getCharset(File file) { + InputStream is = null; + try { + is = new FileInputStream(file); + return getCharset(new BufferedInputStream(is)); + } catch (FileNotFoundException e) { + return DEFAULT_CHARSET; + } finally { + IOUtils.closeQuietly(is); + } + } + + static String getCharset(final BufferedInputStream is) { + String charset = DEFAULT_CHARSET; + byte[] first3Bytes = new byte[3]; + try { + boolean checked = false; + is.mark(0); + int read = is.read(first3Bytes, 0, 3); + if (read == -1) + return charset; + if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) { + charset = "UTF-16LE"; + checked = true; + } else if (first3Bytes[0] == (byte) 0xFE + && first3Bytes[1] == (byte) 0xFF) { + charset = "UTF-16BE"; + checked = true; + } else if (first3Bytes[0] == (byte) 0xEF + && first3Bytes[1] == (byte) 0xBB + && first3Bytes[2] == (byte) 0xBF) { + charset = "UTF-8"; + checked = true; + } + is.reset(); + if (!checked) { + int loc = 0; + + while ((read = is.read()) != -1 && loc < 100) { + loc++; + if (read >= 0xF0) + break; + if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK + break; + if (0xC0 <= read && read <= 0xDF) { + read = is.read(); + if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF) + // (0x80 + // - 0xBF),也可能在GB编码内 + continue; + else + break; + } else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错,但是几率较小 + read = is.read(); + if (0x80 <= read && read <= 0xBF) { + read = is.read(); + if (0x80 <= read && read <= 0xBF) { + charset = "UTF-8"; + break; + } else + break; + } else + break; + } + } + } + is.reset(); + } catch (Exception e) { + e.printStackTrace(); + } + + return charset; + } +} diff --git a/utils/src/main/java/com/brook/example/utils/io/RemoteFileFetcher.java b/utils/src/main/java/com/brook/example/utils/io/RemoteFileFetcher.java new file mode 100644 index 0000000000000000000000000000000000000000..70c04c42335d2a77171b863477745c98b98efe03 --- /dev/null +++ b/utils/src/main/java/com/brook/example/utils/io/RemoteFileFetcher.java @@ -0,0 +1,79 @@ +package com.brook.example.utils.io; + +import com.brook.example.utils.lang.Console; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 定时抓取远程文件 + * @author brook + * @create 2017/7/7 + */ +public class RemoteFileFetcher { + + private static final ScheduledExecutorService scheduledExecutorService = Executors + .newSingleThreadScheduledExecutor(r -> new Thread(r, "RemoteFileFetcher_Schedule_Thread")); + private byte[] fileConent; + private String url; + private long lastModified; + private int connectTimeout; + private int readTimeout; + private FileChangeListener listener; + + private RemoteFileFetcher(String url, int reloadInterval, FileChangeListener listener) { + this.connectTimeout = 1000; + this.readTimeout = 1000; + + this.url = url; + this.listener = listener; + if (reloadInterval > 0) { + scheduledExecutorService.scheduleWithFixedDelay( + RemoteFileFetcher.this::doFetch, + reloadInterval, reloadInterval, TimeUnit.MILLISECONDS); + } + doFetch(); + } + + private void doFetch() { + if (url == null) { + return; + } + Console.log("Begin fetch remote file... url = {}", this.url); + try { + URL target = new URL(this.url); + this.fileConent = IOUtils.toByteArray((InputStream) target.getContent()); + this.lastModified = System.currentTimeMillis(); + if (this.listener != null && this.fileConent != null) { + this.listener.fileReloaded(this.fileConent); + } + } catch (Exception e) { + Console.error("read from url failed", e); + } + } + + public static RemoteFileFetcher createPeriodFetcher(String url, + int reloadInterval, + FileChangeListener listener) { + + return new RemoteFileFetcher(url, reloadInterval, listener); + + } + + public long getLastModified() { + return this.lastModified; + } + + public byte[] getFileByteArray() { + return this.fileConent; + } + + public interface FileChangeListener { + void fileReloaded(byte[] contentBytes) throws IOException; + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/brook/example/utils/lang/ArrayUtil.java b/utils/src/main/java/com/brook/example/utils/lang/ArrayUtil.java index 5e6da6f87a412176b02b7dbfddcbaede51b66610..b90622834d0d02f9859e147eb5166675a3449d51 100644 --- a/utils/src/main/java/com/brook/example/utils/lang/ArrayUtil.java +++ b/utils/src/main/java/com/brook/example/utils/lang/ArrayUtil.java @@ -1,7 +1,6 @@ package com.brook.example.utils.lang; import com.brook.example.utils.text.Strs; -import com.sun.xml.internal.ws.util.UtilException; import java.util.Arrays; import java.util.Iterator; @@ -52,7 +51,7 @@ public interface ArrayUtil { case "double": return Arrays.toString((double[]) obj); default: - throw new UtilException(e); + throw new RuntimeException(e); } } } @@ -105,7 +104,7 @@ public interface ArrayUtil { * * @param obj 对象,可以是对象数组或者基本类型数组 * @return 包装类型数组或对象数组 - * @throws UtilException 对象为非数组 + * @throws RuntimeException 对象为非数组 */ static Object[] wrap(Object obj) { if (isArray(obj)) { @@ -131,11 +130,11 @@ public interface ArrayUtil { case "double": return wrap(obj); default: - throw new UtilException(e); + throw new RuntimeException(e); } } } - throw new UtilException(Strs.format("[{}] is not Array!", obj.getClass())); + throw new RuntimeException(Strs.format("[{}] is not Array!", obj.getClass())); } } diff --git a/utils/src/main/java/com/brook/example/utils/text/ForbiddenWordUtils.java b/utils/src/main/java/com/brook/example/utils/text/ForbiddenWordUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..44dad3cd92984e5de0f282467dc4b9dfd27883ec --- /dev/null +++ b/utils/src/main/java/com/brook/example/utils/text/ForbiddenWordUtils.java @@ -0,0 +1,140 @@ +package com.brook.example.utils.text; + +import com.brook.example.utils.io.RemoteFileFetcher; +import com.google.common.base.Charsets; +import com.google.common.collect.Lists; +import lombok.experimental.UtilityClass; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 屏蔽关键词 工具类 + * @author brook + * @create 2017/7/7 + */ +@Log4j2 +@UtilityClass +public class ForbiddenWordUtils { + + /** + * 默认的遮罩文字 + */ + private static final String DEFAULT_MASK = "***"; + /** + * 屏蔽关键词抓取的url + */ + private static String forbiddenWordFetchURL; + + /** + * 屏蔽关键词抓取时间间隔 毫秒 + */ + private static int reloadInterval = 60000; //10分钟 + + /** + * 屏蔽关键词 + */ + private static List forbiddenWords; + + public static void setForbiddenWordFetchURL(String forbiddenWordFetchURL) { + ForbiddenWordUtils.forbiddenWordFetchURL = forbiddenWordFetchURL; + } + + public static void setReloadInterval(int reloadInterval) { + ForbiddenWordUtils.reloadInterval = reloadInterval; + } + + /** + * 替换input中的屏蔽关键词为默认的掩码 + * + * @param input + * @return + */ + public static String replace(String input) { + return replace(input, DEFAULT_MASK); + } + + /** + * 将屏蔽关键词 替换为 mask + * + * @param input + * @param mask + * @return + */ + public static String replace(String input, String mask) { + for (int i = 0, l = forbiddenWords.size(); i < l; i++) { + Pattern forbiddenWordPattern = forbiddenWords.get(i); + input = forbiddenWordPattern.matcher(input).replaceAll(mask); + } + return input; + } + + + /** + * 是否包含屏蔽关键词 + * + * @param input + * @return + */ + public static boolean containsForbiddenWord(String input) { + for (int i = 0, l = forbiddenWords.size(); i < l; i++) { + Pattern forbiddenWordPattern = forbiddenWords.get(i); + if (forbiddenWordPattern.matcher(input).find()) { + return true; + } + } + return false; + } + + + static { + InputStream is = null; + try { + String fileName = "forbidden.txt"; + is = ForbiddenWordUtils.class.getClassLoader().getResourceAsStream(fileName); + byte[] fileCBytes; + fileCBytes = IOUtils.toByteArray(is); + ForbiddenWordUtils.loadForbiddenWords(fileCBytes); + } catch (IOException e) { + log.error("read forbidden file failed", e); + } finally { + IOUtils.closeQuietly(is); + } + + } + + /** + * 初始化远程抓取配置 + */ + public static void initRemoteFetch() { + RemoteFileFetcher.createPeriodFetcher( + forbiddenWordFetchURL, + reloadInterval, + fileConent -> ForbiddenWordUtils.loadForbiddenWords(fileConent)); + } + + private static void loadForbiddenWords(byte[] fileCBytes) throws IOException { + Reader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(fileCBytes), Charsets.UTF_8)); + List forbiddenWordsStrList = IOUtils.readLines(reader); + forbiddenWords = Lists.newArrayList(); + for (int i = forbiddenWordsStrList.size() - 1; i >= 0; i--) { + String forbiddenWord = forbiddenWordsStrList.get(i).trim(); + if (forbiddenWord.length() == 0 || forbiddenWord.startsWith("#")) { + continue; + } else { + forbiddenWords.add(Pattern.compile(forbiddenWord)); + } + } + } catch (Exception e) { + log.error("load forbidden words failed", e); + } finally { + IOUtils.closeQuietly(reader); + } + } + +} \ No newline at end of file diff --git a/utils/src/main/resources/forbidden.txt b/utils/src/main/resources/forbidden.txt new file mode 100644 index 0000000000000000000000000000000000000000..88da85160ca6bca0df6d0ed2bb6b7e3289c2bba0 --- /dev/null +++ b/utils/src/main/resources/forbidden.txt @@ -0,0 +1,1030 @@ +#google的 +马驰.*新加坡 +新加坡.*马驰 +自由光诚 +陈光诚事件 +光诚.*沂南 +沂南.*光诚 +陈光诚.*使馆 +使馆.*陈光诚 +职称英语.*答案 +答案.*职称英语 +公务员.*答案 +答案.*公务员 +薄瓜瓜 +海伍德 +尼尔伍德 +heywood +neil.*wood +wood.*neil +天线宝宝.*康师傅 +康师傅.*天线宝宝 +天线宝宝.*方便面 +方便面.*天线宝宝 +天线宝宝.*轮胎 +轮胎.*天线宝宝 +轮胎.*方便面 +方便面.*轮胎 +政变 +枪声 +戒严 +3\.19 +北京事件 +北京.*出事了 +出事了.*北京 +北京怎么了 +不厚 +薄督 +谷开来 +重庆 +叶城.*砍杀 +砍杀.*叶城 +连承敏 +弟弟.*睡 +睡.*弟弟 +要有光.*要有诚 +要有诚.*要有光 +杨杰 +陈刚 +山水文园 +跑官 +移动.*十年兴衰 +十年兴衰.*移动 +陈坚 +戴坚 +冯珏 +罗川 +马力 +盛勇 +谢岷 +谢文 +杨希 +叶兵 +张斌 +陈瑞卿 +高念书 +华如秀 +鲁向东 +曲乃杰 +孙静晔 +涂志森 +于剑鸣 +张晓明 +赵志强 +郑建源 +先皇 +太上皇 +蛤蟆 +驾崩 +丘小雄 +公诉 +右派 +增城 +暴动 +宣言 +莫日根 +内蒙古.*抗议 +抗议.*内蒙古 +西乌旗 +方滨兴 +moli +麦当劳 +天府 +人民公园 +广场 +埃及 +突尼斯 +茉莉 +jasmine.*revolution +revolution.*jasmine +集会 +革命 +齐鲁银行 +公开信 +高考时间 +诺贝尔和平奖 +被就业 +小屋 +日记 +鲁昕 +天安.*事件 +事件.*天安 +1989.*天安門 +天安門.*1989 +天安门 +八九 +六四 +六 四 +六\.四 +平反64 +5月35日 +5月35号 +89动乱 +89.*学生动乱 +学生动乱.*89 +89.*学生运动 +学生运动.*89 +64.*学生运动 +学生运动.*64 +64.*镇压 +镇压.*64 +64.*真相 +真相.*64 +64memo +tiananmen +8964 +学潮 +罢课 +民运 +学运 +学联 +学自联 +高自联 +工自联 +民联 +民阵 +中国民主党 +中国民主正义党 +中国民主运动 +世纪中国基金会 +坦克人 +挡坦克 +tankman +木犀地 +维园晚会 +blood is on the square +姜维平 +艾未未 +艾末末 +路青 +发课 +余杰 +辛子陵 +茅于轼 +铁流 +liu.*xiaobo +xiaobo.*liu +蟹农场 +陈西 +谭作人 +高智晟 +冯正虎 +丁子霖 +唯色 +焦国标 +何清涟 +耀邦 +紫阳 +方励之 +严家其 +鲍彤 +鮑彤 +鲍朴 +柴玲 +乌尔凯西 +封从德 +炳章 +苏绍智 +陈一谘 +韩东方 +辛灏年 +曹长青 +陈破空 +盘古乐队 +盛雪 +伍凡 +魏京生 +司徒华 +黎安友 +防火长城 +great.*firewall +firewall.*great +gfw.*什么 +什么.*gfw +国家防火墙 +翻墙 +代理 +vpn.*免费 +免费.*vpn +vpn.*下载 +下载.*vpn +vpn.*世纪 +世纪.*vpn +hotspot.*shield +shield.*hotspot +无界 +ultrasurf +^freenet +safeweb +动态网 +花园网 +^cache +阅后即焚 +法轮 +falun +明慧 +minghui +退党 +三退 +九评 +nine commentaries +洪吟 +神韵艺术 +神韵晚会 +人民报 +renminbao +纪元 +^dajiyuan +epochtimes +新唐人 +ntdtv +ndtv +新生网 +^xinsheng +正见网 +zhengjian +追查国际 +真善忍 +法会 +正念 +经文 +天灭 +天怒 +讲真相 +马三家 +善恶有报 +活摘器官 +群体灭绝 +中功 +张宏堡 +地下教会 +冤民大同盟 +达赖 +藏独 +freetibet +雪山狮子 +西藏流亡政府 +青天白日旗 +民进党 +洪哲胜 +独立台湾会 +台湾政论区 +台湾自由联盟 +台湾建国运动组织 +台湾.*独立联盟 +独立联盟.*台湾 +新疆.*独立 +独立.*新疆 +东土耳其斯坦 +east.*turkistan +turkistan.*east +世维会 +迪里夏提 +美国之音 +自由亚洲电台 +记者无疆界 +维基解密.*中国 +中国.*维基解密 +facebook +twitter +推特 +新京报 +世界经济导报 +中国数字时代 +^ytht +新语丝 +^creaders +^tianwang +中国.*禁闻 +禁闻.*中国 +阿波罗网 +阿波罗新闻 +大参考 +^bignews +多维 +看中国 +博讯 +^boxun +peacehall +^hrichina +独立中文笔会 +华夏文摘 +开放杂志 +大家论坛 +华夏论坛 +中国论坛 +木子论坛 +争鸣论坛 +大中华论坛 +反腐败论坛 +新观察论坛 +新华通论坛 +正义党论坛 +热站政论网 +华通时事论坛 +华语世界论坛 +华岳时事论坛 +两岸三地论坛 +南大自由论坛 +人民之声论坛 +万维读者论坛 +你说我说论坛 +东西南北论坛 +东南西北论谈 +知情者 +红太阳的陨落 +和谐拯救危机 +血房 +一个孤僻的人 +零八.*宪章 +宪章.*零八 +08.*宪章 +宪章.*08 +八宪章 +8宪章 +零八.*县长 +县长.*零八 +08县长 +淋巴县长 +我的最后陈述 +我没有敌人 +河殇 +天葬 +黄祸 +我的奋斗 +历史的伤口 +改革.*历程 +历程.*改革 +国家的囚徒 +prisoner of the state +改革年代的政治斗争 +改革年代政治斗争 +关键时刻 +超越红墙 +梦萦未名湖 +一寸山河一寸血 +政治局常委内幕 +北国之春 +北京之春 +中国之春 +东方红时空 +纳米比亚 +婴儿汤 +泄题 +罢餐 +月月 +代开.*发票 +发票.*代开 +钓鱼岛 +^triangle +女保镖 +玩ps +玩photoshop +chinese people eating babies +开枪 +迫害 +酷刑 +邪恶 +洗脑 +网特 +内斗 +党魁 +文字狱 +一党专政 +一党独裁 +新闻封锁 +老人政治 +^freedom +^freechina +反社会 +维权人士 +维权律师 +异见人士 +异议人士 +地下刊物 +高瞻 +共产 +共铲党 +共残党 +共惨党 +共匪 +赤匪 +中共 +中宣 +真理部 +十八大 +18大 +太子 +上海帮 +团派 +北京当局 +裆中央 +九常委 +九长老 +锦涛 +家宝 +影帝 +近平 +回良玉 +汪洋 +张高丽 +俞正声 +徐才厚 +郭伯雄 +熙来 +梁光烈 +孟建柱 +戴秉国 +马凯 +令计划 +韩正 +章沁生 +陈世炬 +泽民 +贼民 +邓小平 +庆红 +罗干 +假庆淋 +hujin +wenjiabao +xijinping +likeqiang +zhouyongkang +lichangchun +wubangguo +heguoqiang +jiaqinglin +jiangzemin +xjp +jzm +色情 +花花公子 +tits +boobs +^\s*海峰\s*$ +^\s*威视公司\s*$ +^\s*nuctech\s*$ +^\s*逍遥游\s*$ +^\s*自由门\s*$ +^\s*自由門\s*$ +^\s*自由之门\s*$ +^\s*freegate\s*$ +^\s*freegate download\s*$ +^\s*download freegate\s*$ +^\s*自由门下载\s*$ +^\s*自由門下載\s*$ +^\s*無界瀏覽\s*$ +^\s*無界浏览\s*$ +^\s*动网通\s*$ +^\s*dynaweb\s*$ +^\s*dongtaiwang\s*$ +高潮 +诱惑 +快感 +淫 +婊子 +勃起 +博讯 +操逼 +操她 +操你 +操他 +操我 +插你 +车仑 +赤匪 +抽插 +臭逼 +吹箫 +打炮 +大b +大逼 +大波 +吊毛 +二逼 +发抡 +发仑 +发伦 +发囵 +发轮 +发论 +法功 +法愣 +法抡 +法仑 +法伦 +法囵 +法纶 +法轮 +肥逼 +肥穴 +分裂 +阝月 +干逼 +肛交 +工力 +功法 +功友 +共党 +共匪 +共狗 +共军 +狗操 +狗日 +龟公 +龟头 +哈批 +豪乳 +宏志 +洪志 +黄菊 +回民 +鸡八 +鸡巴 +鸡吧 +妓女 +奸轮 +贱逼 +贱比 +贱货 +贱人 +江猪 +叫床 +精液 +巨波 +巨乳 +口交 +狂操 +捆绑 +烂逼 +烂比 +烂货 +浪穴 +炼功 +六四 +轮功 +轮奸 +妈批 +卖逼 +卖比 +蒙独 +迷奸 +蜜穴 +民运 +民猪 +明慧 +奶头 +奶子 +皮条 +屁眼 +嫖娼 +嫖妓 +强暴 +强鸡 +强奸 +求欢 +群奸 +群交 +日B +日逼 +日比 +日她 +日你 +日死 +日我 +肉棒 +肉洞 +乳交 +瑞环 +色诱 +傻B +傻逼 +射精 +绳虐 +兽交 +熟女 +氵去 +水扁 +私处 +外阴 +我操 +我日 +小逼 +小洞 +小穴 +泄欲 +性爱 +性交 +性梦 +性奴 +性虐 +性事 +性欲 +艳情 +阳具 +阳痿 +阴部 +阴唇 +阴道 +阴蒂 +阴茎 +阴毛 +阴水 +淫手 +杂种 +招妓 +正法 +猪操 +猪毛 +自焚 +自虐 +自慰 +作爱 +做爱 +操你妈 +操你娘 +处女膜 +打飞机 +大参考 +大花逼 +大纪元 +你妈 +你娘 +动态网 +发抡功 +发伦功 +发论公 +发论功 +发正念 +反封锁 +干你妈 +干你娘 +干死你 +共产党 +狗卵子 +古怪歌 +回民暴 +简鸿章 +江八点 +江独裁 +江流氓 +江戏子 +江.?民 +江贼民 +江折民 +江猪媳 +僵贼民 +酱猪媳 +靠你妈 +李大师 +李登辉 +李红痔 +李宏志 +李洪志 +李总统 +毛厕洞 +毛贼东 +蒙古独 +你妈逼 +人民报 +日你妈 +日你娘 +日死你 +十八摸 +十六大 +四川独 +台湾独 +台湾狗 +我操你 +小鸡鸡 +性游戏 +一夜情 +一夜性 +真善忍 +中国猪 +朱容基 +猪聋畸 +操你老妈 +操你老母 +操你老娘 +操你奶奶 +大法弟子 +法轮大法 +干你老母 +干你奶奶 +共青团派 +回民暴动 +美国之音 +日你老妈 +日你老母 +日你老娘 +日你奶奶 +声色场所 +新华内情 +一边一国 +一党专制 +一中一台 +欲死欲仙 +处女终结者 +独立台湾会 +反封锁技术 +佛展千手法 +鸡毛信文汇 +神通加持法 +天安门事件 +天安门屠杀 +天安门一代 +联总之声传单 +人民内情真相 +人民真实报导 +人民真实报道 +氵去车仑工力 +天安门录影带 +中俄边界新约 +人民大众时事参考 +印尼伊斯兰祈祷团 +印尼依斯兰祈祷团 +东突厥斯坦伊斯兰运动 +乳交绳虐滴蜡 +操你妈逼 +日你妈逼 +奸夫淫妇 +人民报讯 +你妈的 +他妈的 +发轮功 +法抡功 +法轮功 +两性狂情 +操你吗逼 +乳房畜生 +骚逼 +淫逼 +淫荡 +淫妇 +骚货 +淫贱 +你老母 +你奶奶 +骚女 +淫片 +淫水 +骚穴 +淫穴 +东突厥斯坦依斯兰运动 +发骚 +风骚 +奸淫 +九评共产党 +卖骚 +卖淫 +去你妈的 +手淫 +维吾尔自由运动 +西藏独 +西藏独立 +新疆独 +成人 +淫片 +外阴 +小洞 +抽插 +泄欲 +激情 +性交 +性事 +性游戏 +色欲 +少女高潮 +X夜激情 +轮奸 +AV女优 +少儿勿入 +色诱 +声色场所 +骚逼 +卖骚 +两性狂情 +18岁以下勿入 +18岁以下勿看 +禁片 +AV女优 +处女终结者 +法轮 +法伦 +法抡 +大法 +共产 +艳情 +操逼 +日逼 +骚逼 +干逼 +操我 +操你 +操她 +性交 +性事 +淫贱 +淫荡 +淫逼 +淫水 +狂操 +操你妈 +操你奶奶 +淫穴 +蜜穴 +骚穴 +集会 +罢工 +游行 +狗日 +日她 +日你 +日我 +日批 +日逼 +日B +日比 +日死 +自焚 +主席 +大法 +真善忍 +愚民 +泽民 +台独 +锦涛 +温家宝 +李鹏 +瑞环 +邦国 +家宝 +云帆 +杨宁 +邓小平 +庆林 +庆红 +反共 +一塌糊涂 +共产党 +公安 +动态网 +六四 +fuck +六合彩 +法轮功 +李宏志 +真善忍 +宏志 +法伦 +法抡 +法囵 +法仑 +法纶 +发仑 +发囵 +自由运动 +法轮大法 +E周刊 +龙卷风 +大纪元 +大参考 +正法 +镇压 +达赖 +迫害 +嫖娼 +静坐 +政变 +造反 +分裂 +自焚 +弟子 +发轮功 +功友 +人民大众时事参考 +示威 +游行 +共匪 +明慧 +大法弟子 +疆独 +民运 +印尼伊斯兰祈祷团 +中俄边界新约 +大法 +政治运动 +压迫 +博讯 +反革命 +胡锦涛 +温家宝 +吴邦国 +江独裁 +江泽民 +杨宁 +云帆 +东突厥斯坦伊斯兰运动 +一边一国 +人民内情真相 +回民 +新闻封锁 +鸡毛信文汇 +新华举报 +新华内情 +政治风波 +江八点 +古怪歌 +民猪 +突厥斯坦 +广闻 +简鸿章 +人民报 +联总之声传单 +人民报讯 +东突 +美国之音 +人民真实报道 +教徒 +打倒 +推翻 +人权 +操你奶奶 +操你妈 +fa lun +falun +洪志 +f a lun +法轮 +大法 +李洪志 +自焚 +分裂 +西藏 +中华民国 +造反 +示威 +政变 +江泽民 +李鹏 +朱容基 +教徒 +人权 +迫害 +新华内情 +达赖 +镇压 +新闻封锁 +他妈的 +东突 +吕秀莲 +正法 +新华举报 +人民大众时事参考 +人民内情真相 +人民真实报导 +鸡毛信文汇 +联总之声传单 +美国之音 +民运 +人民报讯 +E周刊 +博讯 +人民报 +简鸿章 +迫害 +人权 +政变 +压迫 +反革命 +无能 +突厥斯坦 +印尼依斯兰祈祷团 +东突厥斯坦依斯兰运动 +疆独 +维吾尔自由运动 +自由运动 +1989天安门 +九评共产党 +一党专制 +太子党 +共青团派 +共党 +共匪 +共狗 +共军 +黄菊 +江流氓 +天安门事件 +共产党 +达赖 +藏独 +热比娅 +热比亚 +疆独 +台独 + +#测试的 +test \ No newline at end of file diff --git a/utils/src/test/java/com/brook/example/AppTest.java b/utils/src/test/java/com/brook/example/AppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1b1eb9725f732747c3b19aab7305c9e378517de9 --- /dev/null +++ b/utils/src/test/java/com/brook/example/AppTest.java @@ -0,0 +1,38 @@ +package com.brook.example; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/utils/src/test/java/com/brook/example/utils/text/ForbiddenWordUtilsTest.java b/utils/src/test/java/com/brook/example/utils/text/ForbiddenWordUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..92df550b46bf2579eacaa7daf3c140cd022446f3 --- /dev/null +++ b/utils/src/test/java/com/brook/example/utils/text/ForbiddenWordUtilsTest.java @@ -0,0 +1,52 @@ +package com.brook.example.utils.text; + +import org.junit.Assert; +import org.junit.Test; + +/** + * ForbiddenWordUtils test case. + * + * @author Shaojun Liu + * @create 2017/7/28 + */ +public class ForbiddenWordUtilsTest { + @Test + public void testReplaceWithDefaultMask() { + + String input = "你是傻逼"; + String expected = "你是***"; + String actual = ForbiddenWordUtils.replace(input); + Assert.assertEquals(expected, actual); + + } + + @Test + public void testReplaceWithDefaultMask2() { + + String input = "你是主席吗"; + String expected = "你是***吗"; + String actual = ForbiddenWordUtils.replace(input); + Assert.assertEquals(expected, actual); + + } + + + @Test + public void testReplaceWithDefaultMask3() { + + String input = "傻B123"; + String expected = "***123"; + String actual = ForbiddenWordUtils.replace(input); + Assert.assertEquals(expected, actual); + + } + + @Test + public void testReplaceWithCustomMask() { + String input = "傻B123"; + String expected = "###123"; + String actual = ForbiddenWordUtils.replace(input,"###"); + Assert.assertEquals(expected, actual); + } + +} \ No newline at end of file diff --git a/utils/src/test/resources/forbidden-test.txt b/utils/src/test/resources/forbidden-test.txt new file mode 100644 index 0000000000000000000000000000000000000000..88da85160ca6bca0df6d0ed2bb6b7e3289c2bba0 --- /dev/null +++ b/utils/src/test/resources/forbidden-test.txt @@ -0,0 +1,1030 @@ +#google的 +马驰.*新加坡 +新加坡.*马驰 +自由光诚 +陈光诚事件 +光诚.*沂南 +沂南.*光诚 +陈光诚.*使馆 +使馆.*陈光诚 +职称英语.*答案 +答案.*职称英语 +公务员.*答案 +答案.*公务员 +薄瓜瓜 +海伍德 +尼尔伍德 +heywood +neil.*wood +wood.*neil +天线宝宝.*康师傅 +康师傅.*天线宝宝 +天线宝宝.*方便面 +方便面.*天线宝宝 +天线宝宝.*轮胎 +轮胎.*天线宝宝 +轮胎.*方便面 +方便面.*轮胎 +政变 +枪声 +戒严 +3\.19 +北京事件 +北京.*出事了 +出事了.*北京 +北京怎么了 +不厚 +薄督 +谷开来 +重庆 +叶城.*砍杀 +砍杀.*叶城 +连承敏 +弟弟.*睡 +睡.*弟弟 +要有光.*要有诚 +要有诚.*要有光 +杨杰 +陈刚 +山水文园 +跑官 +移动.*十年兴衰 +十年兴衰.*移动 +陈坚 +戴坚 +冯珏 +罗川 +马力 +盛勇 +谢岷 +谢文 +杨希 +叶兵 +张斌 +陈瑞卿 +高念书 +华如秀 +鲁向东 +曲乃杰 +孙静晔 +涂志森 +于剑鸣 +张晓明 +赵志强 +郑建源 +先皇 +太上皇 +蛤蟆 +驾崩 +丘小雄 +公诉 +右派 +增城 +暴动 +宣言 +莫日根 +内蒙古.*抗议 +抗议.*内蒙古 +西乌旗 +方滨兴 +moli +麦当劳 +天府 +人民公园 +广场 +埃及 +突尼斯 +茉莉 +jasmine.*revolution +revolution.*jasmine +集会 +革命 +齐鲁银行 +公开信 +高考时间 +诺贝尔和平奖 +被就业 +小屋 +日记 +鲁昕 +天安.*事件 +事件.*天安 +1989.*天安門 +天安門.*1989 +天安门 +八九 +六四 +六 四 +六\.四 +平反64 +5月35日 +5月35号 +89动乱 +89.*学生动乱 +学生动乱.*89 +89.*学生运动 +学生运动.*89 +64.*学生运动 +学生运动.*64 +64.*镇压 +镇压.*64 +64.*真相 +真相.*64 +64memo +tiananmen +8964 +学潮 +罢课 +民运 +学运 +学联 +学自联 +高自联 +工自联 +民联 +民阵 +中国民主党 +中国民主正义党 +中国民主运动 +世纪中国基金会 +坦克人 +挡坦克 +tankman +木犀地 +维园晚会 +blood is on the square +姜维平 +艾未未 +艾末末 +路青 +发课 +余杰 +辛子陵 +茅于轼 +铁流 +liu.*xiaobo +xiaobo.*liu +蟹农场 +陈西 +谭作人 +高智晟 +冯正虎 +丁子霖 +唯色 +焦国标 +何清涟 +耀邦 +紫阳 +方励之 +严家其 +鲍彤 +鮑彤 +鲍朴 +柴玲 +乌尔凯西 +封从德 +炳章 +苏绍智 +陈一谘 +韩东方 +辛灏年 +曹长青 +陈破空 +盘古乐队 +盛雪 +伍凡 +魏京生 +司徒华 +黎安友 +防火长城 +great.*firewall +firewall.*great +gfw.*什么 +什么.*gfw +国家防火墙 +翻墙 +代理 +vpn.*免费 +免费.*vpn +vpn.*下载 +下载.*vpn +vpn.*世纪 +世纪.*vpn +hotspot.*shield +shield.*hotspot +无界 +ultrasurf +^freenet +safeweb +动态网 +花园网 +^cache +阅后即焚 +法轮 +falun +明慧 +minghui +退党 +三退 +九评 +nine commentaries +洪吟 +神韵艺术 +神韵晚会 +人民报 +renminbao +纪元 +^dajiyuan +epochtimes +新唐人 +ntdtv +ndtv +新生网 +^xinsheng +正见网 +zhengjian +追查国际 +真善忍 +法会 +正念 +经文 +天灭 +天怒 +讲真相 +马三家 +善恶有报 +活摘器官 +群体灭绝 +中功 +张宏堡 +地下教会 +冤民大同盟 +达赖 +藏独 +freetibet +雪山狮子 +西藏流亡政府 +青天白日旗 +民进党 +洪哲胜 +独立台湾会 +台湾政论区 +台湾自由联盟 +台湾建国运动组织 +台湾.*独立联盟 +独立联盟.*台湾 +新疆.*独立 +独立.*新疆 +东土耳其斯坦 +east.*turkistan +turkistan.*east +世维会 +迪里夏提 +美国之音 +自由亚洲电台 +记者无疆界 +维基解密.*中国 +中国.*维基解密 +facebook +twitter +推特 +新京报 +世界经济导报 +中国数字时代 +^ytht +新语丝 +^creaders +^tianwang +中国.*禁闻 +禁闻.*中国 +阿波罗网 +阿波罗新闻 +大参考 +^bignews +多维 +看中国 +博讯 +^boxun +peacehall +^hrichina +独立中文笔会 +华夏文摘 +开放杂志 +大家论坛 +华夏论坛 +中国论坛 +木子论坛 +争鸣论坛 +大中华论坛 +反腐败论坛 +新观察论坛 +新华通论坛 +正义党论坛 +热站政论网 +华通时事论坛 +华语世界论坛 +华岳时事论坛 +两岸三地论坛 +南大自由论坛 +人民之声论坛 +万维读者论坛 +你说我说论坛 +东西南北论坛 +东南西北论谈 +知情者 +红太阳的陨落 +和谐拯救危机 +血房 +一个孤僻的人 +零八.*宪章 +宪章.*零八 +08.*宪章 +宪章.*08 +八宪章 +8宪章 +零八.*县长 +县长.*零八 +08县长 +淋巴县长 +我的最后陈述 +我没有敌人 +河殇 +天葬 +黄祸 +我的奋斗 +历史的伤口 +改革.*历程 +历程.*改革 +国家的囚徒 +prisoner of the state +改革年代的政治斗争 +改革年代政治斗争 +关键时刻 +超越红墙 +梦萦未名湖 +一寸山河一寸血 +政治局常委内幕 +北国之春 +北京之春 +中国之春 +东方红时空 +纳米比亚 +婴儿汤 +泄题 +罢餐 +月月 +代开.*发票 +发票.*代开 +钓鱼岛 +^triangle +女保镖 +玩ps +玩photoshop +chinese people eating babies +开枪 +迫害 +酷刑 +邪恶 +洗脑 +网特 +内斗 +党魁 +文字狱 +一党专政 +一党独裁 +新闻封锁 +老人政治 +^freedom +^freechina +反社会 +维权人士 +维权律师 +异见人士 +异议人士 +地下刊物 +高瞻 +共产 +共铲党 +共残党 +共惨党 +共匪 +赤匪 +中共 +中宣 +真理部 +十八大 +18大 +太子 +上海帮 +团派 +北京当局 +裆中央 +九常委 +九长老 +锦涛 +家宝 +影帝 +近平 +回良玉 +汪洋 +张高丽 +俞正声 +徐才厚 +郭伯雄 +熙来 +梁光烈 +孟建柱 +戴秉国 +马凯 +令计划 +韩正 +章沁生 +陈世炬 +泽民 +贼民 +邓小平 +庆红 +罗干 +假庆淋 +hujin +wenjiabao +xijinping +likeqiang +zhouyongkang +lichangchun +wubangguo +heguoqiang +jiaqinglin +jiangzemin +xjp +jzm +色情 +花花公子 +tits +boobs +^\s*海峰\s*$ +^\s*威视公司\s*$ +^\s*nuctech\s*$ +^\s*逍遥游\s*$ +^\s*自由门\s*$ +^\s*自由門\s*$ +^\s*自由之门\s*$ +^\s*freegate\s*$ +^\s*freegate download\s*$ +^\s*download freegate\s*$ +^\s*自由门下载\s*$ +^\s*自由門下載\s*$ +^\s*無界瀏覽\s*$ +^\s*無界浏览\s*$ +^\s*动网通\s*$ +^\s*dynaweb\s*$ +^\s*dongtaiwang\s*$ +高潮 +诱惑 +快感 +淫 +婊子 +勃起 +博讯 +操逼 +操她 +操你 +操他 +操我 +插你 +车仑 +赤匪 +抽插 +臭逼 +吹箫 +打炮 +大b +大逼 +大波 +吊毛 +二逼 +发抡 +发仑 +发伦 +发囵 +发轮 +发论 +法功 +法愣 +法抡 +法仑 +法伦 +法囵 +法纶 +法轮 +肥逼 +肥穴 +分裂 +阝月 +干逼 +肛交 +工力 +功法 +功友 +共党 +共匪 +共狗 +共军 +狗操 +狗日 +龟公 +龟头 +哈批 +豪乳 +宏志 +洪志 +黄菊 +回民 +鸡八 +鸡巴 +鸡吧 +妓女 +奸轮 +贱逼 +贱比 +贱货 +贱人 +江猪 +叫床 +精液 +巨波 +巨乳 +口交 +狂操 +捆绑 +烂逼 +烂比 +烂货 +浪穴 +炼功 +六四 +轮功 +轮奸 +妈批 +卖逼 +卖比 +蒙独 +迷奸 +蜜穴 +民运 +民猪 +明慧 +奶头 +奶子 +皮条 +屁眼 +嫖娼 +嫖妓 +强暴 +强鸡 +强奸 +求欢 +群奸 +群交 +日B +日逼 +日比 +日她 +日你 +日死 +日我 +肉棒 +肉洞 +乳交 +瑞环 +色诱 +傻B +傻逼 +射精 +绳虐 +兽交 +熟女 +氵去 +水扁 +私处 +外阴 +我操 +我日 +小逼 +小洞 +小穴 +泄欲 +性爱 +性交 +性梦 +性奴 +性虐 +性事 +性欲 +艳情 +阳具 +阳痿 +阴部 +阴唇 +阴道 +阴蒂 +阴茎 +阴毛 +阴水 +淫手 +杂种 +招妓 +正法 +猪操 +猪毛 +自焚 +自虐 +自慰 +作爱 +做爱 +操你妈 +操你娘 +处女膜 +打飞机 +大参考 +大花逼 +大纪元 +你妈 +你娘 +动态网 +发抡功 +发伦功 +发论公 +发论功 +发正念 +反封锁 +干你妈 +干你娘 +干死你 +共产党 +狗卵子 +古怪歌 +回民暴 +简鸿章 +江八点 +江独裁 +江流氓 +江戏子 +江.?民 +江贼民 +江折民 +江猪媳 +僵贼民 +酱猪媳 +靠你妈 +李大师 +李登辉 +李红痔 +李宏志 +李洪志 +李总统 +毛厕洞 +毛贼东 +蒙古独 +你妈逼 +人民报 +日你妈 +日你娘 +日死你 +十八摸 +十六大 +四川独 +台湾独 +台湾狗 +我操你 +小鸡鸡 +性游戏 +一夜情 +一夜性 +真善忍 +中国猪 +朱容基 +猪聋畸 +操你老妈 +操你老母 +操你老娘 +操你奶奶 +大法弟子 +法轮大法 +干你老母 +干你奶奶 +共青团派 +回民暴动 +美国之音 +日你老妈 +日你老母 +日你老娘 +日你奶奶 +声色场所 +新华内情 +一边一国 +一党专制 +一中一台 +欲死欲仙 +处女终结者 +独立台湾会 +反封锁技术 +佛展千手法 +鸡毛信文汇 +神通加持法 +天安门事件 +天安门屠杀 +天安门一代 +联总之声传单 +人民内情真相 +人民真实报导 +人民真实报道 +氵去车仑工力 +天安门录影带 +中俄边界新约 +人民大众时事参考 +印尼伊斯兰祈祷团 +印尼依斯兰祈祷团 +东突厥斯坦伊斯兰运动 +乳交绳虐滴蜡 +操你妈逼 +日你妈逼 +奸夫淫妇 +人民报讯 +你妈的 +他妈的 +发轮功 +法抡功 +法轮功 +两性狂情 +操你吗逼 +乳房畜生 +骚逼 +淫逼 +淫荡 +淫妇 +骚货 +淫贱 +你老母 +你奶奶 +骚女 +淫片 +淫水 +骚穴 +淫穴 +东突厥斯坦依斯兰运动 +发骚 +风骚 +奸淫 +九评共产党 +卖骚 +卖淫 +去你妈的 +手淫 +维吾尔自由运动 +西藏独 +西藏独立 +新疆独 +成人 +淫片 +外阴 +小洞 +抽插 +泄欲 +激情 +性交 +性事 +性游戏 +色欲 +少女高潮 +X夜激情 +轮奸 +AV女优 +少儿勿入 +色诱 +声色场所 +骚逼 +卖骚 +两性狂情 +18岁以下勿入 +18岁以下勿看 +禁片 +AV女优 +处女终结者 +法轮 +法伦 +法抡 +大法 +共产 +艳情 +操逼 +日逼 +骚逼 +干逼 +操我 +操你 +操她 +性交 +性事 +淫贱 +淫荡 +淫逼 +淫水 +狂操 +操你妈 +操你奶奶 +淫穴 +蜜穴 +骚穴 +集会 +罢工 +游行 +狗日 +日她 +日你 +日我 +日批 +日逼 +日B +日比 +日死 +自焚 +主席 +大法 +真善忍 +愚民 +泽民 +台独 +锦涛 +温家宝 +李鹏 +瑞环 +邦国 +家宝 +云帆 +杨宁 +邓小平 +庆林 +庆红 +反共 +一塌糊涂 +共产党 +公安 +动态网 +六四 +fuck +六合彩 +法轮功 +李宏志 +真善忍 +宏志 +法伦 +法抡 +法囵 +法仑 +法纶 +发仑 +发囵 +自由运动 +法轮大法 +E周刊 +龙卷风 +大纪元 +大参考 +正法 +镇压 +达赖 +迫害 +嫖娼 +静坐 +政变 +造反 +分裂 +自焚 +弟子 +发轮功 +功友 +人民大众时事参考 +示威 +游行 +共匪 +明慧 +大法弟子 +疆独 +民运 +印尼伊斯兰祈祷团 +中俄边界新约 +大法 +政治运动 +压迫 +博讯 +反革命 +胡锦涛 +温家宝 +吴邦国 +江独裁 +江泽民 +杨宁 +云帆 +东突厥斯坦伊斯兰运动 +一边一国 +人民内情真相 +回民 +新闻封锁 +鸡毛信文汇 +新华举报 +新华内情 +政治风波 +江八点 +古怪歌 +民猪 +突厥斯坦 +广闻 +简鸿章 +人民报 +联总之声传单 +人民报讯 +东突 +美国之音 +人民真实报道 +教徒 +打倒 +推翻 +人权 +操你奶奶 +操你妈 +fa lun +falun +洪志 +f a lun +法轮 +大法 +李洪志 +自焚 +分裂 +西藏 +中华民国 +造反 +示威 +政变 +江泽民 +李鹏 +朱容基 +教徒 +人权 +迫害 +新华内情 +达赖 +镇压 +新闻封锁 +他妈的 +东突 +吕秀莲 +正法 +新华举报 +人民大众时事参考 +人民内情真相 +人民真实报导 +鸡毛信文汇 +联总之声传单 +美国之音 +民运 +人民报讯 +E周刊 +博讯 +人民报 +简鸿章 +迫害 +人权 +政变 +压迫 +反革命 +无能 +突厥斯坦 +印尼依斯兰祈祷团 +东突厥斯坦依斯兰运动 +疆独 +维吾尔自由运动 +自由运动 +1989天安门 +九评共产党 +一党专制 +太子党 +共青团派 +共党 +共匪 +共狗 +共军 +黄菊 +江流氓 +天安门事件 +共产党 +达赖 +藏独 +热比娅 +热比亚 +疆独 +台独 + +#测试的 +test \ No newline at end of file