diff --git a/README.md b/README.md
index 07b5575..cbbe5f5 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,30 @@
-#JavaTutorial(Java教程)
+# JavaTutorial(Java教程)
在成长的过程中,会学到许多新的知识;在项目实践过程中,经常需要学习新的技术。但经过一段时间后,这些知识点和技术又忘得差不多了,等到要用的时候又要搜索很多的资料从头到尾学习一次,浪费很多的时间,没有沉淀和积累。
-在这里,通过文章和代码,把这些知识点和技术的主要内容记录并汇总,供自己快速回顾,也分享给他人。涉及的内容有:
-* Java基础
-* Java中级
-* Java高级
-* Java企业开发
+在这里,通过文章和代码,把这些知识点和技术的主要内容记录并汇总,供自己快速回顾,也分享给他人。
-#Java基础
-##数据结构
+注:部分例子基于JDK8。
-##数据库
-###代码
-* [Apache DbUtils - JDBC Utility Component](src/cn/aofeng/demo/dbutils)
+## 关键字
+* [transient与序列化](src/cn/aofeng/demo/java/lang/serialization/TransientDemo.java)
-##Redis
-###代码
-* [Redis客户端Jedis使用示例](src/cn/aofeng/demo/redis/JedisDemo.java)
+## 数据结构
+* [在控制台输出类似目录树的结构](src/cn/aofeng/demo/tree)
+## 定时器
+* [Timer](src/cn/aofeng/demo/java/util/timer/TimerDemo.java)
+* [ScheduledExecutorService](src/cn/aofeng/demo/java/util/concurret/ScheduledExecutorServiceDemo.java)
-#Java中级
-##反射
-###代码
-* [获取构造方法、普通方法、字段和注解](src/cn/aofeng/demo/java/lang/reflect/ClassAnalyze.java)
-* [使用构造方法创建对象实例](src/cn/aofeng/demo/java/lang/reflect/CreateInstance.java)
-* [调用方法](src/cn/aofeng/demo/java/lang/reflect/InvokeMethod.java)
-* [获取/设置字段值](src/cn/aofeng/demo/java/lang/reflect/InvokeField.java)
+## 数据库
+* [Apache DbUtils - JDBC Utility Component](src/cn/aofeng/demo/dbutils)
+* [JDBC元数据使用示例](src/cn/aofeng/demo/jdbc/MetaDataExample.java)
+* [MyBatis3使用示例](src/cn/aofeng/demo/mybatis)
+
+## XML
+* [XPath](src/cn/aofeng/demo/xml/XPathDemo.java)
-##序列化与反序列化
-###Json
-####Gson
-#####代码
+## 序列化与反序列化
+### Gson
* [普通对象-序列化与反序列化](src/cn/aofeng/demo/json/gson/SimpleObjectSerialize.java)
* [数组-序列化](src/cn/aofeng/demo/json/gson/ArraySerialize.java)
* [数组-反序列化](src/cn/aofeng/demo/json/gson/ArrayDeserialize.java)
@@ -38,114 +32,118 @@
* [集合-反序列化](src/cn/aofeng/demo/json/gson/CollectionDeserialize.java)
* [自定义序列化](src/cn/aofeng/demo/json/gson/CustomSerialize.java)
* [自定义反序列化](src/cn/aofeng/demo/json/gson/CustomDeserialize.java)
+* [Gson教程-序列化与反序列化基础操作](http://aofengblog.com/2015/08/12/Gson-%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/)
+* [Gson教程-自定义序列化](http://aofengblog.com/2015/08/14/Gson-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BA%8F%E5%88%97%E5%8C%96/)
+* [Gson教程-自定义反序列化](http://aofengblog.com/2015/08/17/Gson-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/)
-#####文章
-* [Gson教程-序列化与反序列化基础操作](http://aofengblog.blog.163.com/blog/static/6317021201571275718825/)
-* [Gson教程-自定义序列化](http://aofengblog.blog.163.com/blog/static/631702120157148122062/)
-* [Gson教程-自定义反序列化](http://aofengblog.blog.163.com/blog/static/631702120157178202587/)
-
-##定时器
-###代码
-* [Timer](src/cn/aofeng/demo/java/util/timer/TimerDemo.java)
-* [ScheduledExecutorService](src/cn/aofeng/demo/java/util/concurret/ScheduledExecutorServiceDemo.java)
+## 单元测试
+* [单元测试 - JUnit+Ant自动化执行单元测试并生成报告 | How to Automated Unit Testing with JUnit and Ant](http://aofengblog.com/2013/02/27/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-JUnit-Ant%E8%87%AA%E5%8A%A8%E5%8C%96%E6%89%A7%E8%A1%8C%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E5%B9%B6%E7%94%9F%E6%88%90%E6%8A%A5%E5%91%8A/)
+* [单元测试 - JUnit+Ant+Cobertura生成单元测试代码覆盖率 | How to Generate Code Coverage Report With Junit And Cobertura](http://aofengblog.com/2013/02/27/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-JUnit-Ant-Cobertura%E7%94%9F%E6%88%90%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E4%BB%A3%E7%A0%81%E8%A6%86%E7%9B%96%E7%8E%87/)
+* [使用Jetty实现Http Server Mock作单元测试](src/cn/aofeng/demo/jetty)
-##设计模式
-###代码
-* [事件驱动](src/cn/aofeng/demo/eventdriver_normal)
-* [事件驱动改进版](src/cn/aofeng/demo/eventdriver_improve)
-* [Reactor](src/cn/aofeng/demo/reactor)
+### EasyMock
+* [使用EasyMock作单元测试](src/cn/aofeng/demo/easymock)
-##单元测试
-###文章
-* [单元测试 - JUnit+Ant自动化执行单元测试并生成报告 | How to Automated Unit Testing with JUnit and Ant](http://aofengblog.blog.163.com/blog/static/6317021201312734058296/)
-* [单元测试 - JUnit+Ant+Cobertura生成单元测试代码覆盖率 | How to Generate Code Coverage Report With Junit And Cobertura](http://aofengblog.blog.163.com/blog/static/6317021201312763630878/)
-* [使用Jetty实现Http Server Mock作单元测试](src/cn/aofeng/demo/jetty)
+### WireMock
* [使用WireMock实现Http Server Mock作单元测试](src/cn/aofeng/demo/wiremock)
-##其他
-###代码
-* [获取主机信息](src/cn/aofeng/demo/misc/GetHostInfo.java)
+### Mockito
+* [使用Mockito作单元测试](src/cn/aofeng/demo/mockito)
+
-#Java高级
-##网络与I/O
-###代码
+## 网络&I/O
* [多线程网络echo服务](src/cn/aofeng/demo/io/MultiThreadEchoServer.java)
* [线程池网络echo服务](src/cn/aofeng/demo/io/ThreadPoolEchoServer.java)
* [NIO 缓冲区](src/cn/aofeng/demo/nio/BufferIO.java)
* [NIO 内存映射](src/cn/aofeng/demo/nio/MemoryMapper.java)
* [NioEchoServer](src/cn/aofeng/demo/nio/NioEchoServer.java)
-
-####Netty 4.0.x
+* [NIO - 入门篇](http://aofengblog.com/2008/10/21/Java-NIO%E5%85%A5%E9%97%A8%E7%AF%87/)
+* [NIO - 文件锁](http://aofengblog.com/2008/10/27/Java-NIO%E6%96%87%E4%BB%B6%E9%94%81/)
+* [缓冲区操作和内存映射](http://aofengblog.com/2013/09/29/Java-%E7%BC%93%E5%86%B2%E5%8C%BA%E6%93%8D%E4%BD%9C%E5%92%8C%E5%86%85%E5%AD%98%E6%98%A0%E5%B0%84/)
+* [Java网络处理模型-阻塞I/O+多线程](http://aofengblog.com/2013/10/22/Java%E7%BD%91%E7%BB%9C%E5%A4%84%E7%90%86%E6%A8%A1%E5%9E%8B-%E9%98%BB%E5%A1%9EI-O-%E5%A4%9A%E7%BA%BF%E7%A8%8B/)
+* [Java网络处理模型-阻塞I/O+线程池](http://aofengblog.com/2013/10/24/Java%E7%BD%91%E7%BB%9C%E5%A4%84%E7%90%86%E6%A8%A1%E5%9E%8B-%E9%98%BB%E5%A1%9EI-O-%E7%BA%BF%E7%A8%8B%E6%B1%A0/)
+* [Reactor模式与非阻塞I/O | Reactor Pattern and NIO](http://aofengblog.com/2013/11/04/Reactor%E6%A8%A1%E5%BC%8F%E4%B8%8E%E9%9D%9E%E9%98%BB%E5%A1%9EI-O/)
+
+### RMI
+* [RMI服务端与客户端示例](src/cn/aofeng/demo/java/rmi)
+
+### HTTP
+* [使用Fluent API发起HTTP请求(Get和Post)](src/cn/aofeng/demo/httpclient/FluentApi.java)
+* [使用HttpClient发起HTTP请求(Get, Post和上传文件)](src/cn/aofeng/demo/httpclient/HttpClientBasic.java)
+* [使用JDK中的API建立简单的HTTP Server](src/cn/aofeng/demo/httpclient/server/SimpleHttpServer.java)
+
+### Netty 4.0.x
* [EchoServer](src/cn/aofeng/demo/netty40x/echo/EchoServer.java)
* [EchoClient](src/cn/aofeng/demo/netty40x/echo/EchoClient.java)
-###文章
-* [NIO - 入门篇(笔记)](http://aofengblog.blog.163.com/blog/static/6317021200892145321884/)
-* [NIO - 文件锁(笔记)](http://aofengblog.blog.163.com/blog/static/631702120089276182626/)
-* [缓冲区操作和内存映射](http://aofengblog.blog.163.com/blog/static/6317021201382925314705/)
-* [Java网络处理模型-阻塞I/O+多线程](http://aofengblog.blog.163.com/blog/static/6317021201392153148393/)
-* [Java网络处理模型-阻塞I/O+线程池](http://aofengblog.blog.163.com/blog/static/63170212013921758425/)
-* [Reactor模式与非阻塞I/O | Reactor Pattern and NIO](http://aofengblog.blog.163.com/blog/static/6317021201310293953286/)
-##线程与并发
-###代码
+## 线程&并发
+* [守护线程](src/cn/aofeng/demo/thread/DaemonThreadDemo.java)
* [fork/join](src/cn/aofeng/demo/java/util/forkjoin/HelloForkJoin.java)
* [Future](src/cn/aofeng/demo/java/util/future/HelloFuture.java)
* [Future接口关系图](src/cn/aofeng/demo/java/util/future/Future.ucls)
-
-###文章
-* [Servlet的线程安全问题](http://aofengblog.blog.163.com/blog/static/631702120081016102531434/)
-* [多线程池执行异步任务](http://aofengblog.blog.163.com/blog/static/6317021201423193434810/)
+* [Servlet的线程安全问题](http://aofengblog.com/2008/11/16/Servlet%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98/)
+* [多线程池执行异步任务](http://aofengblog.com/2014/03/31/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B1%A0%E6%89%A7%E8%A1%8C%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1/)
* [事件驱动的异步化框架:event4j](http://aofengblog.blog.163.com/blog/static/63170212014102463624267/)
-* [JDK HashMap源码分析 | HashMap Source Code Analysis](http://aofengblog.blog.163.com/blog/static/631702120147148919858/)
+* [JDK HashMap源码分析 | HashMap Source Code Analysis](http://aofengblog.com/2014/08/14/JDK-HashMap%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/)
+
+## 反射&代理
+* [获取构造方法、普通方法、字段和注解](src/cn/aofeng/demo/java/lang/reflect/ClassAnalyze.java)
+* [使用构造方法创建对象实例](src/cn/aofeng/demo/java/lang/reflect/CreateInstance.java)
+* [调用方法](src/cn/aofeng/demo/java/lang/reflect/InvokeMethod.java)
+* [获取/设置字段值](src/cn/aofeng/demo/java/lang/reflect/InvokeField.java)
+* [静态代理&动态代理](src/cn/aofeng/demo/proxy/AccountServiceClient.java)
+
+## AOP
+* [AspectJ-编译时织入和加载类时织入](src/cn/aofeng/demo/aspectj)
+* [Instrumentation入门](src/cn/aofeng/demo/java/lang/instrument)
## 脚本语言
-###代码
* [在Java中执行JavaScript脚本](src/cn/aofeng/demo/script/ScriptRunPerformence.java)
+* [多个脚本引擎执行JavaScript的性能比较](src/cn/aofeng/demo/script/MultiScriptEngineCompare.java)
+* [在JVM中编译脚本提升运行性能](http://aofengblog.com/2013/12/12/%E5%9C%A8JVM%E4%B8%AD%E7%BC%96%E8%AF%91%E8%84%9A%E6%9C%AC%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD/)
-###文章
-* [在JVM中编译脚本提升运行性能](http://aofengblog.blog.163.com/blog/static/6317021201311143045607/)
-
-##加密解密
-###代码
+## 加密解密
* [Blowfish加密与解密](src/cn/aofeng/demo/encrypt/Blowfish.java)
* [AES加密与解密](src/cn/aofeng/demo/encrypt/AES.java)
* [Blowfish, AES性能比较](src/cn/aofeng/demo/encrypt/PerformanceCompare.java)
+* [HMAC-SHA1签名算法](src/cn/aofeng/demo/encrypt/HmacSha1.java)
-#Java企业开发
-##开源框架
-###文章
-* [Struts2 - 支持JSON / Using json in struts2](http://aofengblog.blog.163.com/blog/static/6317021201072311443715/)
-* [Struts2 - Unable to load configuration. - bean 错误解决](http://aofengblog.blog.163.com/blog/static/631702120103184042768/)
-* [Spring IBatis Struts2 集成之一:Spring与IBatis集成 | Spring IBatis Struts2 Integration I: Spring Integrates with IBatis](http://aofengblog.blog.163.com/blog/static/6317021201121911105510/)
-* [Spring IBatis Struts2 集成之二:Spring与Struts2集成 | Spring IBatis Struts2 Integration II: Spring Integrates with Struts2](http://aofengblog.blog.163.com/blog/static/6317021201122172525296/)
+## 开源组件
-##XML
-###代码
-* [XPath](src/cn/aofeng/demo/xml/XPathDemo.java)
+### Slf4j
+* [slf4j使用示例](src/cn/aofeng/demo/slf4j/HelloSlf4j.java)
-## EJB
-###文章
-* [EJB3 - 使用Eclipse和JBoss开发和部署Session Bean | How to create Session Bean using JBoss and Eclipse](http://aofengblog.blog.163.com/blog/static/6317021201121310318552/)
-* [EJB3 - 使用Eclipse和JBoss开发和部署Entity Bean | How to create Entity Bean using JBoss and Eclipse](http://aofengblog.blog.163.com/blog/static/631702120112147279187/)
+### Redis
+* [Redis客户端Jedis使用示例](src/cn/aofeng/demo/redis/JedisDemo.java)
-##Tomcat
-###文章
-* [JMX管理Tomcat/Resin](http://aofengblog.blog.163.com/blog/static/6317021200871711013857/)
+### SSH
+* [Struts2 - 支持JSON / Using json in struts2](http://aofengblog.com/2010/08/23/Struts2-%E6%94%AF%E6%8C%81JSON/)
+* [Struts2 - Unable to load configuration. - bean 错误解决](http://aofengblog.blog.163.com/blog/static/631702120103184042768/)
+* [Spring IBatis Struts2 集成之一:Spring与IBatis集成 | Spring IBatis Struts2 Integration I: Spring Integrates with IBatis](http://aofengblog.com/2011/03/19/Spring-IBatis-Struts2-%E9%9B%86%E6%88%90%E4%B9%8B%E4%B8%80%EF%BC%9ASpring%E4%B8%8EIBatis%E9%9B%86%E6%88%90/)
+* [Spring IBatis Struts2 集成之二:Spring与Struts2集成 | Spring IBatis Struts2 Integration II: Spring Integrates with Struts2](http://aofengblog.com/2011/03/22/Spring-IBatis-Struts2-%E9%9B%86%E6%88%90%E4%B9%8B%E4%BA%8C%EF%BC%9ASpring%E4%B8%8EStruts2%E9%9B%86%E6%88%90/)
-##Jetty
-* []()
+### Tomcat
+* [JMX管理Tomcat/Resin](http://aofengblog.com/2008/08/17/Tomcat-JMX%E7%AE%A1%E7%90%86Tomcat-Resin/)
-##JBoss
-###文章
+### JBoss
* [JBoss5.1.0 GA - 部署项目类库兼容和类库冲突问题解决方法](http://aofengblog.blog.163.com/blog/static/631702120113674626533/)
+## EJB
+* [EJB3 - 使用Eclipse和JBoss开发和部署Session Bean | How to create Session Bean using JBoss and Eclipse](http://aofengblog.com/2011/03/14/EJB3-%E4%BD%BF%E7%94%A8Eclipse%E5%92%8CJBoss%E5%BC%80%E5%8F%91%E5%92%8C%E9%83%A8%E7%BD%B2Session-Bean/)
+* [EJB3 - 使用Eclipse和JBoss开发和部署Entity Bean | How to create Entity Bean using JBoss and Eclipse](http://aofengblog.com/2011/03/14/EJB3-%E4%BD%BF%E7%94%A8Eclipse%E5%92%8CJBoss%E5%BC%80%E5%8F%91%E5%92%8C%E9%83%A8%E7%BD%B2Entity-Bean/)
+
+## 设计模式
+* [事件驱动](src/cn/aofeng/demo/eventdriver_normal)
+* [事件驱动改进版](src/cn/aofeng/demo/eventdriver_improve)
+* [Reactor](src/cn/aofeng/demo/reactor)
-#Java工具
-##文章
-* [在Linux上安装JDK](http://aofengblog.blog.163.com/blog/static/6317021200810172817818/)
+## Java工具
+* [在Linux上安装JDK](http://aofengblog.com/2008/11/17/%E5%9C%A8Linux%E4%B8%8A%E5%AE%89%E8%A3%85JDK/)
* [在Linux上安装Ant](http://aofengblog.blog.163.com/blog/static/631702120081017113327582/)
* [Java程序以后台方式在Linux上运行](http://aofengblog.blog.163.com/blog/static/631702120081014104934762/)
* [Java使用SSH从远程服务器下载文件](http://aofengblog.blog.163.com/blog/static/6317021201362975846986/)
-* [Java实现在控制终端显示的字符进度条](http://aofengblog.blog.163.com/blog/static/6317021201362723025426/)
-* [VisualGC+Jstatd实时监控远程JVM](http://aofengblog.blog.163.com/blog/static/63170212013529818373/)
+* [Java实现在控制终端显示的字符进度条](http://aofengblog.com/2013/07/27/Java-%E5%AE%9E%E7%8E%B0%E5%9C%A8%E6%8E%A7%E5%88%B6%E7%BB%88%E7%AB%AF%E6%98%BE%E7%A4%BA%E7%9A%84%E5%AD%97%E7%AC%A6%E8%BF%9B%E5%BA%A6%E6%9D%A1/)
+* [VisualGC+Jstatd实时监控远程JVM](http://aofengblog.com/2013/06/29/Java-VisualGC-Jstatd%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7%E8%BF%9C%E7%A8%8BJVM/)
+* [获取主机信息](src/cn/aofeng/demo/misc/GetHostInfo.java)
+
diff --git a/conf/META-INF/aop.xml b/conf/META-INF/aop.xml
new file mode 100644
index 0000000..d6666a2
--- /dev/null
+++ b/conf/META-INF/aop.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/conf/gneratorConfig.xml b/conf/gneratorConfig.xml
new file mode 100644
index 0000000..51d6f85
--- /dev/null
+++ b/conf/gneratorConfig.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/log4j.properties b/conf/log4j.properties
index 70af5a7..075b90d 100644
--- a/conf/log4j.properties
+++ b/conf/log4j.properties
@@ -1,12 +1,18 @@
log4j.rootLogger=INFO, console
+# 应用本身
log4j.logger.cn.aofeng.demo=DEBUG, console
log4j.additivity.cn.aofeng.demo=false
-log4j.logger.org.jboss.netty.handler.logging=DEBUG, console
+
+# Netty
+log4j.logger.org.jboss.netty.handler.logging=INFO, console
log4j.additivity.org.jboss.netty.handler.logging=false
-# 控制台Console
-log4j.additivity.console=false
+# MyBatis
+log4j.logger.org.apache.ibatis=INFO, console
+log4j.additivity.org.apache.ibatis=false
+
+# 控制台输出
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] %p ~ %m%n
+log4j.appender.console.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] %p %m%n
diff --git a/conf/mybatis-config.xml b/conf/mybatis-config.xml
new file mode 100644
index 0000000..e3cab5d
--- /dev/null
+++ b/conf/mybatis-config.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/aspectjrt-1.8.10.jar b/lib/aspectjrt-1.8.10.jar
new file mode 100644
index 0000000..b5e8ce5
Binary files /dev/null and b/lib/aspectjrt-1.8.10.jar differ
diff --git a/lib/aspectjtools-1.8.10.jar b/lib/aspectjtools-1.8.10.jar
new file mode 100644
index 0000000..2e4a08d
Binary files /dev/null and b/lib/aspectjtools-1.8.10.jar differ
diff --git a/lib/aspectjweaver-1.8.10.jar b/lib/aspectjweaver-1.8.10.jar
new file mode 100755
index 0000000..4dbcaaf
Binary files /dev/null and b/lib/aspectjweaver-1.8.10.jar differ
diff --git a/lib/byte-buddy-1.2.1.jar b/lib/byte-buddy-1.2.1.jar
new file mode 100644
index 0000000..cd529cc
Binary files /dev/null and b/lib/byte-buddy-1.2.1.jar differ
diff --git a/lib/cglib-2.2.2.jar b/lib/cglib-2.2.2.jar
deleted file mode 100644
index 564b9f8..0000000
Binary files a/lib/cglib-2.2.2.jar and /dev/null differ
diff --git a/lib/cglib-nodep-2.2.2.jar b/lib/cglib-nodep-2.2.2.jar
new file mode 100644
index 0000000..02d81e8
Binary files /dev/null and b/lib/cglib-nodep-2.2.2.jar differ
diff --git a/lib/commons-jexl3-3.0.jar b/lib/commons-jexl3-3.0.jar
new file mode 100644
index 0000000..94566bf
Binary files /dev/null and b/lib/commons-jexl3-3.0.jar differ
diff --git a/lib/commons-logging-1.2.jar b/lib/commons-logging-1.2.jar
new file mode 100644
index 0000000..93a3b9f
Binary files /dev/null and b/lib/commons-logging-1.2.jar differ
diff --git a/lib/easymock-3.2.jar b/lib/easymock-3.2.jar
deleted file mode 100644
index 7c439f8..0000000
Binary files a/lib/easymock-3.2.jar and /dev/null differ
diff --git a/lib/easymock-3.4.jar b/lib/easymock-3.4.jar
new file mode 100644
index 0000000..e1118b2
Binary files /dev/null and b/lib/easymock-3.4.jar differ
diff --git a/lib/fluent-hc-4.5.2.jar b/lib/fluent-hc-4.5.2.jar
new file mode 100644
index 0000000..f44d04d
Binary files /dev/null and b/lib/fluent-hc-4.5.2.jar differ
diff --git a/lib/httpclient-4.5.2.jar b/lib/httpclient-4.5.2.jar
new file mode 100644
index 0000000..701609f
Binary files /dev/null and b/lib/httpclient-4.5.2.jar differ
diff --git a/lib/httpclient-cache-4.5.2.jar b/lib/httpclient-cache-4.5.2.jar
new file mode 100644
index 0000000..a6f5d31
Binary files /dev/null and b/lib/httpclient-cache-4.5.2.jar differ
diff --git a/lib/httpcore-4.4.4.jar b/lib/httpcore-4.4.4.jar
new file mode 100644
index 0000000..ac4a877
Binary files /dev/null and b/lib/httpcore-4.4.4.jar differ
diff --git a/lib/httpmime-4.5.2.jar b/lib/httpmime-4.5.2.jar
new file mode 100644
index 0000000..474670a
Binary files /dev/null and b/lib/httpmime-4.5.2.jar differ
diff --git a/lib/javassist-3.21.0-GA.jar b/lib/javassist-3.21.0-GA.jar
new file mode 100644
index 0000000..64549c4
Binary files /dev/null and b/lib/javassist-3.21.0-GA.jar differ
diff --git a/lib/junit-4.11.jar b/lib/junit-4.11.jar
deleted file mode 100644
index aaf7444..0000000
Binary files a/lib/junit-4.11.jar and /dev/null differ
diff --git a/lib/junit-4.12.jar b/lib/junit-4.12.jar
new file mode 100644
index 0000000..3a7fc26
Binary files /dev/null and b/lib/junit-4.12.jar differ
diff --git a/lib/mockito-core-2.0.42-beta.jar b/lib/mockito-core-2.0.42-beta.jar
new file mode 100644
index 0000000..a6de3d0
Binary files /dev/null and b/lib/mockito-core-2.0.42-beta.jar differ
diff --git a/lib/mybatis-3.4.4.jar b/lib/mybatis-3.4.4.jar
new file mode 100644
index 0000000..16c6d9e
Binary files /dev/null and b/lib/mybatis-3.4.4.jar differ
diff --git a/lib/mysql-connector-java-5.1.22-bin.jar b/lib/mysql-connector-java-5.1.22-bin.jar
deleted file mode 100644
index a50424d..0000000
Binary files a/lib/mysql-connector-java-5.1.22-bin.jar and /dev/null differ
diff --git a/lib/mysql-connector-java-5.1.40-bin.jar b/lib/mysql-connector-java-5.1.40-bin.jar
new file mode 100644
index 0000000..60bef5c
Binary files /dev/null and b/lib/mysql-connector-java-5.1.40-bin.jar differ
diff --git a/lib/objenesis-1.2.jar b/lib/objenesis-1.2.jar
deleted file mode 100644
index fb04d7f..0000000
Binary files a/lib/objenesis-1.2.jar and /dev/null differ
diff --git a/lib/objenesis-2.4.jar b/lib/objenesis-2.4.jar
new file mode 100644
index 0000000..f76ea51
Binary files /dev/null and b/lib/objenesis-2.4.jar differ
diff --git a/lib/powermock-mockito2-1.6.6-full.jar b/lib/powermock-mockito2-1.6.6-full.jar
new file mode 100644
index 0000000..ab68f68
Binary files /dev/null and b/lib/powermock-mockito2-1.6.6-full.jar differ
diff --git a/lib/slf4j-api-1.7.21.jar b/lib/slf4j-api-1.7.21.jar
new file mode 100644
index 0000000..7811fa0
Binary files /dev/null and b/lib/slf4j-api-1.7.21.jar differ
diff --git a/lib/slf4j-log4j12-1.7.21.jar b/lib/slf4j-log4j12-1.7.21.jar
new file mode 100644
index 0000000..d07a6dc
Binary files /dev/null and b/lib/slf4j-log4j12-1.7.21.jar differ
diff --git a/src/cn/aofeng/demo/aspectj/BusinessService.java b/src/cn/aofeng/demo/aspectj/BusinessService.java
new file mode 100644
index 0000000..36f465c
--- /dev/null
+++ b/src/cn/aofeng/demo/aspectj/BusinessService.java
@@ -0,0 +1,142 @@
+package cn.aofeng.demo.aspectj;
+
+/**
+ * 模拟业务方法,将被Aspectj织入代码,增加功能。
+ *
+ * @author 聂勇
+ */
+public class BusinessService {
+
+ public long add(int a, int b) {
+ return a+b;
+ }
+
+ public long add(int a, int b, int... other) {
+ long result = a + b;
+ for (int i : other) {
+ result += i;
+ }
+
+ return result;
+ }
+
+ public String join(String first, String... appends) {
+ if (null == first) {
+ throw new IllegalArgumentException("first is null");
+ }
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(first);
+ for (String str : appends) {
+ buffer.append(str);
+ }
+
+ return buffer.toString();
+ }
+
+ public String addPrefix(String src) {
+ if (null == src) {
+ throw new IllegalArgumentException("src is null");
+ }
+
+ return "-->"+src;
+ }
+
+ public static void printLine(char style) {
+ if ('=' == style) {
+ System.out.println("========================================================================================");
+ } else if ('-' == style) {
+ System.out.println("----------------------------------------------------------------------------------------");
+ } else {
+ System.out.println(" ");
+ }
+ }
+
+ public static void main(String[] args) {
+ final BusinessService bs = new BusinessService();
+
+ System.out.println("1、执行方法add(int, int)");
+ RunMethod rm = new RunMethod() {
+
+ @Override
+ public void run() {
+ long result = bs.add(1, 2);
+ System.out.println(">>> 结果:" + result);
+ }
+ };
+ rm.execute();
+
+ System.out.println("2、执行方法add(int, int, int...)");
+ rm = new RunMethod() {
+
+ @Override
+ public void run() {
+ long result = bs.add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ System.out.println(">>> 结果:" + result);
+ }
+ };
+ rm.execute();
+
+ System.out.println("3、执行方法join(String, String...)");
+ rm = new RunMethod() {
+
+ @Override
+ public void run() {
+ String str = bs.join("first", "-second", "-third");
+ System.out.println(">>> 结果:" + str);
+ }
+ };
+ rm.execute();
+
+ System.out.println("4、执行方法join(String, String...)");
+ rm = new RunMethod() {
+
+ @Override
+ public void run() {
+ String str = bs.join(null, "-second", "-third");
+ System.out.println(">>> 结果:" + str);
+ }
+ };
+ rm.execute();
+
+ System.out.println("5、执行方法addPrefix(String)");
+ rm = new RunMethod() {
+
+ @Override
+ public void run() {
+ String str = bs.addPrefix("原字符串");
+ System.out.println(">>> 结果:" + str);
+ }
+ };
+ rm.execute();
+
+ System.out.println("6、执行方法addPrefix(String)");
+ rm = new RunMethod() {
+
+ @Override
+ public void run() {
+ String str = bs.addPrefix(null);
+ System.out.println(">>> 结果:" + str);
+ }
+ };
+ rm.execute();
+ }
+
+ public static abstract class RunMethod {
+
+ private char _style = '=';
+
+ public void execute() {
+ printLine(_style);
+ try {
+ run();
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ }
+ printLine(_style);
+ printLine(' ');
+ }
+
+ public abstract void run();
+ }
+
+}
diff --git a/src/cn/aofeng/demo/aspectj/BusinessServiceInterceptor.java b/src/cn/aofeng/demo/aspectj/BusinessServiceInterceptor.java
new file mode 100644
index 0000000..847a2a4
--- /dev/null
+++ b/src/cn/aofeng/demo/aspectj/BusinessServiceInterceptor.java
@@ -0,0 +1,122 @@
+package cn.aofeng.demo.aspectj;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.After;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 拦截器。
+ *
+ * @author 聂勇
+ */
+@Aspect
+public class BusinessServiceInterceptor {
+
+ private static Logger _logger = LoggerFactory.getLogger(BusinessServiceInterceptor.class);
+
+ private char _style = '-';
+
+ /**
+ *
+ * Before通知不能修改方法传入的参数。
+ *
+ */
+ @Before("execution(public * cn.aofeng.demo.aspectj.BusinessService.add(..))")
+ public void beforeAdd(JoinPoint joinPoint) {
+ _logger.info( String.format("拦截到方法:%s, 传入参数:%s",
+ joinPoint.getSignature().getName(),
+ ArrayUtils.toString(joinPoint.getArgs()) ) );
+ BusinessService.printLine(_style);
+ }
+
+ /**
+ *
+ * After通知不能修改方法的返回值。
+ * 如果被拦截的方法抛出异常,拦截代码仍然正常执行。
+ *
+ */
+ @After("execution(public * cn.aofeng.demo.aspectj.BusinessService.join(..))")
+ public void beforeAddSupportMultiArgs(JoinPoint joinPoint) {
+ _logger.info("Signature.name:"+joinPoint.getSignature().getName());
+ _logger.info("Args:" + ArrayUtils.toString(joinPoint.getArgs()));
+ _logger.info("Target:" + ArrayUtils.toString(joinPoint.getTarget()));
+ _logger.info("This:" + ArrayUtils.toString(joinPoint.getThis()));
+ _logger.info("Kind:" + ArrayUtils.toString(joinPoint.getKind()));
+ _logger.info("SourceLocation:" + ArrayUtils.toString(joinPoint.getSourceLocation()));
+
+ // 试图修改传入的参数
+ joinPoint.getArgs()[0] = "100";
+
+ BusinessService.printLine(_style);
+ }
+
+ /**
+ *
+ * AfterReturning通知不能修改方法的返回值。
+ * 如果被拦截的方法抛出异常,拦截代码不再执行。
+ *
+ */
+ @AfterReturning(pointcut="execution(public * cn.aofeng.demo.aspectj.BusinessService.join(..))", returning="result")
+ public void afterReturnAdd(JoinPoint joinPoint, Object result) {
+ _logger.info( String.format("拦截到方法:%s, 传入参数:%s, 执行结果:%s",
+ joinPoint.getSignature().getName(),
+ ArrayUtils.toString(joinPoint.getArgs()),
+ result) );
+
+ // 试图修改返回值
+ result = "hello, changed";
+
+ BusinessService.printLine(_style);
+ }
+
+ /**
+ *
+ * 只在被拦截的方法抛出异常时才执行。
+ *
+ */
+ @AfterThrowing(pointcut="execution(public * cn.aofeng.demo.aspectj.BusinessService.join(..))", throwing="ex")
+ public void afterThrowingAdd(JoinPoint joinPoint, Exception ex) {
+ _logger.info( String.format("拦截到方法:%s, 传入参数:%s",
+ joinPoint.getSignature().getName(),
+ ArrayUtils.toString(joinPoint.getArgs()) ) );
+ if (null != ex) {
+ _logger.info("拦截到异常:", ex);
+ }
+
+ BusinessService.printLine(_style);
+ }
+
+ /**
+ *
+ * {@link ProceedingJoinPoint}只能在Around通知中使用。
+ * Around通知可以修改被拦截方法的传入参数和返回值。
+ *
+ */
+ @Around("execution(public * cn.aofeng.demo.aspectj.BusinessService.addPrefix(..))")
+ public Object afterAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ _logger.info( String.format("拦截到方法:%s, 传入参数:%s",
+ joinPoint.getSignature().getName(),
+ ArrayUtils.toString(joinPoint.getArgs()) ) );
+
+ Object result = null;
+ try {
+ result = joinPoint.proceed();
+ _logger.info("执行结果:" + result);
+ } catch (Throwable e) {
+ throw e;
+ } finally {
+ BusinessService.printLine(_style);
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/aspectj/build.xml b/src/cn/aofeng/demo/aspectj/build.xml
new file mode 100644
index 0000000..b5375f9
--- /dev/null
+++ b/src/cn/aofeng/demo/aspectj/build.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/cn/aofeng/demo/encrypt/EncryptAndDecrypt.java b/src/cn/aofeng/demo/encrypt/EncryptAndDecrypt.java
index da92cec..475523e 100644
--- a/src/cn/aofeng/demo/encrypt/EncryptAndDecrypt.java
+++ b/src/cn/aofeng/demo/encrypt/EncryptAndDecrypt.java
@@ -20,7 +20,7 @@
*/
public class EncryptAndDecrypt {
- private final static String CHARSET = "utf8";
+ public final static String CHARSET = "utf8";
/**
* 创建安全密钥。
@@ -46,8 +46,8 @@ public SecretKey createSecretKey(String encryptType, String keyStr)
*
* @param encryptType 加密方式,如:AES,Blowfish。详情查看Java Cryptography Architecture Standard Algorithm Name Documentation
* @param secretKey 密钥
- * @param srcData 待加密的数据
- * @return 数据加密后的字节数组
+ * @param srcData 待加密的源数据
+ * @return 加密后的二进制数据(字节数组)
* @see #encrypt(String, SecretKey, String, String)
*/
public byte[] encrypt(String encryptType, SecretKey secretKey,
@@ -58,6 +58,15 @@ public byte[] encrypt(String encryptType, SecretKey secretKey,
return encrypt(encryptType, secretKey, srcData, null);
}
+ /**
+ * 加密数据。
+ *
+ * @param encryptType 加密类型,如:AES/CBC/PKCS5Padding
+ * @param secretKey 密钥
+ * @param srcData 待加密的源数据
+ * @param algorithmParam 某些加密算法的附加参数
+ * @return 加密后的二进制数据(字节数组)
+ */
public byte[] encrypt(String encryptType, SecretKey secretKey,
String srcData, String algorithmParam) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
diff --git a/src/cn/aofeng/demo/encrypt/HmacSha1.java b/src/cn/aofeng/demo/encrypt/HmacSha1.java
new file mode 100644
index 0000000..4a6864e
--- /dev/null
+++ b/src/cn/aofeng/demo/encrypt/HmacSha1.java
@@ -0,0 +1,53 @@
+package cn.aofeng.demo.encrypt;
+
+import static cn.aofeng.demo.util.LogUtil.log;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * HMAC-SHA1签名算法。
+ *
+ * @author 聂勇
+ */
+public class HmacSha1 {
+
+ public final String encryptType = "HmacSHA1";
+ public final String key = "abcdefgh_1234567";
+
+ public void execute(String data) throws UnsupportedEncodingException,
+ NoSuchAlgorithmException, InvalidKeyException {
+ EncryptAndDecrypt ead = new EncryptAndDecrypt();
+
+ byte[] srcData = data.getBytes(EncryptAndDecrypt.CHARSET);
+ SecretKey secretKey = ead.createSecretKey(encryptType, key); // 生成密钥对象
+ Mac mac = Mac.getInstance(encryptType);
+ mac.init(secretKey);
+ byte[] result = mac.doFinal(srcData);
+
+ log("使用%s签名后的数据:", encryptType);
+ log(Base64.encodeBase64String(result));
+ }
+
+ public static void main(String[] args) throws InvalidKeyException,
+ UnsupportedEncodingException, IllegalBlockSizeException,
+ BadPaddingException, NoSuchAlgorithmException,
+ NoSuchPaddingException, InvalidAlgorithmParameterException {
+ String data = "炎黄,汉字,english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
+ log("待签名的数据:\n%s", data);
+
+ HmacSha1 hs = new HmacSha1();
+ hs.execute(data);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/httpclient/FluentApi.java b/src/cn/aofeng/demo/httpclient/FluentApi.java
new file mode 100644
index 0000000..89340d4
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/FluentApi.java
@@ -0,0 +1,85 @@
+/**
+ * 创建时间:2016-8-5
+ */
+package cn.aofeng.demo.httpclient;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.fluent.Response;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.log4j.Logger;
+
+import cn.aofeng.demo.httpclient.server.SimpleHttpServer;
+
+/**
+ * 使用Fluent API快速地进行简单的HTTP的请求和响应处理,启动{@link SimpleHttpServer}作为请求的服务端。
+ *
+ * @author 聂勇
+ */
+public class FluentApi {
+
+ private static Logger _logger = Logger.getLogger(FluentApi.class);
+
+ private static String _targetHost = "http://127.0.0.1:8888";
+
+ private static String _charset = "utf-8";
+
+ /**
+ * HTTP GET请求,关闭Keep-Alive。
+ *
+ * @param targetUrl 请求的地址
+ * @param charset 将响应流转换成字符串时使用的编码
+ */
+ public static void get(String targetUrl, String charset) {
+ Response response = null;
+ try {
+ response = Request.Get(targetUrl).setHeader("Connection", "close").execute();
+ String content = response.returnContent().asString(Charset.forName(charset));
+ _logger.info(content);
+ } catch (ClientProtocolException e) {
+ _logger.error("协议问题", e);
+ } catch (IOException e) {
+ _logger.error("连接服务器或读取响应出错", e);
+ }
+ }
+
+ /**
+ * HTTP POST请求,默认开启Keep-Alive,表单数据使用utf-8编码。
+ *
+ * @param targetUrl 请求的地址
+ * @param charset 请求表单内容处理和将响应流转换成字符串时使用的编码
+ */
+ public static void post(String targetUrl, String charset) {
+ List form = new ArrayList();
+ form.add(new BasicNameValuePair("hello", "喂"));
+ form.add(new BasicNameValuePair("gogogo", "走走走"));
+
+ Response response = null;
+ try {
+ response = Request.Post(targetUrl)
+ .bodyForm(form, Charset.forName(charset))
+ .execute();
+ String content = response.returnContent().asString(Charset.forName(charset));
+ _logger.info(content);
+ } catch (ClientProtocolException e) {
+ _logger.error("协议问题", e);
+ } catch (IOException e) {
+ _logger.error("连接服务器或读取响应出错", e);
+ }
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ get(_targetHost+"/get", _charset);
+ post(_targetHost+"/post", _charset);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/httpclient/HttpClientBasic.java b/src/cn/aofeng/demo/httpclient/HttpClientBasic.java
new file mode 100644
index 0000000..3be7281
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/HttpClientBasic.java
@@ -0,0 +1,110 @@
+/**
+ * 创建时间:2016-2-23
+ */
+package cn.aofeng.demo.httpclient;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.FileEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * HttpClient的基本操作。
+ *
+ * @author 聂勇
+ */
+public class HttpClientBasic {
+
+ private static Logger _logger = Logger.getLogger(HttpClientBasic.class);
+
+ private static String _targetHost = "http://127.0.0.1:8888";
+
+ private static String _charset = "utf-8";
+
+ public void get() throws URISyntaxException, ClientProtocolException, IOException {
+ CloseableHttpClient client = HttpClients.createDefault();
+ HttpGet get = new HttpGet(_targetHost+"/get");
+ CloseableHttpResponse response = client.execute(get);
+ processResponse(response);
+ }
+
+ public void post() throws ClientProtocolException, IOException {
+ List params = new ArrayList();
+ params.add(new BasicNameValuePair("chinese", "中文"));
+ params.add(new BasicNameValuePair("english", "英文"));
+ UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, _charset);
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ HttpPost post = new HttpPost(_targetHost+"/post");
+ post.addHeader("Cookie", "character=abcdefghijklmnopqrstuvwxyz; sign=abc-123-jkl-098");
+ post.setEntity(entity);
+ CloseableHttpResponse response = client.execute(post);
+ processResponse(response);
+ }
+
+ public void sendFile(String filePath) throws UnsupportedOperationException, IOException {
+ CloseableHttpClient client = HttpClients.createDefault();
+ HttpPost post = new HttpPost(_targetHost+"/file");
+ File file = new File(filePath);
+ FileEntity entity = new FileEntity(file, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), _charset));
+ post.setEntity(entity);
+ CloseableHttpResponse response = client.execute(post);
+ processResponse(response);
+ }
+
+ private void processResponse(CloseableHttpResponse response)
+ throws UnsupportedOperationException, IOException {
+ try {
+ // 获取响应头
+ Header[] headers = response.getAllHeaders();
+ for (Header header : headers) {
+ _logger.info(header.getName() + ":" + header.getValue());
+ }
+
+ // 获取状态信息
+ StatusLine sl =response.getStatusLine();
+ _logger.info( String.format("ProtocolVersion:%s, StatusCode:%d, Desc:%s",
+ sl.getProtocolVersion().toString(), sl.getStatusCode(), sl.getReasonPhrase()) );
+
+ // 获取响应内容
+ HttpEntity entity = response.getEntity();
+ _logger.info( String.format("ContentType:%s, Length:%d, Encoding:%s",
+ null == entity.getContentType() ? "" : entity.getContentType().getValue(),
+ entity.getContentLength(),
+ null == entity.getContentEncoding() ? "" : entity.getContentEncoding().getValue()) );
+ _logger.info(EntityUtils.toString(entity, _charset));
+// _logger.info( IOUtils.toString(entity.getContent(), _charset) ); // 大部分情况下效果与上行语句等同,但实现上的编码处理不同
+ } finally {
+ response.close();
+ }
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) throws Exception {
+ HttpClientBasic basic = new HttpClientBasic();
+// basic.get();
+// basic.post();
+ basic.sendFile("/devdata/projects/open_source/mine/JavaTutorial/LICENSE");
+ }
+
+}
diff --git a/src/cn/aofeng/demo/httpclient/README.md b/src/cn/aofeng/demo/httpclient/README.md
new file mode 100644
index 0000000..33c669b
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/README.md
@@ -0,0 +1,9 @@
+#HTTP
+##HTTP客户端
+###代码
+* [使用Fluent API发起HTTP请求(Get和Post)](cn/aofeng/demo/httpclient/FluentApi.java)
+* [使用HttpClient发起HTTP请求(Get, Post和上传文件)](cn/aofeng/demo/httpclient/FluentApi.java)
+
+##HTTP服务端
+###代码
+* [使用JDK中的API建立简单的HTTP Server](src/cn/aofeng/demo/httpclient/SimpleHttpServer.java)
diff --git a/src/cn/aofeng/demo/httpclient/server/AbstractHandler.java b/src/cn/aofeng/demo/httpclient/server/AbstractHandler.java
new file mode 100644
index 0000000..ce439a4
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/server/AbstractHandler.java
@@ -0,0 +1,71 @@
+/**
+ * 创建时间:2016-8-18
+ */
+package cn.aofeng.demo.httpclient.server;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/**
+ * @author 聂勇
+ */
+public abstract class AbstractHandler implements HttpHandler {
+
+ private static Logger _logger = Logger.getLogger(AbstractHandler.class);
+
+ protected String _charset = "utf-8";
+
+ public AbstractHandler(String charset) {
+ this._charset = charset;
+ }
+
+ /**
+ * 处理请求头。
+ */
+ public void handleHeader(HttpExchange httpEx) {
+ InetSocketAddress remoteAddress = httpEx.getRemoteAddress();
+ _logger.info("收到来自"+remoteAddress.getAddress().getHostAddress()+":"+remoteAddress.getPort()+"的请求");
+
+ URI rUri = httpEx.getRequestURI();
+ _logger.info("请求地址:"+rUri.toString());
+
+ String method = httpEx.getRequestMethod();
+ _logger.info("请求方法:"+method);
+
+ Headers headers = httpEx.getRequestHeaders();
+ Set>> headerSet = headers.entrySet();
+ _logger.info("请求头:");
+ for (Entry> header : headerSet) {
+ _logger.info(header.getKey()+":"+header.getValue());
+ }
+ }
+
+ /**
+ * 处理响应。
+ */
+ public void handleResponse(HttpExchange httpEx, String content)
+ throws UnsupportedEncodingException, IOException {
+ String rc = "冒号后面是收到的请求,原样返回:"+content;
+ byte[] temp = rc.getBytes(_charset);
+ Headers outHeaders = httpEx.getResponseHeaders();
+ outHeaders.set("ABC", "123");
+ httpEx.sendResponseHeaders(200, temp.length);
+ OutputStream outs = httpEx.getResponseBody();
+ outs.write(temp);
+ IOUtils.closeQuietly(outs);
+ }
+
+}
\ No newline at end of file
diff --git a/src/cn/aofeng/demo/httpclient/server/BinaryHandler.java b/src/cn/aofeng/demo/httpclient/server/BinaryHandler.java
new file mode 100644
index 0000000..d9336ed
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/server/BinaryHandler.java
@@ -0,0 +1,66 @@
+/**
+ * 创建时间:2016-8-18
+ */
+package cn.aofeng.demo.httpclient.server;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/**
+ * 二进制处理器。
+ *
+ * @author 聂勇
+ */
+public class BinaryHandler extends AbstractHandler implements HttpHandler {
+
+ private static Logger _logger = Logger.getLogger(BinaryHandler.class);
+
+ private String _dir = "/home/nieyong/temp/JavaTutorial";
+
+ public BinaryHandler(String charset) {
+ super(charset);
+ }
+
+ @Override
+ public void handle(HttpExchange httpEx) throws IOException {
+ super.handleHeader(httpEx);
+ handleRequest(httpEx);
+ String content = "收到一个二进制的请求";
+ super.handleResponse(httpEx, content);
+ }
+
+ /**
+ * 处理请求。
+ * @throws IOException
+ */
+ public String handleRequest(HttpExchange httpEx) throws IOException {
+ OutputStream outs = null;
+ InputStream ins = null;
+ try {
+ File file = new File(_dir, ""+System.currentTimeMillis());
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ outs = new FileOutputStream(file);
+ ins = httpEx.getRequestBody();
+ IOUtils.copyLarge(ins, outs);
+ } catch (Exception e) {
+ _logger.error("read request or write file occurs error", e);
+ } finally {
+ IOUtils.closeQuietly(ins);
+ IOUtils.closeQuietly(outs);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/httpclient/server/CharacterHandler.java b/src/cn/aofeng/demo/httpclient/server/CharacterHandler.java
new file mode 100644
index 0000000..ac5e444
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/server/CharacterHandler.java
@@ -0,0 +1,50 @@
+/**
+ * 创建时间:2016-8-18
+ */
+package cn.aofeng.demo.httpclient.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/**
+ * 字符串处理器。
+ *
+ * @author 聂勇
+ */
+public class CharacterHandler extends AbstractHandler implements HttpHandler {
+
+ static Logger _logger = Logger .getLogger(CharacterHandler.class);
+
+ public CharacterHandler(String charset) {
+ super(charset);
+ }
+
+ @Override
+ public void handle(HttpExchange httpEx) throws IOException {
+ super.handleHeader(httpEx);
+ String content = handleRequest(httpEx);
+ super.handleResponse(httpEx, content);
+ }
+
+ /**
+ * 处理请求。
+ */
+ public String handleRequest(HttpExchange httpEx)
+ throws UnsupportedEncodingException, IOException {
+ InputStream ins = httpEx.getRequestBody();
+ String content = URLDecoder.decode(
+ IOUtils.toString(ins, _charset), _charset);
+ _logger.info("请求内容:"+content);
+ IOUtils.closeQuietly(ins);
+ return content;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/httpclient/server/SimpleHttpServer.java b/src/cn/aofeng/demo/httpclient/server/SimpleHttpServer.java
new file mode 100644
index 0000000..d82088e
--- /dev/null
+++ b/src/cn/aofeng/demo/httpclient/server/SimpleHttpServer.java
@@ -0,0 +1,41 @@
+/**
+ * 创建时间:2016-8-5
+ */
+package cn.aofeng.demo.httpclient.server;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.apache.log4j.Logger;
+
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * 简单的HTTP Server。
+ *
+ * @author 聂勇
+ */
+public class SimpleHttpServer {
+
+ private static Logger _logger = Logger.getLogger(SimpleHttpServer.class);
+
+ private static String _charset = "utf-8";
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ int port = 8888;
+ try {
+ HttpServer server = HttpServer.create(new InetSocketAddress(port), 128);
+ server.createContext("/get", new CharacterHandler(_charset));
+ server.createContext("/post", new CharacterHandler(_charset));
+ server.createContext("/file", new BinaryHandler(_charset));
+ server.start();
+ _logger.info("http server already started, listen port:"+port);
+ } catch (IOException e) {
+ _logger.error("", e);
+ }
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/lang/instrument/FirstInstrumentation.java b/src/cn/aofeng/demo/java/lang/instrument/FirstInstrumentation.java
new file mode 100644
index 0000000..d4319ca
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/instrument/FirstInstrumentation.java
@@ -0,0 +1,25 @@
+package cn.aofeng.demo.java.lang.instrument;
+
+import java.lang.instrument.Instrumentation;
+
+import org.apache.commons.lang.StringUtils;
+
+import cn.aofeng.demo.util.LogUtil;
+
+/**
+ * Instrument入口类。
+ *
+ * @author 聂勇
+ */
+public class FirstInstrumentation {
+
+ public static void premain(String options, Instrumentation ins) {
+ if (StringUtils.isBlank(options)) {
+ LogUtil.log("instrument without options");
+ } else {
+ LogUtil.log("instrument with options:%s", options);
+ }
+
+ ins.addTransformer(new FirstTransformer());
+ }
+}
diff --git a/src/cn/aofeng/demo/java/lang/instrument/FirstTransformer.java b/src/cn/aofeng/demo/java/lang/instrument/FirstTransformer.java
new file mode 100644
index 0000000..48ce691
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/instrument/FirstTransformer.java
@@ -0,0 +1,26 @@
+/**
+ *
+ */
+package cn.aofeng.demo.java.lang.instrument;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+import cn.aofeng.demo.util.LogUtil;
+
+/**
+ * 只输出问候语,不进行字节码修改的Class转换器。
+ *
+ * @author 聂勇
+ */
+public class FirstTransformer implements ClassFileTransformer {
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ LogUtil.log(">>> %s", className);
+ return null;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/lang/instrument/Hello.java b/src/cn/aofeng/demo/java/lang/instrument/Hello.java
new file mode 100644
index 0000000..01fd76f
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/instrument/Hello.java
@@ -0,0 +1,14 @@
+package cn.aofeng.demo.java.lang.instrument;
+
+/**
+ * Instrumentation启动类。 *
+ *
+ * @author 聂勇
+ */
+public class Hello {
+
+ public static void main(String[] args) {
+ // nothing
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/lang/instrument/README.md b/src/cn/aofeng/demo/java/lang/instrument/README.md
new file mode 100644
index 0000000..53fe4e5
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/instrument/README.md
@@ -0,0 +1,40 @@
+# 一、Instrumentation入门
+
+* [FirstTransformer.java](FirstTransformer.java) 处理字节码,由类FirstInstrumentation执行
+* [FirstInstrumentation.java](FirstInstrumentation.java) instrumentation入口类,由javaagent载入执行
+* [build.xml](build.xml) Ant脚本,负责编译、打包和运行
+
+在当前目录下执行命令:
+```bash
+ant
+```
+
+输出信息如下:
+> [java] instrument with options:"Hello, Instrumentation"
+> [java] >>> java/lang/invoke/MethodHandleImpl
+> [java] >>> java/lang/invoke/MethodHandleImpl$1
+> [java] >>> java/lang/invoke/MethodHandleImpl$2
+> [java] >>> java/util/function/Function
+> [java] >>> java/lang/invoke/MethodHandleImpl$3
+> [java] >>> java/lang/invoke/MethodHandleImpl$4
+> [java] >>> java/lang/ClassValue
+> [java] >>> java/lang/ClassValue$Entry
+> [java] >>> java/lang/ClassValue$Identity
+> [java] >>> java/lang/ClassValue$Version
+> [java] >>> java/lang/invoke/MemberName$Factory
+> [java] >>> java/lang/invoke/MethodHandleStatics
+> [java] >>> java/lang/invoke/MethodHandleStatics$1
+> [java] >>> sun/misc/PostVMInitHook
+> [java] >>> sun/usagetracker/UsageTrackerClient
+> [java] >>> java/util/concurrent/atomic/AtomicBoolean
+> [java] >>> sun/usagetracker/UsageTrackerClient$1
+> [java] >>> sun/usagetracker/UsageTrackerClient$4
+> [java] >>> sun/usagetracker/UsageTrackerClient$3
+> [java] >>> java/io/FileOutputStream$1
+> [java] >>> sun/launcher/LauncherHelper
+> [java] >>> cn/aofeng/demo/java/lang/instrument/Hello
+> [java] >>> sun/launcher/LauncherHelper$FXHelper
+> [java] >>> java/lang/Class$MethodArray
+> [java] >>> java/lang/Void
+> [java] >>> java/lang/Shutdown
+> [java] >>> java/lang/Shutdown$Lock
diff --git a/src/cn/aofeng/demo/java/lang/instrument/build.xml b/src/cn/aofeng/demo/java/lang/instrument/build.xml
new file mode 100644
index 0000000..2abfbb1
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/instrument/build.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/cn/aofeng/demo/java/lang/serialization/Man.java b/src/cn/aofeng/demo/java/lang/serialization/Man.java
new file mode 100644
index 0000000..840867b
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/serialization/Man.java
@@ -0,0 +1,74 @@
+package cn.aofeng.demo.java.lang.serialization;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * 自定义序列化和反序列化。
+ *
+ * @author 聂勇
+ */
+public class Man implements Externalizable {
+
+ private String manName;
+
+ private int manAge;
+
+ private transient String password;
+
+ public Man() {
+ // nothing
+ }
+
+ public Man(String name, int age) {
+ this.manName = name;
+ this.manAge = age;
+ }
+
+ public Man(String name, int age, String password) {
+ this.manName = name;
+ this.manAge = age;
+ this.password = password;
+ }
+
+ public String getManName() {
+ return manName;
+ }
+
+ public void setManName(String manName) {
+ this.manName = manName;
+ }
+
+ public int getManAge() {
+ return manAge;
+ }
+
+ public void setManAge(int manAge) {
+ this.manAge = manAge;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeObject(manName);
+ out.writeInt(manAge);
+ out.writeObject(password);
+ }
+
+ @Override
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ manName = (String) in.readObject();
+ manAge = in.readInt();
+ password = (String) in.readObject();
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/lang/serialization/People.java b/src/cn/aofeng/demo/java/lang/serialization/People.java
new file mode 100644
index 0000000..70fbb13
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/serialization/People.java
@@ -0,0 +1,79 @@
+package cn.aofeng.demo.java.lang.serialization;
+
+import java.io.Serializable;
+
+/**
+ * 默认序列化和
+ *
+ * @author 聂勇
+ */
+public class People implements Serializable {
+
+ private static final long serialVersionUID = 6235620243018494633L;
+
+ private String name;
+
+ private int age;
+
+ private transient String address;
+
+ private static String sTestNormal;
+
+ private static transient String sTestTransient;
+
+ public People(String name) {
+ this.name = name;
+ }
+
+ public People(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public People(String name, int age, String address) {
+ this.name = name;
+ this.age = age;
+ this.address = address;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getsTestNormal() {
+ return sTestNormal;
+ }
+
+ public void setsTestNormal(String sTestNormal) {
+ People.sTestNormal = sTestNormal;
+ }
+
+ public String getsTestTransient() {
+ return sTestTransient;
+ }
+
+ public void setsTestTransient(String sTestTransient) {
+ People.sTestTransient = sTestTransient;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/lang/serialization/TransientDemo.java b/src/cn/aofeng/demo/java/lang/serialization/TransientDemo.java
new file mode 100644
index 0000000..c6f82f5
--- /dev/null
+++ b/src/cn/aofeng/demo/java/lang/serialization/TransientDemo.java
@@ -0,0 +1,126 @@
+package cn.aofeng.demo.java.lang.serialization;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 关键字 transient 测试。
+ *
+ * @author 聂勇
+ */
+public class TransientDemo {
+
+ private static Logger _logger = LoggerFactory.getLogger(TransientDemo.class);
+
+ private String _tempFileName = "TransientDemo";
+
+ /**
+ * 将对象序列化并保存到文件。
+ *
+ * @param obj 待序列化的对象
+ */
+ public void save(Object obj) {
+ ObjectOutputStream outs = null;
+ try {
+ outs = new ObjectOutputStream(
+ new FileOutputStream( getTempFile(_tempFileName) ));
+ outs.writeObject(obj);
+ } catch (IOException e) {
+ _logger.error("save object to file occurs error", e);
+ } finally {
+ IOUtils.closeQuietly(outs);
+ }
+ }
+
+ /**
+ * 从文件读取内容并反序列化成对象。
+ *
+ * @return {@link People}对象。如果读取文件出错 或 对象类型转换失败,返回null。
+ */
+ public T load() {
+ ObjectInputStream ins = null;
+ try {
+ ins = new ObjectInputStream(
+ new FileInputStream( getTempFile(_tempFileName)) );
+ return ((T) ins.readObject());
+ } catch (IOException e) {
+ _logger.error("load object from file occurs error", e);
+ } catch (ClassNotFoundException e) {
+ _logger.error("load object from file occurs error", e);
+ } finally {
+ IOUtils.closeQuietly(ins);
+ }
+
+ return null;
+ }
+
+ private File getTempFile(String filename) {
+ return new File(getTempDir(), filename);
+ }
+
+ private String getTempDir() {
+ return System.getProperty("java.io.tmpdir");
+ }
+
+ private void displayPeople(People people) {
+ if (null == people) {
+ return;
+ }
+ String template = "People[name:%s, age:%d, address:%s, sTestNormal:%s, sTestTransient:%s]";
+ System.out.println( String.format(template, people.getName(), people.getAge(),
+ people.getAddress(), people.getsTestNormal(), people.getsTestTransient()));
+ }
+
+ private void displayMan(Man man) {
+ if (null == man) {
+ return;
+ }
+ String template = "Man[manName:%s, manAge:%d, password:%s]";
+ System.out.println( String.format(template, man.getManName(), man.getManAge(), man.getPassword()) );
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ System.out.println(">>> Serializable测试");
+ TransientDemo demo = new TransientDemo();
+
+ People people = new People("张三", 30, "中国广州");
+ people.setsTestNormal("normal-first");
+ people.setsTestTransient("transient-first");
+ System.out.println("序列化之前的对象信息:");
+ demo.displayPeople(people);
+ demo.save(people);
+
+ // 修改静态变量的值
+ people.setsTestNormal("normal-second");
+ people.setsTestTransient("transient-second");
+
+ People fromLoad = demo.load();
+ System.out.println("反序列化之后的对象信息:");
+ demo.displayPeople(fromLoad);
+
+
+
+ System.out.println("");
+ System.out.println(">>> Externalizable测试");
+ Man man = new Man("李四", 10, "假密码");
+ System.out.println("序列化之前的对象信息:");
+ demo.displayMan(man);
+ demo.save(man);
+
+ Man manFromLoadMan = demo.load();
+ System.out.println("反序列化之后的对象信息:");
+ demo.displayMan(manFromLoadMan);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/rmi/Gender.java b/src/cn/aofeng/demo/java/rmi/Gender.java
new file mode 100644
index 0000000..2ba3e7b
--- /dev/null
+++ b/src/cn/aofeng/demo/java/rmi/Gender.java
@@ -0,0 +1,20 @@
+package cn.aofeng.demo.java.rmi;
+
+/**
+ * 性别。
+ *
+ * @author 聂勇
+ */
+public class Gender {
+
+ /**
+ * 男。
+ */
+ public final static char MALE = 'M';
+
+ /**
+ * 女。
+ */
+ public final static char FEMALE = 'F';
+
+}
diff --git a/src/cn/aofeng/demo/java/rmi/RmiClient.java b/src/cn/aofeng/demo/java/rmi/RmiClient.java
new file mode 100644
index 0000000..96f3c75
--- /dev/null
+++ b/src/cn/aofeng/demo/java/rmi/RmiClient.java
@@ -0,0 +1,60 @@
+package cn.aofeng.demo.java.rmi;
+
+import java.net.MalformedURLException;
+import java.rmi.Naming;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ *
+ * @author 聂勇
+ */
+public class RmiClient {
+
+ private static Logger _logger = LoggerFactory.getLogger(RmiClient.class);
+
+ private static String assembleUrl(String hostUrl, String bindName) {
+ if (StringUtils.isBlank(hostUrl) || StringUtils.isBlank(bindName)) {
+ return null;
+ }
+
+ String host = hostUrl.endsWith("/") ? hostUrl.substring(0, hostUrl.length()-1) : hostUrl;
+ String name = bindName.startsWith("/") ? bindName.substring(1) : bindName;
+
+ return host + "/" + name;
+ }
+
+ /**
+ * @param args
+ *
+ * - [0]:待连接的RMI主机。如:rmi://192.168.56.102:9999
+ * - [1]:服务名称。如:UserService
+ *
+ */
+ public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
+ String host = args[0];
+ String serviceName = args[1];
+ String url = assembleUrl(host, serviceName);
+ UserService userService = (UserService) Naming.lookup(url);
+
+ String userId = "10000";
+ User user = userService.findById(userId);
+ _logger.info("身份证号为{}的用户信息{}", userId, user);
+ userId = "10001";
+ user = userService.findById(userId);
+ _logger.info("身份证号为{}的用户信息{}", userId, user);
+
+ String userName = "小明";
+ user = userService.findByName(userName);
+ _logger.info("姓名为{}的用户信息{}", userName, user);
+ userName = "张三";
+ user = userService.findByName(userName);
+ _logger.info("姓名为{}的用户信息{}", userName, user);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/rmi/RmiServer.java b/src/cn/aofeng/demo/java/rmi/RmiServer.java
new file mode 100644
index 0000000..24f9e9f
--- /dev/null
+++ b/src/cn/aofeng/demo/java/rmi/RmiServer.java
@@ -0,0 +1,53 @@
+package cn.aofeng.demo.java.rmi;
+
+import java.rmi.AlreadyBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * RMI服务端。
+ *
+ * @author 聂勇
+ */
+public class RmiServer {
+
+ private static Logger _logger = LoggerFactory.getLogger(RmiServer.class);
+
+ public static Registry createRegistry(int port) {
+ Registry registry = null;
+ try {
+ registry = LocateRegistry.createRegistry(port);
+ } catch (RemoteException e) {
+ _logger.info( String.format("注册端口%s失败", port), e);
+ }
+
+ return registry;
+ }
+
+ /**
+ * @param args [0]:绑定端口
+ * @throws RemoteException
+ */
+ public static void main(String[] args) throws RemoteException {
+ int port = Integer.parseInt(args[0]);
+ UserService userService = new UserServiceImpl();
+ Registry registry = createRegistry(port);
+ if (null == registry) {
+ System.exit(0);
+ }
+
+ String bindName = "UserService";
+ try {
+ registry.bind(bindName, userService);
+ } catch (AlreadyBoundException e) {
+ _logger.info("服务{}已经绑定过", bindName);
+ }
+
+ _logger.info("RMI Server started, listen port:{}", port);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/rmi/User.java b/src/cn/aofeng/demo/java/rmi/User.java
new file mode 100644
index 0000000..3f54cfa
--- /dev/null
+++ b/src/cn/aofeng/demo/java/rmi/User.java
@@ -0,0 +1,170 @@
+package cn.aofeng.demo.java.rmi;
+
+import java.io.Serializable;
+
+/**
+ * 用户信息。
+ *
+ * @author 聂勇
+ */
+public class User implements Serializable {
+
+ private static final long serialVersionUID = 7616705579045104892L;
+
+ /**
+ * 身份证号。
+ */
+ private String id;
+
+ /**
+ * 姓名。
+ */
+ private String name;
+
+ /**
+ * 性别:M-男;F-女。
+ */
+ private char gender;
+
+ /**
+ * 出生日期。以毫秒存储。
+ */
+ private long birthday;
+
+ /**
+ * 国家。
+ */
+ private String country;
+
+ /**
+ * 省/州。
+ */
+ private String province;
+
+ /**
+ * 市/区。
+ */
+ private String city;
+
+ /**
+ * 街道详细地址。
+ */
+ private String address;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public char getGender() {
+ return gender;
+ }
+
+ public void setGender(char gender) {
+ this.gender = gender;
+ }
+
+ public long getBirthday() {
+ return birthday;
+ }
+
+ public void setBirthday(long birthday) {
+ this.birthday = birthday;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public String getProvince() {
+ return province;
+ }
+
+ public void setProvince(String province) {
+ this.province = province;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((country == null) ? 0 : country.hashCode());
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ User other = (User) obj;
+ if (country == null) {
+ if (other.country != null)
+ return false;
+ } else if (!country.equals(other.country))
+ return false;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder(256)
+ .append("User [id=").append(id)
+ .append(", name=").append(name)
+ .append(", gender=").append(gender)
+ .append(", birthday=").append(birthday)
+ .append(", country=").append(country)
+ .append(", province=").append(province)
+ .append(", city=").append(city)
+ .append(", address=").append(address)
+ .append("]");
+ return buffer.toString();
+ }
+
+}
diff --git a/src/cn/aofeng/demo/java/rmi/UserService.java b/src/cn/aofeng/demo/java/rmi/UserService.java
new file mode 100644
index 0000000..2469743
--- /dev/null
+++ b/src/cn/aofeng/demo/java/rmi/UserService.java
@@ -0,0 +1,19 @@
+package cn.aofeng.demo.java.rmi;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * 用户信息服务。
+ *
+ * @author 聂勇
+ */
+public interface UserService extends Remote {
+
+ public User findByName(String name) throws RemoteException;
+
+ public User findById(String id) throws RemoteException;
+
+ public boolean add(User user) throws RemoteException;
+
+}
diff --git a/src/cn/aofeng/demo/java/rmi/UserServiceImpl.java b/src/cn/aofeng/demo/java/rmi/UserServiceImpl.java
new file mode 100644
index 0000000..60e44b8
--- /dev/null
+++ b/src/cn/aofeng/demo/java/rmi/UserServiceImpl.java
@@ -0,0 +1,70 @@
+package cn.aofeng.demo.java.rmi;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * 用户信息服务。
+ *
+ * @author 聂勇
+ */
+public class UserServiceImpl extends UnicastRemoteObject implements UserService {
+
+ public UserServiceImpl() throws RemoteException {
+ super();
+ }
+
+ private static final long serialVersionUID = -9134952963637302483L;
+
+ @Override
+ public User findByName(String name) throws RemoteException {
+ if (StringUtils.isBlank(name)) {
+ return null;
+ }
+
+ if ("小明".equals(name)) {
+ return createUser("10000", "小明", Gender.MALE);
+ }
+
+ return null;
+ }
+
+ @Override
+ public User findById(String id) throws RemoteException {
+ if (StringUtils.isBlank(id)) {
+ return null;
+ }
+
+ if ("10000".equals(id)) {
+ return createUser("10000", "小丽", Gender.FEMALE);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean add(User user) throws RemoteException {
+ if (null == user || StringUtils.isBlank(user.getId()) || StringUtils.isBlank(user.getName())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private User createUser(String id, String name, char gender) {
+ User user = new User();
+ user.setId(id);
+ user.setName(name);
+ user.setGender(gender);
+ user.setBirthday(System.currentTimeMillis());
+ user.setCountry("中国");
+ user.setProvince("广东");
+ user.setCity("广州");
+ user.setAddress("xxx区xxx街道xxx号");
+
+ return user;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/jdbc/JDBCUtils.java b/src/cn/aofeng/demo/jdbc/JDBCUtils.java
new file mode 100644
index 0000000..57741f5
--- /dev/null
+++ b/src/cn/aofeng/demo/jdbc/JDBCUtils.java
@@ -0,0 +1,64 @@
+package cn.aofeng.demo.jdbc;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * JDBC常用方法集。
+ *
+ * @author 聂勇
+ */
+public class JDBCUtils {
+
+ private static Logger _logger = LoggerFactory.getLogger(JDBCUtils.class);
+
+ public static void close(ResultSet rs) {
+ if (null != rs) {
+ try {
+ rs.close();
+ } catch (SQLException e) {
+ _logger.error("close resultset occurs error", e);
+ }
+ }
+ }
+
+ public static void close(Statement stmt) {
+ if (null != stmt) {
+ try {
+ stmt.close();
+ } catch (SQLException e) {
+ _logger.error("close statement occurs error", e);
+ }
+ }
+ }
+
+ public static void close(Connection conn) {
+ if (null != conn) {
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ _logger.error("close connection occurs error", e);
+ }
+ }
+ }
+
+ public static void showResultSetInfo(ResultSet rs) throws SQLException {
+ ResultSetMetaData rsMeta = rs.getMetaData();
+ int colCount = rsMeta.getColumnCount();
+ _logger.info("total columns:{}", colCount);
+ for (int i = 1; i <= colCount; i++) {
+ _logger.info("column name:{}, label:{}, type:{}, typeName:{}",
+ rsMeta.getColumnName(i),
+ rsMeta.getColumnLabel(i),
+ rsMeta.getColumnType(i),
+ rsMeta.getColumnTypeName(i));
+ }
+ }
+
+}
diff --git a/src/cn/aofeng/demo/jdbc/MetaDataExample.java b/src/cn/aofeng/demo/jdbc/MetaDataExample.java
new file mode 100644
index 0000000..f631135
--- /dev/null
+++ b/src/cn/aofeng/demo/jdbc/MetaDataExample.java
@@ -0,0 +1,82 @@
+package cn.aofeng.demo.jdbc;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.aofeng.demo.tree.PrettyTree;
+import cn.aofeng.demo.tree.PrettyTree.Node;
+
+/**
+ * JDBC元数据使用示例。
+ *
+ * @author 聂勇
+ */
+public class MetaDataExample {
+
+ private static Logger _logger = LoggerFactory.getLogger(MetaDataExample.class);
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ String url = "jdbc:mysql://192.168.56.102:19816/ucgc_sdk?useUnicode=true&characterEncoding=UTF8";
+ Properties info = new Properties();
+ info.put("user", "uzone");
+ info.put("password", "uzone");
+
+ Connection conn = null;
+ try {
+ conn = DriverManager.getConnection(url, info);
+ DatabaseMetaData dbMeta = conn.getMetaData();
+ showCatalogs(dbMeta);
+ } catch (SQLException e) {
+ _logger.error("get connection occurs error", e);
+ } finally {
+ JDBCUtils.close(conn);
+ }
+ }
+
+ private static void showCatalogs(DatabaseMetaData dbMeta) throws SQLException {
+ ResultSet catalogsRs = null;
+ try {
+ catalogsRs = dbMeta.getCatalogs();
+// JDBCUtils.showResultSetInfo(catalogsRs);
+ while (catalogsRs.next()) {
+ String catalog = catalogsRs.getString("TABLE_CAT");
+ showTables(dbMeta, catalog);
+ }
+ } finally {
+ JDBCUtils.close(catalogsRs);
+ }
+ }
+
+ /**
+ * 采用树状结构输出Catalog和所有归属于它的表的名称。
+ */
+ private static void showTables(DatabaseMetaData dbMeta, String catalog) throws SQLException {
+ ResultSet tablesRs = null;
+ Node root = new Node(catalog);
+ try {
+ tablesRs = dbMeta.getTables(catalog, null, null, null);
+// JDBCUtils.showResultSetInfo(tablesRs);
+ while (tablesRs.next()) {
+ root.add(new Node(tablesRs.getString("TABLE_NAME")));
+ }
+ } finally {
+ JDBCUtils.close(tablesRs);
+ }
+
+ StringBuilder buffer = new StringBuilder(256);
+ PrettyTree pt = new PrettyTree();
+ pt.renderRoot(root, buffer);
+ System.out.print(buffer);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mockito/Commodity.java b/src/cn/aofeng/demo/mockito/Commodity.java
new file mode 100644
index 0000000..32d52a9
--- /dev/null
+++ b/src/cn/aofeng/demo/mockito/Commodity.java
@@ -0,0 +1,50 @@
+package cn.aofeng.demo.mockito;
+
+/**
+ * 商品信息。
+ *
+ * @author 聂勇
+ */
+public class Commodity {
+
+ private String id;
+
+ private String name;
+
+ private int type;
+
+ public Commodity() {
+ // nothing
+ }
+
+ public Commodity(String id, String name, int type) {
+ this.id = id;
+ this.name = name;
+ this.type = type;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mockito/CommodityDao.java b/src/cn/aofeng/demo/mockito/CommodityDao.java
new file mode 100644
index 0000000..b6ad73d
--- /dev/null
+++ b/src/cn/aofeng/demo/mockito/CommodityDao.java
@@ -0,0 +1,20 @@
+package cn.aofeng.demo.mockito;
+
+import java.util.List;
+
+/**
+ * 商品存储层。
+ *
+ * @author 聂勇
+ */
+public class CommodityDao {
+
+ public Commodity queryById(String id) {
+ return null;
+ }
+
+ public List queryByType(int type) {
+ return null;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mockito/User.java b/src/cn/aofeng/demo/mockito/User.java
new file mode 100644
index 0000000..baade9f
--- /dev/null
+++ b/src/cn/aofeng/demo/mockito/User.java
@@ -0,0 +1,70 @@
+package cn.aofeng.demo.mockito;
+
+/**
+ * 用户信息。
+ *
+ * @author 聂勇
+ */
+public class User {
+
+ private long id;
+
+ private String name;
+
+ private int sex;
+
+ private int age;
+
+ private String address;
+
+ public User() {
+ // nothing
+ }
+
+ public User(long userId, String userName, int age) {
+ this.id = userId;
+ this.name = userName;
+ this.age = age;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getSex() {
+ return sex;
+ }
+
+ public void setSex(int sex) {
+ this.sex = sex;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mockito/UserDao.java b/src/cn/aofeng/demo/mockito/UserDao.java
new file mode 100644
index 0000000..a786167
--- /dev/null
+++ b/src/cn/aofeng/demo/mockito/UserDao.java
@@ -0,0 +1,19 @@
+package cn.aofeng.demo.mockito;
+
+import java.util.List;
+
+/**
+ * 用户信息存储层。
+ *
+ * @author 聂勇
+ */
+public class UserDao {
+
+ public User queryById(long userId) {
+ return null;
+ }
+
+ public List queryByName(String userName) {
+ return null;
+ }
+}
diff --git a/src/cn/aofeng/demo/mockito/UserService.java b/src/cn/aofeng/demo/mockito/UserService.java
new file mode 100644
index 0000000..c6ee66b
--- /dev/null
+++ b/src/cn/aofeng/demo/mockito/UserService.java
@@ -0,0 +1,54 @@
+package cn.aofeng.demo.mockito;
+
+/**
+ * 用户服务。
+ *
+ * @author 聂勇
+ */
+public class UserService {
+
+ /** 成人年龄分界线 */
+ private final static int ADULT_AGE = 18;
+
+ private UserDao _userDao;
+ private CommodityDao _commodityDao;
+
+ public boolean isAdult(long userId) {
+ User user = _userDao.queryById(userId);
+ if (null == user || user.getAge() < ADULT_AGE) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean buy(long userId, String commodityId) {
+ if (! isAdult(userId)) {
+ return false;
+ }
+
+ Commodity commodity = _commodityDao.queryById(commodityId);
+ if (null == commodity) {
+ return false;
+ }
+ // 省略余下的处理逻辑
+ return true;
+ }
+
+ public UserDao getUserDao() {
+ return _userDao;
+ }
+
+ public void setUserDao(UserDao userDao) {
+ this._userDao = userDao;
+ }
+
+ public CommodityDao getCommodityDao() {
+ return _commodityDao;
+ }
+
+ public void setCommodityDao(CommodityDao commodityDao) {
+ this._commodityDao = commodityDao;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mockito/UserServiceTest.java b/src/cn/aofeng/demo/mockito/UserServiceTest.java
new file mode 100644
index 0000000..fd7009e
--- /dev/null
+++ b/src/cn/aofeng/demo/mockito/UserServiceTest.java
@@ -0,0 +1,66 @@
+package cn.aofeng.demo.mockito;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link UserService}的单元测试用例。
+ *
+ * @author 聂勇
+ */
+public class UserServiceTest {
+
+ private UserService _userService = new UserService();
+
+ @Test
+ public void testIsAdult4UserExist() {
+ long userId = 123;
+ User user = new User(userId, "张三", 19);
+
+ // 大于18岁的测试用例
+ UserDao daoMock = mock(UserDao.class);
+ when(daoMock.queryById(userId)).thenReturn(user); // 设置行为和对应的返回值
+ _userService.setUserDao(daoMock); // 设置mock
+ assertTrue(_userService.isAdult(userId)); // 校验结果
+
+ // 等于18岁的测试用例
+ User user2 = new User(userId, "李四", 18);
+ when(daoMock.queryById(userId)).thenReturn(user2);
+ _userService.setUserDao(daoMock);
+ assertTrue(_userService.isAdult(userId));
+
+ // 小于18岁的测试用例
+ User user3 = new User(userId, "王五", 17);
+ when(daoMock.queryById(userId)).thenReturn(user3);
+ _userService.setUserDao(daoMock);
+ assertFalse(_userService.isAdult(userId));
+ }
+
+ @Test
+ public void testIsAdult4UserNotExist() {
+ // 用户不存在的测试用例
+ long userId = 123;
+ UserDao daoMock = mock(UserDao.class);
+ when(daoMock.queryById(userId)).thenReturn(null); // 设置行为和对应的返回值
+ _userService.setUserDao(daoMock); // 设置mock
+ assertFalse(_userService.isAdult(userId)); // 校验结果
+ }
+
+ @Test
+ public void testBuy() {
+ long userId = 12345;
+ UserDao daoMock = mock(UserDao.class);
+ when(daoMock.queryById(anyLong())).thenReturn(new User(userId, "张三", 19));
+
+ String commodityId = "S01A10009823";
+ CommodityDao commodityDao = mock(CommodityDao.class);
+ when(commodityDao.queryById(anyString())).thenReturn(new Commodity(commodityId, "xxx手机", 1));
+
+ _userService.setUserDao(daoMock);
+ _userService.setCommodityDao(commodityDao);
+ assertTrue(_userService.buy(userId, commodityId));
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mybatis/MyBatisClient.java b/src/cn/aofeng/demo/mybatis/MyBatisClient.java
new file mode 100644
index 0000000..6b6f3f1
--- /dev/null
+++ b/src/cn/aofeng/demo/mybatis/MyBatisClient.java
@@ -0,0 +1,62 @@
+package cn.aofeng.demo.mybatis;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.aofeng.demo.mybatis.dao.MonitNotifyHistoryDao;
+import cn.aofeng.demo.mybatis.entity.MonitNotifyHistory;
+
+/**
+ *
+ *
+ * @author 聂勇
+ */
+public class MyBatisClient {
+
+ private static Logger _logger = LoggerFactory.getLogger(MyBatisClient.class);
+
+ public static void main(String[] args) throws IOException {
+ String resource = "mybatis-config.xml";
+ InputStream ins = Resources.getResourceAsStream(resource);
+ SqlSessionFactory ssFactory = new SqlSessionFactoryBuilder().build(ins);
+ MonitNotifyHistoryDao dao = new MonitNotifyHistoryDao(ssFactory);
+
+ // 插入一条记录
+ long recordId = 999;
+ MonitNotifyHistory record = new MonitNotifyHistory();
+ record.setRecordId(recordId);
+ record.setMonitId(9999);
+ record.setAppId(99);
+ record.setNotifyTarget(1);
+ record.setNotifyType(1);
+ record.setNotifyContent("通知内容测试");
+ record.setStatus(0);
+ record.setCreateTime(1492061400000L);
+ int count = dao.insert(record);
+ _logger.info("insert complete, effects {} rows", count);
+
+ // 查询记录
+ MonitNotifyHistory entity = dao.selectByPrimaryKey(recordId);
+ _logger.info("query record id {}, result: {}", recordId, entity);
+
+ // 更新一个字段再执行查询
+ entity.setRetryTimes(99);
+ count = dao.updateByPrimaryKeySelective(entity);
+ _logger.info("update complete, record id:{}, effects {} rows", recordId, count);
+ entity = dao.selectByPrimaryKey(recordId);
+ _logger.info("query record id {}, result: {}", recordId, entity);
+
+ // 删除记录后再执行查询
+ count = dao.deleteByPrimaryKey(recordId);
+ _logger.info("delete complete, record id {}, effects {} rows", recordId, count);
+ entity = dao.selectByPrimaryKey(recordId);
+ _logger.info("query record id {}, result: {}", recordId, entity);
+ }
+
+}
diff --git a/src/cn/aofeng/demo/mybatis/README.md b/src/cn/aofeng/demo/mybatis/README.md
new file mode 100644
index 0000000..e214deb
--- /dev/null
+++ b/src/cn/aofeng/demo/mybatis/README.md
@@ -0,0 +1,5 @@
+* [MyBatisClien.java](MyBatisClient.java) 入口类。读取配置文件,生成SqlSessionFactory,使用DAO操作表。
+* [mybatis-config.xml](../../../../../conf/mybatis-config.xml) mybatis配置文件
+* [MonitNotifyHistoryDao](dao/MonitNotifyHistoryDao.java) DAO类
+* [MonitNotifyHisto.java](entity/MonitNotifyHistory.java) 实体类
+* [MonitNotifyHistoryMapper.xml](mapper/MonitNotifyHistoryMapper.xml) SQL模板映射配置文件
diff --git a/src/cn/aofeng/demo/mybatis/dao/MonitNotifyHistoryDao.java b/src/cn/aofeng/demo/mybatis/dao/MonitNotifyHistoryDao.java
new file mode 100644
index 0000000..d549e87
--- /dev/null
+++ b/src/cn/aofeng/demo/mybatis/dao/MonitNotifyHistoryDao.java
@@ -0,0 +1,79 @@
+package cn.aofeng.demo.mybatis.dao;
+
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+
+import cn.aofeng.demo.mybatis.entity.MonitNotifyHistory;
+
+/**
+ * 监控通知历史CURD。
+ *
+ * @author 聂勇
+ */
+public class MonitNotifyHistoryDao {
+
+ private SqlSessionFactory _ssFactory = null;
+
+ public MonitNotifyHistoryDao(SqlSessionFactory factory) {
+ this._ssFactory = factory;
+ }
+
+ private String assembleStatement(String id) {
+ return "cn.aofeng.demo.mybatis.dao.MonitNotifyHistoryMapper."+id;
+ }
+
+ private void close(SqlSession session) {
+ if (null != session) {
+ session.close();
+ }
+ }
+
+ public int deleteByPrimaryKey(Long recordId) {
+ SqlSession session = _ssFactory.openSession(true);
+ int result = 0;
+ try {
+ session.delete(assembleStatement("deleteByPrimaryKey"), recordId);
+ } finally {
+ close(session);
+ }
+
+ return result;
+ }
+
+ public int insert(MonitNotifyHistory record) {
+ SqlSession session = _ssFactory.openSession(true);
+ int result = 0;
+ try {
+ result = session.insert(assembleStatement("insertSelective"), record);
+ } finally {
+ close(session);
+ }
+
+ return result;
+ }
+
+ public MonitNotifyHistory selectByPrimaryKey(Long recordId) {
+ SqlSession session = _ssFactory.openSession();
+ MonitNotifyHistory result = null;
+ try {
+ result = session.selectOne(assembleStatement("selectByPrimaryKey"), recordId);
+ } finally {
+ close(session);
+ }
+
+ return result;
+ }
+
+ public int updateByPrimaryKeySelective(MonitNotifyHistory record) {
+ SqlSession session = _ssFactory.openSession(true);
+ int result = 0;
+ try {
+ session.update(assembleStatement("updateByPrimaryKeySelective"), record);
+ } finally {
+ close(session);
+ }
+
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/src/cn/aofeng/demo/mybatis/entity/MonitNotifyHistory.java b/src/cn/aofeng/demo/mybatis/entity/MonitNotifyHistory.java
new file mode 100644
index 0000000..2bae4f5
--- /dev/null
+++ b/src/cn/aofeng/demo/mybatis/entity/MonitNotifyHistory.java
@@ -0,0 +1,105 @@
+package cn.aofeng.demo.mybatis.entity;
+
+/**
+ * 监控通知历史实体类。
+ *
+ * @author 聂勇
+ */
+public class MonitNotifyHistory {
+ private Long recordId;
+
+ private Integer monitId;
+
+ private Integer appId;
+
+ private Integer notifyType;
+
+ private Integer notifyTarget;
+
+ private String notifyContent;
+
+ private Integer status;
+
+ private Integer retryTimes;
+
+ private Long createTime;
+
+ public Long getRecordId() {
+ return recordId;
+ }
+
+ public void setRecordId(Long recordId) {
+ this.recordId = recordId;
+ }
+
+ public Integer getMonitId() {
+ return monitId;
+ }
+
+ public void setMonitId(Integer monitId) {
+ this.monitId = monitId;
+ }
+
+ public Integer getAppId() {
+ return appId;
+ }
+
+ public void setAppId(Integer appId) {
+ this.appId = appId;
+ }
+
+ public Integer getNotifyType() {
+ return notifyType;
+ }
+
+ public void setNotifyType(Integer notifyType) {
+ this.notifyType = notifyType;
+ }
+
+ public Integer getNotifyTarget() {
+ return notifyTarget;
+ }
+
+ public void setNotifyTarget(Integer notifyTarget) {
+ this.notifyTarget = notifyTarget;
+ }
+
+ public String getNotifyContent() {
+ return notifyContent;
+ }
+
+ public void setNotifyContent(String notifyContent) {
+ this.notifyContent = notifyContent;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Integer getRetryTimes() {
+ return retryTimes;
+ }
+
+ public void setRetryTimes(Integer retryTimes) {
+ this.retryTimes = retryTimes;
+ }
+
+ public Long getCreateTime() {
+ return createTime;
+ }
+
+ public void setCreateTime(Long createTime) {
+ this.createTime = createTime;
+ }
+
+ @Override
+ public String toString() {
+ return "MonitNotifyHistory [recordId=" + recordId + ", monitId=" + monitId + ", appId=" + appId + ", notifyType=" + notifyType + ", notifyTarget="
+ + notifyTarget + ", notifyContent=" + notifyContent + ", status=" + status + ", retryTimes=" + retryTimes + ", createTime=" + createTime + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/src/cn/aofeng/demo/mybatis/mapper/MonitNotifyHistoryMapper.xml b/src/cn/aofeng/demo/mybatis/mapper/MonitNotifyHistoryMapper.xml
new file mode 100644
index 0000000..e6ac688
--- /dev/null
+++ b/src/cn/aofeng/demo/mybatis/mapper/MonitNotifyHistoryMapper.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ delete from monit_notify_history
+ where record_id = #{recordId,jdbcType=BIGINT}
+
+
+ insert into monit_notify_history (record_id, monit_id, app_id,
+ notify_type, notify_target, notify_content,
+ status, retry_times, create_time
+ )
+ values (#{recordId,jdbcType=BIGINT}, #{monitId,jdbcType=INTEGER}, #{appId,jdbcType=TINYINT},
+ #{notifyType,jdbcType=TINYINT}, #{notifyTarget,jdbcType=INTEGER}, #{notifyContent,jdbcType=VARCHAR},
+ #{status,jdbcType=TINYINT}, #{retryTimes,jdbcType=TINYINT}, #{createTime,jdbcType=BIGINT}
+ )
+
+
+ insert into monit_notify_history
+
+
+ record_id,
+
+
+ monit_id,
+
+
+ app_id,
+
+
+ notify_type,
+
+
+ notify_target,
+
+
+ notify_content,
+
+
+ status,
+
+
+ retry_times,
+
+
+ create_time,
+
+
+
+
+ #{recordId,jdbcType=BIGINT},
+
+
+ #{monitId,jdbcType=INTEGER},
+
+
+ #{appId,jdbcType=TINYINT},
+
+
+ #{notifyType,jdbcType=TINYINT},
+
+
+ #{notifyTarget,jdbcType=INTEGER},
+
+
+ #{notifyContent,jdbcType=VARCHAR},
+
+
+ #{status,jdbcType=TINYINT},
+
+
+ #{retryTimes,jdbcType=TINYINT},
+
+
+ #{createTime,jdbcType=BIGINT},
+
+
+
+
+ update monit_notify_history
+
+
+ monit_id = #{monitId,jdbcType=INTEGER},
+
+
+ app_id = #{appId,jdbcType=TINYINT},
+
+
+ notify_type = #{notifyType,jdbcType=TINYINT},
+
+
+ notify_target = #{notifyTarget,jdbcType=INTEGER},
+
+
+ notify_content = #{notifyContent,jdbcType=VARCHAR},
+
+
+ status = #{status,jdbcType=TINYINT},
+
+
+ retry_times = #{retryTimes,jdbcType=TINYINT},
+
+
+ create_time = #{createTime,jdbcType=BIGINT},
+
+
+ where record_id = #{recordId,jdbcType=BIGINT}
+
+
+ update monit_notify_history
+ set monit_id = #{monitId,jdbcType=INTEGER},
+ app_id = #{appId,jdbcType=TINYINT},
+ notify_type = #{notifyType,jdbcType=TINYINT},
+ notify_target = #{notifyTarget,jdbcType=INTEGER},
+ notify_content = #{notifyContent,jdbcType=VARCHAR},
+ status = #{status,jdbcType=TINYINT},
+ retry_times = #{retryTimes,jdbcType=TINYINT},
+ create_time = #{createTime,jdbcType=BIGINT}
+ where record_id = #{recordId,jdbcType=BIGINT}
+
+
\ No newline at end of file
diff --git a/src/cn/aofeng/demo/netty40x/echo/EchoClient.java b/src/cn/aofeng/demo/netty40x/echo/EchoClient.java
index c5b28f4..734887e 100644
--- a/src/cn/aofeng/demo/netty40x/echo/EchoClient.java
+++ b/src/cn/aofeng/demo/netty40x/echo/EchoClient.java
@@ -42,7 +42,7 @@ public void start(String host, int port) {
*/
public static void main(String[] args) {
if (args.length != 2) {
- _logger.error("Invalid arguments。Usage:/njava cn.aofeng.demo.netty40x.echo.EchoClient 192.168.56.102 8080");
+ _logger.error("Invalid arguments。Usage:java cn.aofeng.demo.netty40x.echo.EchoClient 192.168.56.102 8080");
System.exit(-1);
}
diff --git a/src/cn/aofeng/demo/netty40x/echo/EchoServer.java b/src/cn/aofeng/demo/netty40x/echo/EchoServer.java
index 34addc5..dec241a 100644
--- a/src/cn/aofeng/demo/netty40x/echo/EchoServer.java
+++ b/src/cn/aofeng/demo/netty40x/echo/EchoServer.java
@@ -46,7 +46,7 @@ public void start(int port) {
*/
public static void main(String[] args) {
if (args.length != 1) {
- _logger.error("Invalid arguments。Usage:/njava cn.aofeng.demo.netty40x.echo.EchoServer 8080");
+ _logger.error("Invalid arguments。Usage:java cn.aofeng.demo.netty40x.echo.EchoServer 8080");
System.exit(-1);
}
diff --git a/src/cn/aofeng/demo/proxy/AccountService.java b/src/cn/aofeng/demo/proxy/AccountService.java
new file mode 100644
index 0000000..83c8f21
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/AccountService.java
@@ -0,0 +1,27 @@
+package cn.aofeng.demo.proxy;
+
+/**
+ * 账号服务接口定义。
+ *
+ * @author 聂勇
+ */
+public interface AccountService {
+
+ /**
+ * 注册。
+ *
+ * @param username 账号名
+ * @return 具体含义查看{@link Result}的说明。
+ */
+ Result register(String username);
+
+ /**
+ * 登录。
+ *
+ * @param username 账号名
+ * @param password 密码
+ * @return 具体含义查看{@link Result}的说明。
+ */
+ Result login(String username, String password);
+
+}
diff --git a/src/cn/aofeng/demo/proxy/AccountServiceClient.java b/src/cn/aofeng/demo/proxy/AccountServiceClient.java
new file mode 100644
index 0000000..946409a
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/AccountServiceClient.java
@@ -0,0 +1,28 @@
+package cn.aofeng.demo.proxy;
+
+/**
+ * 代理调用示例。
+ *
+ * @author 聂勇
+ */
+public class AccountServiceClient {
+
+ public static void main(String[] args) {
+ AccountService as = new AccountServiceImpl();
+
+ // 静态代理
+ AccountService staticProxy = new AccountServiceStaticProxy(as);
+ staticProxy.register(null);
+ staticProxy.register("A0001");
+ staticProxy.login(null, null);
+ staticProxy.login("A0001", "PWD0001");
+
+ // 动态代理
+ AccountService dynamicProxy = AccountServiceDynamicProxy.newInstance(as);
+ dynamicProxy.register(null);
+ dynamicProxy.register("A0001");
+ dynamicProxy.login(null, null);
+ dynamicProxy.login("A0001", "PWD0001");
+ }
+
+}
diff --git a/src/cn/aofeng/demo/proxy/AccountServiceDynamicProxy.java b/src/cn/aofeng/demo/proxy/AccountServiceDynamicProxy.java
new file mode 100644
index 0000000..562543c
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/AccountServiceDynamicProxy.java
@@ -0,0 +1,42 @@
+package cn.aofeng.demo.proxy;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 账号服务动态代理。
+ *
+ * @author 聂勇
+ */
+public class AccountServiceDynamicProxy implements InvocationHandler {
+
+ private final static Logger _LOGGER = LoggerFactory.getLogger(AccountServiceDynamicProxy.class);
+
+ private AccountService _accountService;
+
+ public AccountServiceDynamicProxy(AccountService accountService) {
+ this._accountService = accountService;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ _LOGGER.debug("execute method {}, arguments: {}", method.getName(), args);
+ Object result = method.invoke(_accountService, args);
+ _LOGGER.debug("execute method {}, result:{}", method.getName(), result);
+
+ return result;
+ }
+
+ public static AccountService newInstance(AccountService accountService) {
+ ClassLoader loader = accountService.getClass().getClassLoader();
+ Class>[] interfaces = accountService.getClass().getInterfaces();
+
+ return (AccountService) Proxy.newProxyInstance(loader,
+ interfaces,
+ new AccountServiceDynamicProxy(accountService));
+ }
+}
diff --git a/src/cn/aofeng/demo/proxy/AccountServiceImpl.java b/src/cn/aofeng/demo/proxy/AccountServiceImpl.java
new file mode 100644
index 0000000..f527efc
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/AccountServiceImpl.java
@@ -0,0 +1,53 @@
+package cn.aofeng.demo.proxy;
+
+import java.util.Random;
+
+import wiremock.org.apache.commons.lang.StringUtils;
+
+/**
+ *
+ *
+ * @author 聂勇
+ */
+public class AccountServiceImpl implements AccountService {
+
+ private final static Random NUM = new Random(System.currentTimeMillis());
+
+ @Override
+ public Result register(String username) {
+ if (StringUtils.isBlank(username)) {
+ return createResult(4000001, "注册失败");
+ }
+
+ User user = new User();
+ user.setUid(NUM.nextInt());
+ user.setNickname("用户"+user.getUid());
+ return createResult(2000001, "注册成功", user);
+ }
+
+ @Override
+ public Result login(String username, String password) {
+ if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+ return createResult(4000001, "登录失败");
+ }
+
+ User user = new User();
+ user.setUid(NUM.nextInt());
+ user.setNickname("用户"+user.getUid());
+ return createResult(2000001, "登录成功", user);
+ }
+
+ private Result createResult(int code, String msg) {
+ Result result = new Result(code, msg);
+
+ return result;
+ }
+
+ private Result createResult(int code, String msg, User user) {
+ Result result = new Result(code, msg);
+ result.setData(user);
+
+ return result;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/proxy/AccountServiceStaticProxy.java b/src/cn/aofeng/demo/proxy/AccountServiceStaticProxy.java
new file mode 100644
index 0000000..9705868
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/AccountServiceStaticProxy.java
@@ -0,0 +1,39 @@
+package cn.aofeng.demo.proxy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link AccountServiceImpl}的代理类,增加了输出入参和响应的日志。
+ *
+ * @author 聂勇
+ */
+public class AccountServiceStaticProxy implements AccountService {
+
+ private AccountService _delegate;
+
+ private final static Logger _LOGGER = LoggerFactory.getLogger(AccountServiceStaticProxy.class);
+
+ public AccountServiceStaticProxy(AccountService accountService) {
+ this._delegate = accountService;
+ }
+
+ @Override
+ public Result register(String username) {
+ _LOGGER.debug("execute method register, arguments: username={}", username);
+ Result result = _delegate.register(username);
+ _LOGGER.debug("execute method register, result:{}", result);
+
+ return result;
+ }
+
+ @Override
+ public Result login(String username, String password) {
+ _LOGGER.debug("execute method login, arguments: username={}, password={}", username, password);
+ Result result = _delegate.login(username, password);
+ _LOGGER.debug("execute method login, result:{}", result);
+
+ return result;
+ }
+
+}
diff --git a/src/cn/aofeng/demo/proxy/Result.java b/src/cn/aofeng/demo/proxy/Result.java
new file mode 100644
index 0000000..66ee038
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/Result.java
@@ -0,0 +1,65 @@
+package cn.aofeng.demo.proxy;
+
+/**
+ * 账号服务操作结果。
+ *
+ * @author 聂勇
+ */
+public class Result {
+
+ /**
+ * 200xxxx 表示成功。
+ * 400xxxx 表示参数错误。
+ * 500xxxx 表示服务内部错误。
+ */
+ private int code;
+
+ /**
+ * code对应的描述信息。
+ */
+ private String msg;
+
+ /**
+ * 只有code为200xxxx时才有值,其他情况下为null。
+ */
+ private User data;
+
+ public Result() {
+ // nothing
+ }
+
+ public Result(int code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public User getData() {
+ return data;
+ }
+
+ public void setData(User data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "Result [code=" + code + ", msg=" + msg + ", data=" + data + "]";
+ }
+
+}
diff --git a/src/cn/aofeng/demo/proxy/User.java b/src/cn/aofeng/demo/proxy/User.java
new file mode 100644
index 0000000..a9cbd69
--- /dev/null
+++ b/src/cn/aofeng/demo/proxy/User.java
@@ -0,0 +1,38 @@
+/**
+ *
+ */
+package cn.aofeng.demo.proxy;
+
+/**
+ * 用户信息。
+ *
+ * @author 聂勇
+ */
+public class User {
+
+ private long uid;
+
+ private String nickname;
+
+ public long getUid() {
+ return uid;
+ }
+
+ public void setUid(long uid) {
+ this.uid = uid;
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+
+ @Override
+ public String toString() {
+ return "User [uid=" + uid + ", nickname=" + nickname + "]";
+ }
+
+}
diff --git a/src/cn/aofeng/demo/script/MultiScriptEngineCompare.java b/src/cn/aofeng/demo/script/MultiScriptEngineCompare.java
new file mode 100644
index 0000000..5e1d61e
--- /dev/null
+++ b/src/cn/aofeng/demo/script/MultiScriptEngineCompare.java
@@ -0,0 +1,250 @@
+package cn.aofeng.demo.script;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+/**
+ * 多个脚本引擎执行JavaScript的性能比较。
+ *
+ * @author 聂勇
+ */
+public class MultiScriptEngineCompare {
+
+ public final static String PARSE = "parse";
+ public final static String COMPILE = "compile";
+
+ /**
+ * 获取指定的脚本引擎执行指定的脚本(解释执行)。
+ *
+ * @param scriptEngineName 脚本引擎名称
+ * @param script 脚本
+ * @param count 脚本的执行次数
+ * @param vars 绑定到脚本的变量集合
+ * @throws ScriptException 执行脚本出错
+ */
+ public ExecuteResult parse(String scriptEngineName, String script, int count,
+ Map vars) throws ScriptException {
+ ScriptEngine scriptEngine = getScriptEngine(scriptEngineName);
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < count; i++) {
+ runSingleScript(script, vars, scriptEngine);
+ }
+ long usedTime = System.currentTimeMillis() - startTime;
+
+ ExecuteResult result = new ExecuteResult();
+ result.setEngine(scriptEngine.getFactory().getEngineName());
+ result.setScript(script);
+ result.setBindParam(vars.toString());
+ result.setExecuteCount(count);
+ result.setExecuteType(PARSE);
+ result.setUsedTime(usedTime);
+
+ return result;
+ }
+
+ private void runSingleScript(String script, Map vars,
+ ScriptEngine scriptEngine) throws ScriptException {
+ if (null == vars || vars.isEmpty()) {
+ scriptEngine.eval(script);
+ } else {
+ Bindings binds = createBinding(scriptEngine, vars);
+ scriptEngine.eval(script, binds);
+ }
+ }
+
+ public ExecuteResult compile(String scriptEngineName, String script, int count,
+ Map vars) throws ScriptException {
+ ScriptEngine scriptEngine = getScriptEngine(scriptEngineName);
+ Compilable compileEngine = (Compilable) scriptEngine;
+ CompiledScript compileScript = compileEngine.compile(script);
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < count; i++) {
+ runSingleScript(compileScript, vars, scriptEngine);
+ }
+ long usedTime = System.currentTimeMillis() - startTime;
+
+ ExecuteResult result = new ExecuteResult();
+ result.setEngine(scriptEngine.getFactory().getEngineName());
+ result.setScript(script);
+ result.setBindParam(vars.toString());
+ result.setExecuteCount(count);
+ result.setExecuteType(COMPILE);
+ result.setUsedTime(usedTime);
+
+ return result;
+ }
+
+ private void runSingleScript(CompiledScript compileScript, Map vars,
+ ScriptEngine scriptEngine) throws ScriptException {
+ if (null == vars || vars.isEmpty()) {
+ compileScript.eval();
+ } else {
+ Bindings binds = createBinding(scriptEngine, vars);
+ compileScript.eval(binds);
+ }
+ }
+
+ protected void log(String msg) {
+ System.out.println(msg);
+ }
+
+ protected void log(String msg, Object... args) {
+ log( String.format(msg, args) );
+ }
+
+ /**
+ * 根据名称获取脚本引擎。
+ *
+ * @param name 脚本引擎名称
+ * @return 实现了{@link ScriptEngine}的脚本引擎。如果没有对应的脚本引擎,返回null。
+ */
+ public ScriptEngine getScriptEngine(String name) {
+ ScriptEngineManager sem = new ScriptEngineManager();
+ return sem.getEngineByName(name);
+ }
+
+ private Bindings createBinding(ScriptEngine scriptEngine, Map vars) {
+ Bindings binds = scriptEngine.createBindings();
+ if (null != vars && !vars.isEmpty()) {
+ binds.putAll(vars);
+ }
+
+ return binds;
+ }
+
+ /**
+ * @param args 执行次数
+ * @throws ScriptException
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public static void main(String[] args) throws ScriptException {
+ if ( args.length != 2) {
+ System.err.println("参数错误。\n语法格式:\n java cn.aofeng.demo.script.MultiScriptEngineCompare 脚本执行次数 脚本执行方式(parse|compile)\n使用示例:\n java cn.aofeng.demo.script.MultiScriptEngineCompare 100000 parse");
+ System.exit(-1);
+ }
+ int count = Integer.parseInt(args[0]);
+ String executeType = args[1];
+
+ String[] scriptEngineList = {"JavaScript", "JEXL"};
+
+ String script1 = "var c = a + b;" +
+ "var d = a * b;" +
+ "var e = a / b;" +
+ "var f = a % b;" +
+ "var g = a - b;" +
+ "var result = ((a * 5) > b || b * 10 >= 100) && (a * b > 99);";
+ Map vars1 = new HashMap(2);
+ vars1.put("a", 20);
+ vars1.put("b", 9);
+
+ String script2 = "var result = src.indexOf(b);";
+ Map vars2 = new HashMap(2);
+ vars2.put("src", "compare performance javascript and jexl");
+ vars2.put("b", "script");
+
+ String[] scriptList = {script1, script2};
+ Map[] varsList = {vars1, vars2};
+
+ MultiScriptEngineCompare msec = new MultiScriptEngineCompare();
+ List resultList = new ArrayList();
+ for (int i = 0; i < scriptEngineList.length; i++) {
+ for (int j = 0; j < scriptList.length; j++) {
+ if (PARSE.equalsIgnoreCase(executeType)) {
+ resultList.add( msec.parse(scriptEngineList[i], scriptList[j], count, varsList[j]) );
+ } else if (COMPILE.equalsIgnoreCase(executeType)) {
+ resultList.add( msec.compile(scriptEngineList[i], scriptList[j], count, varsList[j]) );
+ } else {
+ msec.log("错误的执行方式:%s", executeType);
+ }
+ }
+ }
+
+ List arrayList = new ArrayList();
+ arrayList.add(new String[]{"脚本引擎", "脚本", "脚本绑定参数", "脚本执行次数", "脚本执行类型", "消耗时间(毫秒)", "JDK版本"});
+ for (Iterator iterator = resultList.iterator(); iterator.hasNext();) {
+ ExecuteResult er = (ExecuteResult) iterator.next();
+ arrayList.add(new String[]{er.getEngine(),
+ er.getScript(),
+ er.getBindParam(),
+ String.valueOf(er.getExecuteCount()),
+ er.getExecuteType(),
+ String.valueOf(er.getUsedTime()),
+ er.getJdkVersion()});
+ }
+
+ String[][] table = new String[arrayList.size()][7];
+ arrayList.toArray(table);
+
+ PrettyTable prettyTable = new PrettyTable(System.out);
+ prettyTable.print(table);
+ }
+
+ static class ExecuteResult {
+
+ private String engine;
+ private String script;
+ private String bindParam;
+ private int executeCount;
+ private String executeType;
+ private long usedTime;
+ private String jdkVersion;
+
+ public ExecuteResult() {
+ this.jdkVersion = System.getProperty("java.version");
+ }
+
+ public String getEngine() {
+ return engine;
+ }
+ public void setEngine(String engine) {
+ this.engine = engine;
+ }
+ public String getScript() {
+ return script;
+ }
+ public void setScript(String script) {
+ this.script = script;
+ }
+ public String getBindParam() {
+ return bindParam;
+ }
+ public void setBindParam(String bindParam) {
+ this.bindParam = bindParam;
+ }
+ public int getExecuteCount() {
+ return executeCount;
+ }
+ public void setExecuteCount(int executeCount) {
+ this.executeCount = executeCount;
+ }
+ public String getExecuteType() {
+ return executeType;
+ }
+ public void setExecuteType(String executeType) {
+ this.executeType = executeType;
+ }
+ public long getUsedTime() {
+ return usedTime;
+ }
+ public void setUsedTime(long usedTime) {
+ this.usedTime = usedTime;
+ }
+ public String getJdkVersion() {
+ return jdkVersion;
+ }
+ public void setJdkVersion(String jdkVersion) {
+ this.jdkVersion = jdkVersion;
+ }
+ }
+
+}
diff --git a/src/cn/aofeng/demo/script/PrettyTable.java b/src/cn/aofeng/demo/script/PrettyTable.java
new file mode 100644
index 0000000..b45b60c
--- /dev/null
+++ b/src/cn/aofeng/demo/script/PrettyTable.java
@@ -0,0 +1,125 @@
+package cn.aofeng.demo.script;
+
+import java.io.PrintStream;
+import static java.lang.String.format;
+
+/**
+ * 此代码来自于网络。使用示例:
+ *
+ * PrettyTable printer = new PrettyTable(out);
+ * printer.print(new String[][] {
+ * new String[] {"FIRST NAME", "LAST NAME", "DATE OF BIRTH", "NOTES"},
+ * new String[] {"Joe", "Smith", "November 2, 1972"},
+ * null,
+ * new String[] {"John", "Doe", "April 29, 1970", "Big Brother"},
+ * new String[] {"Jack", null, null, "(yes, no last name)"}
+ * });
+
+ */
+public final class PrettyTable {
+
+ private static final char BORDER_KNOT = '+';
+ private static final char HORIZONTAL_BORDER = '-';
+ private static final char VERTICAL_BORDER = '|';
+
+ private static final String DEFAULT_AS_NULL = "(NULL)";
+
+ private final PrintStream out;
+ private final String asNull;
+
+ public PrettyTable(PrintStream out) {
+ this(out, DEFAULT_AS_NULL);
+ }
+
+ public PrettyTable(PrintStream out, String asNull) {
+ if ( out == null ) {
+ throw new IllegalArgumentException("No print stream provided");
+ }
+ if ( asNull == null ) {
+ throw new IllegalArgumentException("No NULL-value placeholder provided");
+ }
+ this.out = out;
+ this.asNull = asNull;
+ }
+
+ public void print(String[][] table) {
+ if ( table == null ) {
+ throw new IllegalArgumentException("No tabular data provided");
+ }
+ if ( table.length == 0 ) {
+ return;
+ }
+ final int[] widths = new int[getMaxColumns(table)];
+ adjustColumnWidths(table, widths);
+ printPreparedTable(table, widths, getHorizontalBorder(widths));
+ }
+
+ private void printPreparedTable(String[][] table, int widths[], String horizontalBorder) {
+ final int lineLength = horizontalBorder.length();
+ out.println(horizontalBorder);
+ for ( final String[] row : table ) {
+ if ( row != null ) {
+ out.println(getRow(row, widths, lineLength));
+ out.println(horizontalBorder);
+ }
+ }
+ }
+
+ private String getRow(String[] row, int[] widths, int lineLength) {
+ final StringBuilder builder = new StringBuilder(lineLength).append(VERTICAL_BORDER);
+ final int maxWidths = widths.length;
+ for ( int i = 0; i < maxWidths; i++ ) {
+ builder.append(padRight(getCellValue(safeGet(row, i, null)), widths[i])).append(VERTICAL_BORDER);
+ }
+ return builder.toString();
+ }
+
+ private String getHorizontalBorder(int[] widths) {
+ final StringBuilder builder = new StringBuilder(256);
+ builder.append(BORDER_KNOT);
+ for ( final int w : widths ) {
+ for ( int i = 0; i < w; i++ ) {
+ builder.append(HORIZONTAL_BORDER);
+ }
+ builder.append(BORDER_KNOT);
+ }
+ return builder.toString();
+ }
+
+ private int getMaxColumns(String[][] rows) {
+ int max = 0;
+ for ( final String[] row : rows ) {
+ if ( row != null && row.length > max ) {
+ max = row.length;
+ }
+ }
+ return max;
+ }
+
+ private void adjustColumnWidths(String[][] rows, int[] widths) {
+ for ( final String[] row : rows ) {
+ if ( row != null ) {
+ for ( int c = 0; c < widths.length; c++ ) {
+ final String cv = getCellValue(safeGet(row, c, asNull));
+ final int l = cv.length();
+ if ( widths[c] < l ) {
+ widths[c] = l;
+ }
+ }
+ }
+ }
+ }
+
+ private static String padRight(String s, int n) {
+ return format("%1$-" + n + "s", s);
+ }
+
+ private static String safeGet(String[] array, int index, String defaultValue) {
+ return index < array.length ? array[index] : defaultValue;
+ }
+
+ private String getCellValue(Object value) {
+ return value == null ? asNull : value.toString();
+ }
+
+}
diff --git a/src/cn/aofeng/demo/script/SupportScriptEngine.java b/src/cn/aofeng/demo/script/SupportScriptEngine.java
new file mode 100644
index 0000000..a2b042b
--- /dev/null
+++ b/src/cn/aofeng/demo/script/SupportScriptEngine.java
@@ -0,0 +1,36 @@
+package cn.aofeng.demo.script;
+
+import java.util.List;
+
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+
+/**
+ * 支持的脚本引擎列表。
+ *
+ * @author 聂勇
+ */
+public class SupportScriptEngine {
+
+ public void listScriptEngine() {
+ ScriptEngineManager sem = new ScriptEngineManager();
+ List sefList = sem.getEngineFactories();
+ for (ScriptEngineFactory factory : sefList) {
+ printScriptEngineInfo(factory);
+ }
+ }
+
+ private void printScriptEngineInfo(ScriptEngineFactory factory) {
+ System.out.println("ScriptEngineName:" + factory.getEngineName()
+ + ", Names:" + factory.getNames()
+ + ", ScriptEngineVersion:" + factory.getEngineVersion()
+ + ", LanguageName:" + factory.getLanguageName()
+ + ", LanguageVersion:" + factory.getLanguageVersion() );
+ }
+
+ public static void main(String[] args) {
+ SupportScriptEngine msep = new SupportScriptEngine();
+ msep.listScriptEngine();
+ }
+
+}
diff --git a/src/cn/aofeng/demo/slf4j/HelloSlf4j.java b/src/cn/aofeng/demo/slf4j/HelloSlf4j.java
new file mode 100644
index 0000000..dce7ffd
--- /dev/null
+++ b/src/cn/aofeng/demo/slf4j/HelloSlf4j.java
@@ -0,0 +1,31 @@
+package cn.aofeng.demo.slf4j;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * slf4j使用示例。
+ *
+ * @author 聂勇
+ */
+public class HelloSlf4j {
+
+ private static Logger _logger = LoggerFactory.getLogger(HelloSlf4j.class);
+
+ public static void main(String[] args) {
+ // 直接输出字符串
+ _logger.info("Hello, slf4j logger.");
+
+ // 输出格式字符串(携带多个填充参数)
+ _logger.info("{} + {} = {}", 1, 2, (1+2));
+
+ // 输出错误信息和异常堆栈
+ _logger.error("错误信息", new IOException("测试抛出IO异常信息"));
+
+ // 输出错误信息(携带多个填充参数)和异常堆栈
+ _logger.error("两个参数。agrs1:{};agrs2:{}的info级别日志", "args1", "args2", new IOException("测试抛出IO异常信息"));
+ }
+
+}
diff --git a/src/cn/aofeng/demo/thread/DaemonThreadDemo.java b/src/cn/aofeng/demo/thread/DaemonThreadDemo.java
new file mode 100644
index 0000000..541d133
--- /dev/null
+++ b/src/cn/aofeng/demo/thread/DaemonThreadDemo.java
@@ -0,0 +1,45 @@
+/**
+ * 公司:阿里游戏
+ * 创建时间:2018年11月2日下午5:56:43
+ */
+package cn.aofeng.demo.thread;
+
+import java.util.Date;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 守护线程DEMO。
+ *
+ * @author 聂勇
+ */
+public class DaemonThreadDemo extends Thread {
+
+ private static Logger logger = LoggerFactory.getLogger(DaemonThreadDemo.class);
+
+ @Override
+ public void run() {
+ while (true) {
+ System.out.println("守护线程运行, 时间:" + new Date());
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.error("守护线程运行出错", e);
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ DaemonThreadDemo thread = new DaemonThreadDemo();
+ thread.setDaemon(true);
+ thread.start();
+
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ logger.error("主线程运行出错", e);
+ }
+ }
+
+}
diff --git a/src/cn/aofeng/demo/tree/PrettyTree.java b/src/cn/aofeng/demo/tree/PrettyTree.java
new file mode 100644
index 0000000..76aa333
--- /dev/null
+++ b/src/cn/aofeng/demo/tree/PrettyTree.java
@@ -0,0 +1,118 @@
+package cn.aofeng.demo.tree;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 输出类似目录树的结构。
+ *
+ * @author 聂勇
+ */
+public class PrettyTree {
+
+ private final static String NODE_INDENT_PREFIX = "| ";
+ private final static String NODE_PARENT_PREFIX = "├── ";
+ private final static String NODE_LEAF_PREFIX = "└── ";
+ private final static String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
+
+ public void renderRoot(Node root, StringBuilder buffer) {
+ renderParentNode(root, 0, buffer);
+ }
+
+ private void renderParentNode(Node node, int indent, StringBuilder buffer) {
+ addIndent(indent, buffer);
+ buffer.append(NODE_PARENT_PREFIX)
+ .append(node.getName())
+ .append(LINE_SEPARATOR);
+ List childList = node.getChild();
+ if (null == childList) {
+ return;
+ }
+ for (Node child : childList) {
+ if (child.isLeaf()) {
+ renderLeafNode(child, indent+1, buffer);
+ } else {
+ renderParentNode(child, indent+1, buffer);
+ }
+ }
+ }
+
+ private void renderLeafNode(Node node, int indent, StringBuilder buffer) {
+ addIndent(indent, buffer);
+ buffer.append(NODE_LEAF_PREFIX)
+ .append(node.getName())
+ .append(LINE_SEPARATOR);
+ }
+
+ private void addIndent(int indent, StringBuilder buffer) {
+ for (int i = 0; i < indent; i++) {
+ buffer.append(NODE_INDENT_PREFIX);
+ }
+ }
+
+ public static class Node {
+
+ /** 节点名称。 */
+ private String name;
+
+ /** 子节点列表 */
+ private List child = new LinkedList();
+
+ public Node(String name) {
+ this.name = name;
+ }
+
+ public Node(String name, List child) {
+ this.name = name;
+ this.child = child;
+ }
+
+ /**
+ * 判断当前节点是否为叶子节点。
+ *
+ * @return 如果是叶子节点返回true;否则返回false。
+ */
+ public boolean isLeaf() {
+ if (null == child || child.isEmpty()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 批量添加子节点。
+ *
+ * @param child 子节点列表。
+ */
+ public void addAll(List nodeList) {
+ this.child.addAll(nodeList);
+ }
+
+ /**
+ * 添加单个子节点。
+ *
+ * @param node 子节点。
+ */
+ public void add(Node node) {
+ this.child.add(node);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getChild() {
+ return child;
+ }
+
+ public void setChild(List child) {
+ this.child = child;
+ }
+ } // end of Node
+
+}
diff --git a/src/cn/aofeng/demo/tree/PrettyTreeTest.java b/src/cn/aofeng/demo/tree/PrettyTreeTest.java
new file mode 100644
index 0000000..6a03075
--- /dev/null
+++ b/src/cn/aofeng/demo/tree/PrettyTreeTest.java
@@ -0,0 +1,41 @@
+package cn.aofeng.demo.tree;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import cn.aofeng.demo.tree.PrettyTree.Node;
+
+public class PrettyTreeTest {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testPrintRoot() {
+ Node p11 = new Node("p1-1");
+ Node p12 = new Node("p1-2");
+ Node p1 = new Node("p1");
+ p1.add(p11);
+ p1.add(p12);
+
+ Node p21 = new Node("p21");
+ Node p2 = new Node("p2");
+ p12.add(p21);
+
+ Node root = new Node("Root");
+ root.add(p1);
+ root.add(p2);
+
+ StringBuilder buffer = new StringBuilder(256);
+ PrettyTree pt = new PrettyTree();
+ pt.renderRoot(root, buffer);
+ System.out.print(buffer.toString());
+ }
+
+}
diff --git a/src/cn/aofeng/demo/util/LogUtil.java b/src/cn/aofeng/demo/util/LogUtil.java
index 6046793..44558c9 100644
--- a/src/cn/aofeng/demo/util/LogUtil.java
+++ b/src/cn/aofeng/demo/util/LogUtil.java
@@ -7,8 +7,12 @@
*/
public class LogUtil {
+ public static void log(String msg) {
+ System.out.println(msg);
+ }
+
public static void log(String msg, Object... param) {
- System.out.println( String.format(msg, param) );
+ log( String.format(msg, param) );
}
}
diff --git a/src/cn/aofeng/demo/wiremock/README.md b/src/cn/aofeng/demo/wiremock/README.md
index 45d9729..e429c9e 100644
--- a/src/cn/aofeng/demo/wiremock/README.md
+++ b/src/cn/aofeng/demo/wiremock/README.md
@@ -1,6 +1,6 @@
使用WireMock实现Http Server Mock作单元测试
===
-前一篇文章"[使用jetty实现Http Server Mock作单元测试](http://aofengblog.blog.163.com/blog/static/631702120144581541484/)"
+前一篇文章"[使用jetty实现Http Server Mock作单元测试](http://aofengblog.com/2014/05/06/WireMock-%E5%AE%9E%E7%8E%B0Http-Server-Mock%E7%94%A8%E4%BA%8E%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/)"
讲了用Jetty实现Http Server Mock来模拟依赖的外部HTTP服务系统,但如果需要更多的功能,如:区分GET和POST;匹配请求的URL;匹配Http Header;匹配请求内容等等。
实际研发的过程中这些功能都有可能会用到,如果还是用Jetty来实现,需要自己不停地动手去添砖加瓦,虽然有成就感,但在快速迭代的节奏下不一定有足够的时间去做这些。
这时我们需要一个现成的类库来满足我们这些需求,WireMock和MockServer都可以做到,这里只讲WireMock。