diff --git a/README.md b/README.md index cee9b09..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,48 +32,31 @@ * [集合-反序列化](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/) -##定时器 -###代码 -* [Timer](src/cn/aofeng/demo/java/util/timer/TimerDemo.java) -* [ScheduledExecutorService](src/cn/aofeng/demo/java/util/concurret/ScheduledExecutorServiceDemo.java) - -##设计模式 -###代码 -* [事件驱动](src/cn/aofeng/demo/eventdriver_normal) -* [事件驱动改进版](src/cn/aofeng/demo/eventdriver_improve) -* [Reactor](src/cn/aofeng/demo/reactor) - -##单元测试 -###文章 +## 单元测试 * [单元测试 - 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) + +### EasyMock +* [使用EasyMock作单元测试](src/cn/aofeng/demo/easymock) + +### 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 -* [EchoServer](src/cn/aofeng/demo/netty40x/echo/EchoServer.java) -* [EchoClient](src/cn/aofeng/demo/netty40x/echo/EchoClient.java) - -###文章 * [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/) @@ -87,66 +64,86 @@ * [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) + + +## 线程&并发 +* [守护线程](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.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.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/) -##加密解密 -###代码 +## 加密解密 * [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企业开发 -##开源框架 -###文章 +## 开源组件 + +### Slf4j +* [slf4j使用示例](src/cn/aofeng/demo/slf4j/HelloSlf4j.java) + +### Redis +* [Redis客户端Jedis使用示例](src/cn/aofeng/demo/redis/JedisDemo.java) + +### 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/) -##XML -###代码 -* [XPath](src/cn/aofeng/demo/xml/XPathDemo.java) +### Tomcat +* [JMX管理Tomcat/Resin](http://aofengblog.com/2008/08/17/Tomcat-JMX%E7%AE%A1%E7%90%86Tomcat-Resin/) + +### 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/) -##Tomcat -###文章 -* [JMX管理Tomcat/Resin](http://aofengblog.blog.163.com/blog/static/6317021200871711013857/) - -##Jetty -* []() - -##JBoss -###文章 -* [JBoss5.1.0 GA - 部署项目类库兼容和类库冲突问题解决方法](http://aofengblog.blog.163.com/blog/static/631702120113674626533/) - +## 设计模式 +* [事件驱动](src/cn/aofeng/demo/eventdriver_normal) +* [事件驱动改进版](src/cn/aofeng/demo/eventdriver_improve) +* [Reactor](src/cn/aofeng/demo/reactor) -#Java工具 -##文章 +## 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.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/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/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/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 + * + */ + 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/UserServiceTest.java b/src/cn/aofeng/demo/mockito/UserServiceTest.java index e8fab4e..fd7009e 100644 --- a/src/cn/aofeng/demo/mockito/UserServiceTest.java +++ b/src/cn/aofeng/demo/mockito/UserServiceTest.java @@ -52,11 +52,11 @@ public void testIsAdult4UserNotExist() { public void testBuy() { long userId = 12345; UserDao daoMock = mock(UserDao.class); - when(daoMock.queryById(userId)).thenReturn(new User(userId, "张三", 19)); + when(daoMock.queryById(anyLong())).thenReturn(new User(userId, "张三", 19)); String commodityId = "S01A10009823"; CommodityDao commodityDao = mock(CommodityDao.class); - when(commodityDao.queryById(commodityId)).thenReturn(new Commodity(commodityId, "xxx手机", 1)); + when(commodityDao.queryById(anyString())).thenReturn(new Commodity(commodityId, "xxx手机", 1)); _userService.setUserDao(daoMock); _userService.setCommodityDao(commodityDao); 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/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 index faac39b..5e1d61e 100644 --- a/src/cn/aofeng/demo/script/MultiScriptEngineCompare.java +++ b/src/cn/aofeng/demo/script/MultiScriptEngineCompare.java @@ -1,9 +1,14 @@ 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; @@ -15,8 +20,11 @@ */ public class MultiScriptEngineCompare { + public final static String PARSE = "parse"; + public final static String COMPILE = "compile"; + /** - * 获取指定的脚本引擎执行指定的脚本。 + * 获取指定的脚本引擎执行指定的脚本(解释执行)。 * * @param scriptEngineName 脚本引擎名称 * @param script 脚本 @@ -24,22 +32,28 @@ public class MultiScriptEngineCompare { * @param vars 绑定到脚本的变量集合 * @throws ScriptException 执行脚本出错 */ - public void execute(String scriptEngineName, String script, int count, + public ExecuteResult parse(String scriptEngineName, String script, int count, Map vars) throws ScriptException { ScriptEngine scriptEngine = getScriptEngine(scriptEngineName); - System.out.println( String.format("脚本引擎:%s", scriptEngine.getFactory().getEngineName()) ); - System.out.println( String.format("脚本:%s", script) ); - System.out.println( String.format("脚本绑定参数:%s", vars) ); long startTime = System.currentTimeMillis(); for (int i = 0; i < count; i++) { runSingleScript(script, vars, scriptEngine); } long usedTime = System.currentTimeMillis() - startTime; - System.out.println( String.format("脚本执行%d次,消耗%d毫秒。", count, usedTime) ); - System.out.println(""); + + 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 { + private void runSingleScript(String script, Map vars, + ScriptEngine scriptEngine) throws ScriptException { if (null == vars || vars.isEmpty()) { scriptEngine.eval(script); } else { @@ -48,6 +62,46 @@ private void runSingleScript(String script, Map vars, ScriptEngi } } + 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) ); + } + /** * 根据名称获取脚本引擎。 * @@ -74,11 +128,12 @@ private Bindings createBinding(ScriptEngine scriptEngine, Map va */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String[] args) throws ScriptException { - if ( args.length != 1) { - System.err.println("参数错误。使用示例:\n java cn.aofeng.demo.script.MultiScriptEngineCompare 100000"); + 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"}; @@ -101,11 +156,95 @@ public static void main(String[] args) throws ScriptException { 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++) { - msec.execute(scriptEngineList[i], scriptList[j], count, varsList[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/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) ); } }