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。