Java内容重新整理删除过期的东西

This commit is contained in:
estom
2025-09-14 03:49:42 -04:00
parent 9b8524ff80
commit 885b795e45
413 changed files with 643 additions and 1340 deletions

View File

@@ -0,0 +1,3 @@
## 文件说明
> 次目录是**Java语言基础**中的代码实例。

View File

@@ -0,0 +1,16 @@
package com.ykl;
/**
* 整个项目的启动程序
*/
public class HelloWorld {
/*
* 多行注释,可以注释一段文字
* */
public static void main(String[] args) {
/**
* Java Doc能够识别的注释两个*号开始
*/
//单行注释
System.out.println("Hello World!");
}
}

View File

@@ -0,0 +1,21 @@
package com.ykl;
/**
* @author ykl
* @since 2022
* @version 1.0
* 验证前置类型转换的有效性
*/
public class Demo5 {
/**
*
* @param args canshu
*/
public static void main(String[] args) {
int i = 128;
byte b = (byte)i;//-128 强制类型转换,避免内存溢出。
System.out.println(b);
// 操作比较大的数的时候,注意溢出问题。数字之间可以使用下划线进行分割,只起到标识作用。
}
}

View File

@@ -0,0 +1,36 @@
// package com.ykl.exceptions;
/**
* Java工程目录结构
* * src下的内容才能被识别
*/
public class ExceptionTest{
public static void main(String[] args) {
System.out.println("Hello World!");
int a =0;
int b =1;
try {
System.out.println(b/a);
} catch (ArithmeticException e) {
//TODO: handle exception
System.out.println("数学异常");
System.out.println("e.toString"+e.toString());
System.out.println("e.printStackTrace()");
e.printStackTrace();
// System.out.println();
System.out.println("e.getMessage"+e.getMessage());
System.out.println("e.getCause"+e.getCause());
System.out.println("e.fillInStackTrace()"+e.fillInStackTrace());
} finally{
System.out.println("finnaly清理工作");
}
// print some test cases
System.out.println("hello world");
}
}

View File

@@ -0,0 +1,20 @@
package com.ykl.extentions;
/**
* 验证静态变量能够被类的实例访问
*/
class Book{
private String name;
private int price;
static final String id="BOOK";
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.name);
System.out.println(book.price);
// 事实证明这三种方法都能够访问到类变量
System.out.println(id);
System.out.println(Book.id);
System.out.println(book.id);
}
}

View File

@@ -0,0 +1,45 @@
import java.lang.Thread;
/**
* AnonymousClass
*/
public class AnonymousClass {
private int a;
public static void main(String[] args){
new AnonymousClass().test(2);
// 成员内部类需要创建对象
AnonymousClass ac = new AnonymousClass();
ac.new Inner().getName();
//静态内部类可以直接访问
new AnonymousClass.StaticInner().getName();
}
public class Inner{
public void getName(){
System.out.println("成员内部类");
}
}
public static class StaticInner{
public void getName(){
System.out.println("静态内部类");
}
}
//事实证明匿名内部类必须访问final类型的变量或者事实上final类型的变量。
public void test(final int a){
int b =10;
int c =11;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
}
}.start();
// b = 12;
System.out.print(b);
}
}

View File

@@ -0,0 +1,31 @@
package com.ykl.innerclass;
public class PartialDemo {
static String name = "王五";
String name2 = "周七";
public void demo() {
String name = "张三";
class Inner{
String name = "李四";
public void showInner(String name) {
System.out.println("这是外部类变量:"+PartialDemo.this.name2);
System.out.println("这是外部类变量(静态变量可以):"+PartialDemo.name);
System.out.println("这是方法中局部变量变量:"+name);
System.out.println("这是局部内部类中的变量:"+this.name);
}
}
Inner inner=new Inner();
inner.showInner(name);
}
public static void main(String[] args) {
PartialDemo partialDemo = new PartialDemo();
partialDemo.demo();
}
}

View File

@@ -0,0 +1,41 @@
package com.ykl;
/**
* 验证函数重载和类型转换的优先级
*/
import java.sql.Array;
public class Demo6 {
public static void main(String[] args) {
int [] numbers = {10, 20, 30, 40, 50};
max(1.1,2);
// for(int x : numbers ) {
// if( x == 30 ) {
// continue;
// }
// System.out.print( x );
// System.out.print("\n");
// }
// for (int i = 0; i <= 5; i++) {
// for (int j = 0; j < i; j++) {
// System.out.print("*");
// }
// for (int k = 0; k < 5; k++) {
//
// }
// System.out.println();
// }
Integer a = 10;
Array b;
}
public static void max(double a,double b){
System.out.println(1);
}
public static void max(int a,int b){
System.out.println(2);
}
public static void max(int a,int b,int c){
System.out.println(3);
}
}

View File

@@ -0,0 +1,43 @@
package com.ykl;
import java.util.Scanner;
/**
* 验证scanner输入输出的有效性
*/
public class ScannerTest {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("使用不同的方式读取数据");
// // hasNext会一直阻塞直到由新的内容。
// while(s.hasNext()){
// System.out.println("读取前的环节");
// String str = s.next();
// System.out.println("读取到的内容位:"+str);
// }
//
//
// // hasNextLine会一直阻塞直到由新的内容。测试一下next()是否会阻塞
// while(s.hasNextLine()){
// System.out.println("读取前的环节");
// String str = s.nextLine();
// System.out.println("读取到的内容位:"+str);
// }
//
//hasNextInt方法会阻塞如果不是整数会返回False
if(s.hasNextInt()){
System.out.println("读取前的环节");
int str = s.nextInt();
System.out.println("读取到的内容位:"+str);
}
else{
System.out.println("你输入的不是整数");
}
s.close();
}
}

View File

@@ -0,0 +1,37 @@
package com.ykl.stacktrace;
import java.lang.System;
import java.lang.RuntimeException;
import java.lang.Thread;
import java.util.stream.Stream;
public class PrintTrackTest {
void printTrackTest() {
// 1.打印调用堆栈
RuntimeException e = new RuntimeException("print stacktrace");
// e.fillInStackTrace();
System.out.println("1.打印调用堆栈");
Stream.of(e.getStackTrace()).forEach(System.out::println);
// 2.打印调用堆栈
System.out.println("2.打印调用堆栈");
Stream.of(Thread.currentThread().getStackTrace()).forEach(System.out::println);
}
public void first_method(){
second_method();
}
public void second_method(){
printTrackTest();
}
public static void main(String[] args) {
new PrintTrackTest().first_method();
}
}

View File

@@ -0,0 +1,23 @@
package com.ykl.wrapper;
/**
* 用来验证拆箱装箱的有效性
*/
public class WrapperTest {
public static void main(String[] args) {
int a=1;
int b=2;
Integer c =1;
Integer d =2;
Integer e =new Integer(1);
System.out.println(a==b);
System.out.println(a==c);
System.out.println(c==d);
System.out.println(c==e);//不拆箱
System.out.println(c.equals(d));
System.out.println(c.equals(e));
System.out.println(e.equals(a));//类型不转换
}
}

View File

@@ -0,0 +1,3 @@
## 文档说明
这是Springboot的实践教程

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ykl</groupId>
<artifactId>demo01</artifactId>
<version>1.0-SNAPSHOT</version>
<name>demo01</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,22 @@
package com.ykl;
import com.ykl.example01.Animal;
import com.ykl.example01.Dog;
/**
* Hello world!
*
*/
public class App01
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
Dog dd = new Dog();
Animal ad = new Dog();
dd.say();
dd.eat();
ad.say();
ad.eat();
}
}

View File

@@ -0,0 +1,26 @@
package com.ykl;
import com.ykl.example01.Animal;
import com.ykl.example01.Dog;
/**
* Hello world!
* 用来验证不同的类型转换方法,是否成功和最终输出的结果。
*/
public class App02
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
Dog dd = new Dog();
Animal aa = new Animal();
Animal ad = new Dog();
dd.say(); //dog say
((Animal)dd).say();//dog say
aa.say();// animal say
// ((Dog) aa).say();// down
ad.say();//dog say
((Dog) ad).say();//dog say
}
}

View File

@@ -0,0 +1,18 @@
package com.ykl;
import com.ykl.example01.Person;
/**
* Hello world!
* 用来验证初始化的执行顺序。
*/
public class App03
{
public static void main( String[] args )
{
Person person = new Person();
Person person3 = new Person();
person.say();
}
}

View File

@@ -0,0 +1,10 @@
package com.ykl.example01;
public class Animal {
public final void eat(){
System.out.println("animal eat");
}
public void say(){
System.out.println("animal say");
}
}

View File

@@ -0,0 +1,14 @@
package com.ykl.example01;
import com.ykl.example01.Animal;
public class Dog extends Animal{
// final方法默认不能被覆盖编译器报错。
// public void eat(){
// System.out.println("dog eat");
// }
public void say(){
System.out.println("dog say");
}
}

View File

@@ -0,0 +1,37 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2022 All Rights Reserved.
*/
package com.ykl.example01;
import java.sql.SQLOutput;
/**
* @author yinkanglong
* @version : Person, v 0.1 2022-07-24 01:56 yinkanglong Exp $
*/
public class Person {
//静态变量
public static int age=10;
//静态方法
public static void say(){
System.out.println("静态方法执行了");
}
//代码快
{
System.out.println("普通代码块");
}
//静态代码快
static {
System.out.println("静态代码块");
}
public Person(){
System.out.println("构造器执行了");
}
}

View File

@@ -0,0 +1,51 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2022 All Rights Reserved.
*/
package com.ykl.example04;
/**
* @author yinkanglong
* @version : Outer, v 0.1 2022-07-24 13:19 yinkanglong Exp $
*/
public class Outer {
private int id;
public void out(){
System.out.println("outer method");
}
public class Inner{
public void in(){
System.out.println("inner method");
//成员内部类可以直接获取外部类的私有属性。
}
}
public static class InnerStatic{
}
public void method(){
class Inner{
public void inMehtod(){
System.out.println("这是一个方法内部类");
}
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.out();
//通过外部类的new方法实例化一个对象
Outer.Inner inner = outer.new Inner();
inner.in();
new A().eat();
}
}
//一个Java文件中有多个类但是只能有一个public
class A{
public void eat(){
}
}

View File

@@ -0,0 +1,20 @@
package com.ykl;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
}

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>web-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>web-project Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>web-project</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,7 @@
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>

View File

@@ -0,0 +1,5 @@
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

9
Java/JavaDemo/README.md Normal file
View File

@@ -0,0 +1,9 @@
## 说明
### 项目层级
1. JavaDemo
2. 核心框架模块
3. 业务类型模块
4. 业务子模块

View File

@@ -0,0 +1,142 @@
package cn.aofeng.demo.aspectj;
/**
* 模拟业务方法将被Aspectj织入代码增加功能。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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();
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
@Aspect
public class BusinessServiceInterceptor {
private static Logger _logger = LoggerFactory.getLogger(BusinessServiceInterceptor.class);
private char _style = '-';
/**
* <pre>
* Before通知不能修改方法传入的参数。
* </pre>
*/
@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);
}
/**
* <pre>
* After通知不能修改方法的返回值。
* 如果被拦截的方法抛出异常,拦截代码仍然正常执行。
* </pre>
*/
@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);
}
/**
* <pre>
* AfterReturning通知不能修改方法的返回值。
* 如果被拦截的方法抛出异常,拦截代码不再执行。
* </pre>
*/
@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);
}
/**
* <pre>
* 只在被拦截的方法抛出异常时才执行。
* </pre>
*/
@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);
}
/**
* <pre>
* {@link ProceedingJoinPoint}只能在Around通知中使用。
* Around通知可以修改被拦截方法的传入参数和返回值。
* </pre>
*/
@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;
}
}

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="JavaTutorial" default="runNormal" basedir="../../../../../">
<property name="project.src.dir" value="${basedir}/src" />
<property name="project.lib.dir" value="${basedir}/lib" />
<property name="project.conf.dir" value="${basedir}/conf" />
<property name="project.tmp.dir" value="${basedir}/tmp" />
<property name="project.target.dir" value="${basedir}/classes" />
<path id="project.classpath">
<fileset dir="${project.lib.dir}">
<include name="*.jar" />
</fileset>
</path>
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
<classpath refid="project.classpath" />
</taskdef>
<target name="prepare">
<delete dir="${project.target.dir}" />
<mkdir dir="${project.target.dir}"/>
<copy todir="${project.target.dir}">
<fileset dir="${project.conf.dir}">
<include name="**/*.properties" />
<include name="**/*.xml" />
</fileset>
</copy>
</target>
<target name="compileWithIajc" depends="prepare">
<echo message="compile with iajc" />
<iajc destdir="${project.target.dir}" sourceroots="${project.src.dir}" source="1.8" target="1.8" encoding="UTF-8">
<classpath refid="project.classpath" />
</iajc>
</target>
<!-- 运行AspectJ编译时织入功能的代码 -->
<target name="runNormal" depends="compileWithIajc">
<java classname="cn.aofeng.demo.aspectj.BusinessService">
<classpath refid="project.classpath" />
<classpath location="${project.target.dir}" />
</java>
</target>
<target name="compileWithJavac" depends="prepare">
<echo message="compile with javac" />
<javac destdir="${project.target.dir}" srcdir="${project.src.dir}" source="1.8" target="1.8"
encoding="UTF-8" debug="true" includeantruntime="false">
<classpath refid="project.classpath" />
</javac>
</target>
<!-- 载入代码时织入功能LTW -->
<target name="runLTW" depends="compileWithJavac">
<java classname="cn.aofeng.demo.aspectj.BusinessService" fork="true">
<jvmarg value="-Daj.weaving.verbose=true" />
<jvmarg value="-javaagent:${project.lib.dir}/aspectjweaver-1.8.10.jar"/>
<classpath refid="project.classpath" />
<classpath location="${project.target.dir}" />
</java>
</target>
</project>

View File

@@ -0,0 +1,205 @@
package cn.aofeng.demo.dbutils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.log4j.Logger;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
/**
* Apache DbUtils使用示例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class DbUtilsDemo {
private static Logger _logger = Logger.getLogger(DbUtilsDemo.class);
/**
* 创建JDBC连接池。
*
* @param pros 数据库连接信息里面包含4个键值对。
* <pre>
* jdbcDriver => JDBC驱动类名称
* url => 数据库JDBC连接地址
* user => 连接数据库的用户名
* password => 连接数据库的密码
* <pre>
* @return 连接池对象
*/
private DataSource createDataSource(Properties pros) {
MysqlDataSource ds = new MysqlDataSource();
ds.setURL(pros.getProperty("url"));
ds.setUser(pros.getProperty("user"));
ds.setPassword(pros.getProperty("password"));
return ds;
}
/**
* 将查询结果集转换成Bean列表返回。
*
* @param ds JDBC连接池
*/
public void queryBeanList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Student>> handler = new BeanListHandler<Student>(Student.class);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
/**
* 将查询结果转换成Bean返回。
*
* @param ds JDBC连接池
*/
public void queryBean(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Student> handler = new BeanHandler<Student>(Student.class);
Student result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
/**
* 将查询结果集转换成键值对列表返回。
*
* @param ds JDBC连接池
*/
public void queryMapList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Map<String, Object>>> handler = new MapListHandler();
List<Map<String, Object>> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Map<String, Object> map : result) {
System.out.println(map);
}
}
/**
* 将查询结果集转换成键值对列表返回。
*
* @param ds JDBC连接池
* @param userId 用户编号
*/
public void queryMap(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Map<String, Object>> handler = new MapHandler();
Map<String, Object> result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
/**
* 将查询结果集转换成键值对列表返回。
*
* @param ds JDBC连接池
*/
public void queryCustomHandler(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
// 新实现一个ResultSetHandler
ResultSetHandler<List<Student>> handler = new ResultSetHandler<List<Student>>() {
@Override
public List<Student> handle(ResultSet resultset)
throws SQLException {
List<Student> result = new ArrayList<Student>();
while (resultset.next()) {
Student student = new Student();
student.setUserId(resultset.getInt("userId"));
student.setUserName(resultset.getString("userName"));
student.setGender(resultset.getString("gender"));
student.setAge(resultset.getInt("age"));
result.add(student);
}
return result;
}
};
QueryRunner run = new QueryRunner(ds);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
public static void main(String[] args) {
Properties pros = new Properties();
pros.put("jdbcDriver", "com.mysql.jdbc.Driver");
pros.put("url", "jdbc:mysql://192.168.56.102:19816/test?useUnicode=true&characterEncoding=UTF8");
pros.put("user", "uzone");
pros.put("password", "uzone");
DbUtilsDemo demo = new DbUtilsDemo();
DataSource ds = demo.createDataSource(pros);
demo.queryBeanList(ds);
demo.queryBean(ds, 1);
demo.queryBean(ds, 9);
demo.queryMapList(ds);
demo.queryMap(ds, 3);
demo.queryMap(ds, 9);
demo.queryCustomHandler(ds);
}
}

View File

@@ -0,0 +1,226 @@
Apache DbUtils 使用教程
===
用JDBC编程时需要关注和处理的内容非常多而且很容易造成连接资源没有释放导致泄漏的问题。一个普通的查询操作其处理过程如下
1. 创建Connection。
1. 创建Statement。
1. 执行SQL生成ResultSet历遍ResultSet中的所有行记录提取列数据并转换成所需的对象。
1. 关闭ResultSet。
1. 关闭Statement。
1. 关闭Connection。
`Apache DbUtils`对JDBC操作进行了轻量地封装解决了两个问题
1. 自动创建和释放连接资源,不再有泄漏问题。
2. 自动将Result转换成对象。填入不同的`ResultSetHandler`可将ResultSet转换成不同的对象。
使得代码更加简洁和健壮,让你将精力更多地投入到业务处理中。
预备
---
* MySQL数据库
* commons-dbutils-1.5.jar
* mysql-connector-java-5.1.22-bin.jar
创建表和初始化数据
---
1、表结构。
```sql
CREATE TABLE `student` (
`userId` int(11) NOT NULL,
`userName` varchar(30) NOT NULL,
`gender` char(1) NOT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
```
2、初始化数据。
| userId | userName | gender | age |
| :--- | :--- | :--- | :--- |
| 1 | 张三 | M | 20 |
| 2 | 李四 | M | 21 |
| 3 | 王五 | M | 22 |
| 4 | 小明 | M | 6 |
| 5 | 小丽 | F | 9 |
编写查询代码
---
###1、将查询结果转换成POJO对象列表。
1定义一个与表student相对应的对象Student[源代码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/dbutils/Student.java))。
2使用`BeanListHandler`将查询结果转换成POJO对象列表。
```java
public void queryBeanList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Student>> handler = new BeanListHandler<Student>(Student.class);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
```
执行queryBeanList方法控制台输出如下信息
```
Student [userId=1, userName=张三, gender=M, age=20]
Student [userId=2, userName=李四, gender=M, age=21]
Student [userId=3, userName=王五, gender=M, age=22]
Student [userId=4, userName=小明, gender=M, age=6]
Student [userId=5, userName=小丽, gender=F, age=9]
```
###2、将查询结果转换成一个POJO象。
1定义一个与表student相对应的对象Student[源代码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/dbutils/Student.java))。
2使用`BeanHandler`将查询结果转换成一个POJO对象。
```java
public void queryBean(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Student> handler = new BeanHandler<Student>(Student.class);
Student result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
```
执行queryBean方法控制台输出如下信息
```
Student [userId=1, userName=张三, gender=M, age=20]
```
###3、将查询结果转换成Map对象列表。
1使用`MapListHandler`将ResultSet转换成Map对象列表。
```java
public void queryMapList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Map<String, Object>>> handler = new MapListHandler();
List<Map<String, Object>> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Map<String, Object> map : result) {
System.out.println(map);
}
}
```
执行queryMapList方法控制台输出如下信息
```
{age=20, userId=1, gender=M, userName=张三}
{age=21, userId=2, gender=M, userName=李四}
{age=22, userId=3, gender=M, userName=王五}
{age=6, userId=4, gender=M, userName=小明}
{age=9, userId=5, gender=F, userName=小丽}
```
###4、将查询结果转换成一个Map对象。
1使用`MapHandler`将ResultSet转换成一个Map对象。
```java
public void queryMap(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Map<String, Object>> handler = new MapHandler();
Map<String, Object> result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
```
执行queryMap方法控制台输出如下信息
```
{age=22, userId=3, gender=M, userName=王五}
```
###5、自定义ResultSetHandler。
`Apache DbUtils`自带的各种`ResultSetHandler`已经可以满足绝大部分的查询需求,如果有一些特殊的要求满足不了,可以自己实现一个。
```java
public void queryCustomHandler(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
// 新实现一个ResultSetHandler
ResultSetHandler<List<Student>> handler = new ResultSetHandler<List<Student>>() {
@Override
public List<Student> handle(ResultSet resultset)
throws SQLException {
List<Student> result = new ArrayList<Student>();
while (resultset.next()) {
Student student = new Student();
student.setUserId(resultset.getInt("userId"));
student.setUserName(resultset.getString("userName"));
student.setGender(resultset.getString("gender"));
student.setAge(resultset.getInt("age"));
result.add(student);
}
return result;
}
};
QueryRunner run = new QueryRunner(ds);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
```
执行queryCustomHandler方法控制台输出如下信息
```
Student [userId=1, userName=张三, gender=M, age=20]
Student [userId=2, userName=李四, gender=M, age=21]
Student [userId=3, userName=王五, gender=M, age=22]
Student [userId=4, userName=小明, gender=M, age=6]
Student [userId=5, userName=小丽, gender=F, age=9]
```
附录
---
* [DEMO完整源代码](https://github.com/aofeng/JavaDemo/edit/master/src/cn/aofeng/demo/dbutils)
参考资料
---
* [commons-dbutils example](http://commons.apache.org/proper/commons-dbutils/examples.html)

View File

@@ -0,0 +1,86 @@
package cn.aofeng.demo.dbutils;
/**
* 表student的POJO对象。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Student {
private Integer userId;
private String userName;
private String gender;
private Integer age;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Student)) {
return false;
}
Student other = (Student) obj;
if (age == null) {
if (other.age != null) {
return false;
}
} else if (!age.equals(other.age)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Student [userId=" + userId + ", userName=" + userName
+ ", gender=" + gender + ", age=" + age + "]";
}
}

View File

@@ -0,0 +1,258 @@
用EasyMock生成模拟对象进行单元测试 Testing with EasyMock
===
当项目达到一定规模时会根据业务或功能进行模块化,这时研发团队在协作时会碰到一个问题:如何让相互依赖的各模块并行开发?常用的办法是:各模块之间有清晰的边界,模块之间的交互通过接口。在设计阶段定义模块的接口,然后各个模块开发时遵循接口的定义进行实现和调用。
OK并行开发的问题解决了。但不同的模块因其复杂度和工作量不同进度不一致。当研发同学完成所负责的模块时但依赖的模块还没有完成开发不好进行测试。
这里就讲如何用EasyMock生成Mock Object模拟所依赖模块的接口来完成单元测试。
![EasyMock](http://img1.ph.126.net/wErY4T7Ne-KeyB66As_PLA==/6608670713840748025.gif)
预备
---
* easymock-3.2.jar
* easymockclassextension-3.2.jar
* cglib-2.2.2.jar
* objenesis-1.2.jar
* asm-3.1.jar
* asm-commons-3.1.jar
* asm-util-3.1.jar
* gson-2.2.4.jar // *用于处理JSON*
上面的jar文件均可从[MAVEN仓库](http://mvnrepository.com)下载。
使用EasyMock的五部曲
---
1、引入EasyMock。
```java
import static org.easymock.EasyMock.*;
```
2、创建Mock Object。
```java
mock = createMock(InterfaceOrClass.class);
```
3、设置Mock Object的行为和预期结果。
```java
mock.doSomeThing();
expectLastCall().times(1); // 至少要调用一次doSomeThing方法
```
或者
```java
// 模拟抛出异常
expect(mock.getSomeThing(anyString()))
.andThrow(new IOException("单元测试特意抛的异常"));
// 模拟返回指定的数据
expect(mock.getSomeThing(anyString()))
.andReturn("abcd");
```
4、设置Mock Object变成可用状态。
```java
replay(mock);
```
5、运行单元测试代码会调用到Mock Object的方法
```java
// 如果是校验调用次数就要用到verify方法
verify(mock);
```
实践
---
### 业务代码
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/easymock/UserService.java)
```java
package cn.aofeng.demo.easymock;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import cn.aofeng.demo.jetty.HttpGet;
/**
* 用户相关服务。如:获取用户昵称。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserService {
private static Logger _logger = Logger.getLogger(UserService.class);
private HttpGet _httpGet = new HttpGet();
/**
* 根据用户的账号ID获取昵称。
*
* @param accountId 用户的账号ID
* @return 如果账号ID有效且请求成功返回昵称否则返回默认的昵称"用户xxx"。
*/
public String getNickname(String accountId) {
String targetUrl = "http://192.168.56.102:8080/user?method=getNickname&accountId="+accountId;
String response = null;
try {
response = _httpGet.getSomeThing(targetUrl);
} catch (IOException e) {
_logger.error("获取用户昵称时出错,账号ID:"+accountId, e);
}
if (null != response) {
// 响应数据结构示例:{"nickname":"张三"}
Type type = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> data = new Gson().fromJson(response, type);
if (null != data && data.containsKey("nickname")) {
return data.get("nickname");
}
}
return "用户"+accountId;
}
protected void setHttpGet(HttpGet httpGet) {
this._httpGet = httpGet;
}
}
```
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpGet.java)
```java
package cn.aofeng.demo.jetty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* HTTP GET请求。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGet {
public String getSomeThing(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(3000);
urlConn.setRequestMethod("GET");
urlConn.connect();
InputStream ins = null;
try {
if (200 == urlConn.getResponseCode()) {
ins = urlConn.getInputStream();
ByteArrayOutputStream outs = new ByteArrayOutputStream(1024);
IOUtils.copy(ins, outs);
return outs.toString("UTF-8");
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
}
```
### 单元测试代码
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/easymock/UserServiceTest.java)
```java
package cn.aofeng.demo.easymock;
import static org.junit.Assert.*;
import java.io.IOException;
import static org.easymock.EasyMock.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import cn.aofeng.demo.jetty.HttpGet;
/**
* {@link UserService}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserServiceTest {
private HttpGet _mock = createMock(HttpGet.class);
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
reset(_mock);
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 网络请求时发生IO异常
* </pre>
*
* 测试结果:
* <pre>
* 返回默认的用户昵称"用户xxx"
* </pre>
*/
@Test
public void testGetNickname4OccursIOError() throws IOException {
// 设置Mock
expect(_mock.getSomeThing(anyString()))
.andThrow(new IOException("单元测试特意抛的异常"));
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验mock
assertEquals("用户123456", nickname); // 检查返回值
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 1、网络请求成功。
* 2、响应状态码为200且响应内容符合接口定义{\"nickname\":\"张三\"})。
* </pre>
*
* 测试结果:
* <pre>
* 返回"张三"
* </pre>
*/
@Test
public void testGetNickname4Success() throws IOException {
// 设置Mock
_mock.getSomeThing(anyString());
expectLastCall().andReturn("{\"nickname\":\"张三\"}");
expectLastCall().times(1);
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验方法的调用次数
assertEquals("张三", nickname); // 校验返回值
}
}
```

View File

@@ -0,0 +1,56 @@
package cn.aofeng.demo.easymock;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import cn.aofeng.demo.jetty.HttpGet;
/**
* 用户相关服务。如:获取用户昵称。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserService {
private static Logger _logger = Logger.getLogger(UserService.class);
private HttpGet _httpGet = new HttpGet();
/**
* 根据用户的账号ID获取昵称。
*
* @param accountId 用户的账号ID
* @return 如果账号ID有效且请求成功返回昵称否则返回默认的昵称"用户xxx"。
*/
public String getNickname(String accountId) {
String targetUrl = "http://192.168.56.102:8080/user?method=getNickname&accountId="+accountId;
String response = null;
try {
response = _httpGet.getSomeThing(targetUrl);
} catch (IOException e) {
_logger.error("获取用户昵称时出错,账号ID:"+accountId, e);
}
if (null != response) {
// 响应数据结构示例:{"nickname":"张三"}
Type type = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> data = new Gson().fromJson(response, type);
if (null != data && data.containsKey("nickname")) {
return data.get("nickname");
}
}
return "用户"+accountId;
}
protected void setHttpGet(HttpGet httpGet) {
this._httpGet = httpGet;
}
}

View File

@@ -0,0 +1,87 @@
package cn.aofeng.demo.easymock;
import static org.junit.Assert.*;
import java.io.IOException;
import static org.easymock.EasyMock.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import cn.aofeng.demo.jetty.HttpGet;
/**
* {@link UserService}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserServiceTest {
private HttpGet _mock = createMock(HttpGet.class);
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
reset(_mock);
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 网络请求时发生IO异常
* </pre>
*
* 测试结果:
* <pre>
* 返回默认的用户昵称"用户xxx"
* </pre>
*/
@Test
public void testGetNickname4OccursIOError() throws IOException {
// 设置Mock
expect(_mock.getSomeThing(anyString()))
.andThrow(new IOException("单元测试特意抛的异常"));
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验mock
assertEquals("用户123456", nickname); // 检查返回值
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 1、网络请求成功。
* 2、响应状态码为200且响应内容符合接口定义{\"nickname\":\"张三\"})。
* </pre>
*
* 测试结果:
* <pre>
* 返回"张三"
* </pre>
*/
@Test
public void testGetNickname4Success() throws IOException {
// 设置Mock
_mock.getSomeThing(anyString());
expectLastCall().andReturn("{\"nickname\":\"张三\"}");
expectLastCall().times(1);
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验方法的调用次数
assertEquals("张三", nickname); // 校验返回值
}
}

View File

@@ -0,0 +1,54 @@
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.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.apache.commons.codec.binary.Base64;
/**
* AES加密与解密。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class AES extends EncryptAndDecrypt {
public final String encryptType = "AES/CBC/PKCS5Padding";
public final String algorithmParam = "abcdefgh12345678";
public final String key = "abcdefgh_1234567";
public void execute(String data) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
SecretKey secretKey = createSecretKey("AES", key);
byte[] secretData = encrypt(encryptType, secretKey, data,
algorithmParam);
log("使用%s加密后的数据", encryptType);
log(Base64.encodeBase64String(secretData));
String srcStr = decrypt(encryptType, secretKey, secretData,
algorithmParam);
log("解密后的数据:\n%s", srcStr);
}
public static void main(String[] args) throws UnsupportedEncodingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
String data = "炎黄汉字english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
log("待加密的数据:\n%s", data);
AES aes = new AES();
aes.execute(data);
}
}

View File

@@ -0,0 +1,51 @@
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.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.apache.commons.codec.binary.Base64;
/**
* Blowfish加密解密。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Blowfish extends EncryptAndDecrypt {
public final String encryptType = "Blowfish";
public final String key = "abcdefgh_1234567";
public void execute(String data) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
SecretKey secretKey = createSecretKey(encryptType, key);
byte[] secretData = encrypt(encryptType, secretKey, data);
log("使用%s加密后的数据", encryptType);
log(Base64.encodeBase64String(secretData));
String srcStr = decrypt(encryptType, secretKey, secretData);
log("解密后的数据:\n%s", srcStr);
}
public static void main(String[] args) throws UnsupportedEncodingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
String data = "炎黄汉字english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
log("待加密的数据:\n%s", data);
Blowfish bf = new Blowfish();
bf.execute(data);
}
}

View File

@@ -0,0 +1,125 @@
package cn.aofeng.demo.encrypt;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* 加密与解密。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class EncryptAndDecrypt {
public final static String CHARSET = "utf8";
/**
* 创建安全密钥。
*
* @param encryptType
* 加密方式AESBlowfish。详情查看<a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* @param keyStr
* 密钥明文
* @return 安全密钥
* @throws UnsupportedEncodingException
* 不支持指定的字符集编码
*/
public SecretKey createSecretKey(String encryptType, String keyStr)
throws UnsupportedEncodingException {
byte[] secretKeyData = keyStr.getBytes(CHARSET);
SecretKeySpec sks = new SecretKeySpec(secretKeyData, encryptType);
return sks;
}
/**
* 加密数据。
*
* @param encryptType 加密方式AESBlowfish。详情查看<a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* @param secretKey 密钥
* @param srcData 待加密的源数据
* @return 加密后的二进制数据(字节数组)
* @see #encrypt(String, SecretKey, String, String)
*/
public byte[] encrypt(String encryptType, SecretKey secretKey,
String srcData) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
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,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
Cipher encrpyt = Cipher.getInstance(encryptType);
if (null == algorithmParam) {
encrpyt.init(Cipher.ENCRYPT_MODE, secretKey);
} else {
IvParameterSpec iv = new IvParameterSpec(
algorithmParam.getBytes(CHARSET));
encrpyt.init(Cipher.ENCRYPT_MODE, secretKey, iv);
}
byte[] secretData = encrpyt.doFinal(srcData.getBytes(CHARSET));
return secretData;
}
/**
* 解密数据。
*
* @param decryptType 解密方式AESBlowfish。详情查看<a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* @param secretKey 密钥
* @param secretData 待解密的数据
* @return 解密后的数据
* @see #decrypt(String, SecretKey, byte[], String)
*/
public String decrypt(String decryptType, SecretKey secretKey,
byte[] secretData) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
return decrypt(decryptType, secretKey, secretData, null);
}
public String decrypt(String decryptType, SecretKey secretKey,
byte[] secretData, String algorithmParam)
throws InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Cipher decrypt = Cipher.getInstance(decryptType);
if (null == algorithmParam) {
decrypt.init(Cipher.DECRYPT_MODE, secretKey);
} else {
IvParameterSpec iv = new IvParameterSpec(
algorithmParam.getBytes(CHARSET));
decrypt.init(Cipher.DECRYPT_MODE, secretKey, iv);
}
byte[] srcData = decrypt.doFinal(secretData);
String srcStr = new String(srcData, CHARSET);
return srcStr;
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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);
}
}

View File

@@ -0,0 +1,70 @@
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.NoSuchPaddingException;
import javax.crypto.SecretKey;
/**
* 加密解密性能比较。
*
* @author <a href="mailto:nieyong@ucweb.com">聂勇</a>
*/
public class PerformanceCompare extends EncryptAndDecrypt {
public void blowfishPerformence(String data)
throws UnsupportedEncodingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Blowfish bf = new Blowfish();
SecretKey secretKey = createSecretKey(bf.encryptType, bf.key);
long startTime = System.currentTimeMillis();
for (int j = 0; j < 100000; j++) {
bf.encrypt(bf.encryptType, secretKey, data+j);
}
long endTime = System.currentTimeMillis();
long usedTime = endTime - startTime;
log("使用%s进行%d次加密消耗时间%d毫秒", bf.encryptType, 100000, usedTime);
}
public void aesPerformence(String data)
throws UnsupportedEncodingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
AES aes = new AES();
SecretKey secretKey = createSecretKey("AES", aes.key);
long startTime = System.currentTimeMillis();
for (int j = 0; j < 100000; j++) {
aes.encrypt(aes.encryptType, secretKey, data+j,
aes.algorithmParam);
}
long endTime = System.currentTimeMillis();
long usedTime = endTime - startTime;
log("使用%s进行%d次加密消耗时间%d毫秒", aes.encryptType, 100000, usedTime);
}
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);
PerformanceCompare pc = new PerformanceCompare();
// AES
pc.aesPerformence(data);
// Blowfish
pc.blowfishPerformence(data);
}
}

View File

@@ -0,0 +1,38 @@
package cn.aofeng.demo.eventdriver_improve;
/**
* 事件驱动调用示例
* @author 聂勇 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a>
*/
public class ClientMain {
public static void main(String[] args) {
EventManagement eventManagement = new EventManagement();
eventManagement.addListener("read", new HelloWorldListener());
eventManagement.addListener("write", new SimpleListener());
EventSource eventSource = new EventSource(eventManagement);
eventSource.fire(new Event("read", "this is a read event"));
eventSource.fire(new Event("write", "this is a write event"));
}
public static class HelloWorldListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件类型是:"
+ event.getEventType() + ", 事件附带的数据:" + event.getData());
}
}
public static class SimpleListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件类型是:"
+ event.getEventType() + ", 事件附带的数据:" + event.getData());
}
}
}

View File

@@ -0,0 +1,28 @@
package cn.aofeng.demo.eventdriver_improve;
/**
* 事件
*
* @author aofeng <aofengblog@163.com>
*/
public class Event {
// 事件附带的数据
private Object data;
// 事件类型
private String eventType;
public Event(String eventType, Object obj){
this.eventType = eventType;
this.data = obj;
}
public Object getData() {
return this.data;
}
public String getEventType() {
return this.eventType;
}
}

View File

@@ -0,0 +1,16 @@
package cn.aofeng.demo.eventdriver_improve;
/**
* 事件监听器(监听一个或多个事件并进行具体的处理)
*
* @author aofeng <aofengblog@163.com>
*/
public interface EventListener {
/**
* 处理事件
*
* @param event 事件
*/
public void execute(Event event);
}

View File

@@ -0,0 +1,64 @@
package cn.aofeng.demo.eventdriver_improve;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 事件管理器。负责
*
* @author aofeng <aofengblog@163.com>
*/
public class EventManagement {
private Map<String, List<EventListener>> map = new HashMap<String, List<EventListener>>();
public EventManagement(){
}
/**
* 向指定事件添加一个监听器
*
* @param eventType 事件类型
* @param listener 事件监听器
* @return 添加成功返回true添加失败返回false
*/
public boolean addListener(String eventType, EventListener listener){
List<EventListener> listeners = map.get(eventType);
if (null == listeners) {
listeners = new ArrayList<EventListener>();
}
boolean result = listeners.add(listener);
map.put(eventType, listeners);
return result;
}
/**
* 移除事件的某一个监听器
*
* @param eventType 事件类型
* @param listener 事件监听器
* @return 移除成功返回true移除失败返回false
*/
public boolean removeListener(String eventType, EventListener listener){
List<EventListener> listeners = map.get(eventType);
if (null != listeners) {
return listeners.remove(listener);
}
return false;
}
/**
* 获取指定事件的监听器
*
* @param eventType 事件类型
* @return 如果指定的事件没有监听器返回null否则返回监听器列表
*/
public List<EventListener> getEventListeners(String eventType) {
return map.get(eventType);
}
}

View File

@@ -0,0 +1,38 @@
package cn.aofeng.demo.eventdriver_improve;
import java.util.List;
/**
* 事件源(事件发送者)
*
* @author aofeng <aofengblog@163.com>
*/
public class EventSource {
// 事件管理器
private EventManagement eventManagement;;
public EventSource(EventManagement eventManagement){
this.eventManagement = eventManagement;
}
/**
* 派发事件
*
* @param data 事件
*/
public void fire(Event event) {
if (null == event) {
return;
}
List<EventListener> listeners = eventManagement.getEventListeners(event.getEventType());
if (null == listeners) {
return;
}
for (EventListener listener : listeners) {
listener.execute(event);
}
}
}

View File

@@ -0,0 +1,33 @@
package cn.aofeng.demo.eventdriver_normal;
/**
* 事件驱动调用示例
* @author aofeng <aofengblog@163.com>
*/
public class ClientMain {
public static void main(String[] args) {
EventSource eventSource = new EventSource();
eventSource.addListener(new HelloWorldListener());
eventSource.addListener(new SimpleListener());
eventSource.fire("hello, world!");
}
public static class HelloWorldListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件附带的数据:" + event.getData());
}
}
public static class SimpleListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件附带的数据:" + event.getData());
}
}
}

View File

@@ -0,0 +1,17 @@
package cn.aofeng.demo.eventdriver_normal;
/**
* 事件
* @author aofeng <aofengblog@163.com>
*/
public class Event {
private Object data;
public Event(Object obj){
this.data = obj;
}
public Object getData() {
return data;
}
}

View File

@@ -0,0 +1,16 @@
package cn.aofeng.demo.eventdriver_normal;
/**
* 事件监听器(监听一个或多个事件并进行具体的处理)
*
* @author aofeng <aofengblog@163.com>
*/
public interface EventListener {
/**
* 处理事件
*
* @param event 事件
*/
public void execute(Event event);
}

View File

@@ -0,0 +1,47 @@
package cn.aofeng.demo.eventdriver_normal;
import java.util.ArrayList;
import java.util.List;
/**
* 事件源(事件发送者)
*
* @author aofeng <aofengblog@163.com>
*/
public class EventSource {
private List<EventListener> listeners = new ArrayList<EventListener>();
public EventSource() {
}
/**
* 添加事件监听器
*
* @param listener 事件监听器
*/
public boolean addListener(EventListener listener) {
return listeners.add(listener);
}
/**
* 移除事件监听器
*
* @param listener 移除事件监听器
*/
public boolean removeListener(EventListener listener) {
return listeners.remove(listener);
}
/**
* 派发事件
*
* @param data 事件
*/
public void fire(Object data) {
for (EventListener listener : listeners) {
listener.execute(new Event(data));
}
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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<NameValuePair> form = new ArrayList<NameValuePair>();
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);
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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<NameValuePair> params = new ArrayList<NameValuePair>();
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");
}
}

View File

@@ -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)

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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<Entry<String, List<String>>> headerSet = headers.entrySet();
_logger.info("请求头:");
for (Entry<String, List<String>> 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);
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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);
}
}
}

View File

@@ -0,0 +1,53 @@
package cn.aofeng.demo.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 多线程网络echo服务。每接收到一个新连接都新建一个线程处理连接关闭后线程随之销毁。
*
* @author <a href="mailto:aofengblog@163.com">NieYong</a>
*/
public class MultiThreadEchoServer {
private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
/**
* @param args [0]-监听端口
*/
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("无效的参数。使用示例:");
System.err.println(" java cn.aofeng.demo.io.MultiThreadEchoServer 9090");
System.exit(-1);
}
int port = Integer.parseInt(args[0]);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
if (logger.isLoggable(Level.INFO)) {
logger.info("多线程网络echo服务启动完毕监听端口" +port);
}
while (true) {
// 接收新的客户端连接
Socket socket = serverSocket.accept();
if (logger.isLoggable(Level.INFO)) {
logger.info("收到一个新的连接客户端IP"+socket.getInetAddress().getHostAddress()+"客户端Port"+socket.getPort());
}
// 新建一个线程处理Socket连接
Thread thread = new Thread(new Worker(socket));
thread.start();
}
} catch (IOException e) {
logger.log(Level.SEVERE, "处理网络连接出错", e);
}
}
}

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 线程池网络echo服务。每接收到一个新连接都由线程池中的空闲线程处理连接关闭后释放线程不会销毁线程仍在线程池中
*
* @author <a href="mailto:aofengblog@163.com">NieYong</a>
*/
public class ThreadPoolEchoServer {
private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
/**
* @param args [0]-监听端口
*/
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("无效的参数。使用示例:");
System.err.println(" java cn.aofeng.demo.io.ThreadPoolEchoServer 9090");
System.exit(-1);
}
int port = Integer.parseInt(args[0]);
ExecutorService threadpool = Executors.newFixedThreadPool(5);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
if (logger.isLoggable(Level.INFO)) {
logger.info("线程池网络echo服务启动完毕监听端口" +port);
}
while (true) {
// 接收新的客户端连接
Socket socket = serverSocket.accept();
if (logger.isLoggable(Level.INFO)) {
logger.info("收到一个新的连接客户端IP"+socket.getInetAddress().getHostAddress()+"客户端Port"+socket.getPort());
}
// 将Socket连接交给线程池处理
threadpool.submit(new Worker(socket));
}
} catch (IOException e) {
logger.log(Level.SEVERE, "处理网络连接出错", e);
}
}
}

View File

@@ -0,0 +1,77 @@
package cn.aofeng.demo.io;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 处理客户端Socket连接的工作线程。
*
* @author 聂勇 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a>
*/
public class Worker implements Runnable {
private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
// 字符集编码
private final static String CHAR_SET = "utf8";
// 行结束符
private final static String CRLF = "\r\n";
private Socket socket = null;
public Worker(Socket socket) {
this.socket = socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public void close(Closeable c) {
if (null != c) {
try {
c.close();
} catch (IOException e) {
// ingore
}
}
}
@Override
public void run() {
if (null == socket || socket.isClosed()) {
logger.warning("无效的Socket连接" + socket);
return;
}
String lineEnd = CRLF;
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
OutputStream outs = socket.getOutputStream();
String line = null;
while ( null != (line = reader.readLine()) ) {
// 客户端退出
if ("quit".equalsIgnoreCase(line) || "exit".equalsIgnoreCase(line)) {
break;
}
outs.write(line.getBytes(CHAR_SET));
outs.write(lineEnd.getBytes(CHAR_SET));
}
close(reader);
close(outs);
} catch (IOException e) {
logger.log(Level.SEVERE, "读取网络连接数据出错", e);
}
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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());
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}
}

View File

@@ -0,0 +1,14 @@
package cn.aofeng.demo.java.lang.instrument;
/**
* Instrumentation启动类。 *
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Hello {
public static void main(String[] args) {
// nothing
}
}

View File

@@ -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

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Instrumentation" default="run" basedir="../../../../../../../">
<property name="project.src.dir" value="${basedir}/src" />
<property name="project.lib.dir" value="${basedir}/lib" />
<property name="project.conf.dir" value="${basedir}/conf" />
<property name="project.target.dir" value="${basedir}/classes" />
<property name="project.dist.dir" value="${basedir}/dist" />
<path id="project.classpath">
<fileset dir="${project.lib.dir}">
<include name="*.jar" />
</fileset>
</path>
<target name="prepare">
<delete dir="${project.target.dir}" />
<mkdir dir="${project.target.dir}"/>
<copy todir="${project.target.dir}">
<fileset dir="${project.conf.dir}">
<include name="**/*.properties" />
<include name="**/*.xml" />
</fileset>
</copy>
<delete dir="${project.dist.dir}" />
<mkdir dir="${project.dist.dir}" />
</target>
<target name="compileWithJavac" depends="prepare">
<echo message="compile with javac" />
<javac destdir="${project.target.dir}" srcdir="${project.src.dir}" source="1.8" target="1.8"
encoding="UTF-8" debug="true" includeantruntime="false">
<classpath refid="project.classpath" />
</javac>
</target>
<target name="package" depends="compileWithJavac">
<jar destfile="${project.dist.dir}/hello.jar">
<fileset dir="${project.target.dir}">
<include name="cn/aofeng/demo/java/lang/instrument/*.class" />
</fileset>
<manifest>
<attribute name="Premain-Class" value="cn.aofeng.demo.java.lang.instrument.FirstInstrumentation" />
</manifest>
</jar>
</target>
<target name="run" depends="package">
<java classname="cn.aofeng.demo.java.lang.instrument.Hello" fork="true">
<classpath refid="project.classpath" />
<classpath location="${project.target.dir}" />
<jvmarg value="-javaagent:${project.dist.dir}/hello.jar=&quot;Hello, Instrumentation&quot;" />
</java>
</target>
</project>

View File

@@ -0,0 +1,143 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* 通过反射获取类的构造方法、字段、方法和注解等信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ClassAnalyze {
final static String PREFIX = "==========";
final static String SUFFIX = PREFIX;
private static void parseClass(Class<?> claz) {
// 注解
parseAnnotation(claz);
// 类
StringBuilder buffer = new StringBuilder(32)
.append( Modifier.toString(claz.getModifiers()) )
.append(' ')
.append(claz.getName());
log(buffer.toString());
}
private static void parseConstructor(Constructor<?> c) {
// 注解
parseAnnotation(c);
// 构造方法
StringBuilder buffer = new StringBuilder(32)
.append( parseMember(c) )
.append('(');
// 参数
Class<?>[] params = c.getParameterTypes();
for (int index = 0; index < params.length; index++) {
buffer.append(params[index].getName());
if (index!=params.length-1) {
buffer.append(", ");
}
}
buffer.append(')');
log(buffer.toString());
}
private static void parseMethod(Method method) {
// 注解
parseAnnotation(method);
// 方法
StringBuilder buffer = new StringBuilder(32)
.append( parseMember(method) )
.append('(');
// 参数
Class<?>[] params = method.getParameterTypes();
for (int index = 0; index < params.length; index++) {
buffer.append(params[index].getName());
if (index!=params.length-1) {
buffer.append(", ");
}
}
buffer.append(')');
log(buffer.toString());
}
private static void parseField(Field field) {
// 注解
parseAnnotation(field);
// 字段
StringBuilder buffer = parseMember(field);
log(buffer.toString());
}
/**
* 解析方法、字段或构造方法的信息。
* @param member 方法、字段或构造方法
* @return 修饰符和名称组成的字符串。
*/
private static StringBuilder parseMember(Member member) {
StringBuilder buffer = new StringBuilder()
.append(Modifier.toString(member.getModifiers()))
.append(' ')
.append(member.getName());
return buffer;
}
/**
* 解析注解信息。
*/
private static void parseAnnotation(AnnotatedElement ae) {
Annotation[] ans = ae.getDeclaredAnnotations();
for (Annotation annotation : ans) {
log(annotation.toString());
}
}
public static void log(String msg, Object... param) {
System.out.println( String.format(msg, param) );
}
public static void main(String[] args) throws ClassNotFoundException {
if (args.length != 1) {
log("无效的输入参数!");
log("示例:");
log("java cn.aofeng.demo.java.lang.reflect.ClassAnalyze java.util.HashMap");
}
Class<?> claz = Class.forName(args[0]);
log("%s类%s", PREFIX, SUFFIX);
parseClass(claz);
log("%s构造方法%s", PREFIX, SUFFIX);
Constructor<?>[] cs = claz.getDeclaredConstructors();
for (Constructor<?> constructor : cs) {
parseConstructor(constructor);
}
log("%s字段%s", PREFIX, SUFFIX);
Field[] fields = claz.getDeclaredFields();
for (Field field : fields) {
parseField(field);
}
log("%s方法%s", PREFIX, SUFFIX);
Method[] methods = claz.getDeclaredMethods();
for (Method method : methods) {
parseMethod(method);
}
}
}

View File

@@ -0,0 +1,36 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import cn.aofeng.demo.util.LogUtil;
/**
* 通过反射使用构造方法创建对象实例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CreateInstance {
public static void main(String[] args) throws InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException,
IllegalArgumentException, InvocationTargetException {
Class<Man> claz = Man.class;
// 调用默认的public构造方法
Man man = claz.newInstance();
LogUtil.log(man.toString());
// 调用带参数的protected构造方法
Constructor<Man> manC = claz.getDeclaredConstructor(String.class);
man = manC.newInstance("aofeng");
LogUtil.log(man.toString());
// 调用带参数的private构造方法
manC = claz.getDeclaredConstructor(String.class, int.class);
manC.setAccessible(true);
man = manC.newInstance("NieYong", 32);
LogUtil.log(man.toString());
}
}

View File

@@ -0,0 +1,37 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.reflect.Field;
import static cn.aofeng.demo.util.LogUtil.log;
/**
* 通过反射设置字段。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class InvokeField {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, IllegalArgumentException, IllegalAccessException {
Man man = new Man();
Class<?> claz = man.getClass();
log("==========设置public字段的值==========");
log("height的值:%d", man.height);
Field field = claz.getField("height");
field.setInt(man, 175);
log("height的值:%d", man.height);
log("==========设置private字段的值==========");
log("power的值:%d", man.getPower());
field = claz.getDeclaredField("power");
field.setAccessible(true);
field.setInt(man, 100);
log("power的值:%d", man.getPower());
log("==========获取private字段的值==========");
int power = field.getInt(man);
log("power的值:%d", power);
}
}

View File

@@ -0,0 +1,84 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static cn.aofeng.demo.util.LogUtil.log;
/**
* 通过反射调用方法。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class InvokeMethod {
/**
* 调用父类的方法。
*
* @param claz
* 类
* @param man
* 类对应的实例
*/
private static void invokeParentMethod(Class<Man> claz, Man man)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
// 调用父类的public方法
Method method = claz.getMethod("setName", String.class);
method.invoke(man, "NieYong");
log(man.toString());
method = claz.getMethod("getName");
Object result = method.invoke(man);
log("name:%s", result);
// 调用父类的private方法
method = claz.getSuperclass().getDeclaredMethod("reset");
method.setAccessible(true);
result = method.invoke(man);
log(man.toString());
}
/**
* 调用自身的方法。
*
* @param claz
* 类
* @param man
* 类对应的实例
*/
private static void invokeSelfMethod(Class<Man> claz, Man man)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
man.setName("XiaoMing");
// 调用自身的private方法
Method method = claz.getDeclaredMethod("setPower", int.class);
method.setAccessible(true);
method.invoke(man, 99);
log("power:%d", man.getPower());
// 调用自身的public方法
log("%s is marry:%s", man.getName(), (man.isMarry() ? "Yes" : "No"));
method = claz.getDeclaredMethod("setMarry", boolean.class);
method.invoke(man, true);
log("%s is marry:%s", man.getName(), (man.isMarry() ? "Yes" : "No"));
// 调用静态方法可将实例设置为null因为静态方法属于类
Man a = new Man("张三");
Man b = new Man("李四");
method = claz.getMethod("fight", Man.class, Man.class);
method.invoke(null, a, b); //
}
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
InstantiationException {
Class<Man> claz = Man.class;
Man man = claz.newInstance();
invokeParentMethod(claz, man);
invokeSelfMethod(claz, man);
}
}

View File

@@ -0,0 +1,63 @@
package cn.aofeng.demo.java.lang.reflect;
import cn.aofeng.demo.json.gson.Person;
import cn.aofeng.demo.util.LogUtil;
/**
* 男人。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Man extends Person {
private boolean marry;
private int power;
public int height;
public Man() {
super();
LogUtil.log("%s的默认构造方法被调用", Man.class.getName());
}
protected Man(String name) {
super(name, 0);
LogUtil.log("%s带name参数的构造方法被调用", Man.class.getName());
}
@SuppressWarnings("unused")
private Man(String name, int age) {
super(name, age);
LogUtil.log("%s带name和age参数的构造方法被调用", Man.class.getName());
}
public static void fight(Man a, Man b) {
String win = "unkown";
if (a.power > b.power) {
win = a.getName();
} else if (b.power > a.power) {
win = a.getName();
}
LogUtil.log("%s vs %s, fight result:%s", a.getName(), b.getName(), win);
}
public boolean isMarry() {
return marry;
}
public void setMarry(boolean marry) {
this.marry = marry;
}
public int getPower() {
return power;
}
@SuppressWarnings("unused")
private void setPower(int power) {
this.power = power;
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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();
}
}

View File

@@ -0,0 +1,79 @@
package cn.aofeng.demo.java.lang.serialization;
import java.io.Serializable;
/**
* 默认序列化和
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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> 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);
}
}

View File

@@ -0,0 +1,20 @@
package cn.aofeng.demo.java.rmi;
/**
* 性别。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Gender {
/**
* 男。
*/
public final static char MALE = 'M';
/**
* 女。
*/
public final static char FEMALE = 'F';
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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
* <ul>
* <li>[0]待连接的RMI主机。如rmi://192.168.56.102:9999</li>
* <li>[1]服务名称。如UserService</li>
* </ul>
*/
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);
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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);
}
}

View File

@@ -0,0 +1,170 @@
package cn.aofeng.demo.java.rmi;
import java.io.Serializable;
/**
* 用户信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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();
}
}

View File

@@ -0,0 +1,19 @@
package cn.aofeng.demo.java.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 用户信息服务。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}
}

View File

@@ -0,0 +1,78 @@
package cn.aofeng.demo.java.util.concurret;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import cn.aofeng.demo.util.DateUtil;
/**
* {@link ScheduledExecutorService}的使用示例:<br>
* 定时任务1执行过程中会抛出异常。<br>
* 定时任务2执行过程中不会抛出异常。<br>
* <br>
* 目的检测java.util.concurrent.ScheduledExecutorService在执行定时任务的过程中任务内抛出异常没有捕捉时在下一次执行时间到来时是否可以正常执行。<br>
* 测试的JDK版本1.6.xx。<br>
* 结果通过相比java.util.Timer完善地解决了定时任务抛异常的问题。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ScheduledExecutorServiceDemo {
public static void main(String[] args) {
ScheduledExecutorService timer = Executors.newScheduledThreadPool(2);
long delay = computeDelay();
timer.schedule(new ThrowExceptionTask(timer), delay, TimeUnit.MILLISECONDS);
timer.schedule(new NotThrowExceptionTask(timer), delay, TimeUnit.MILLISECONDS);
System.out.println("主线程的功能执行完毕");
}
private static long computeDelay() {
Date now = new Date();
Date nextMinute = DateUtil.getNextMinute();
long delay = nextMinute.getTime() - now.getTime();
return delay;
}
static class ThrowExceptionTask implements Runnable {
private ScheduledExecutorService _timer;
public ThrowExceptionTask(ScheduledExecutorService timer) {
this._timer = timer;
}
@Override
public void run() {
try {
System.out.println("任务名:ThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
throw new IllegalArgumentException();
} finally {
_timer.schedule(new ThrowExceptionTask(_timer), computeDelay(), TimeUnit.MILLISECONDS);
}
}
} // end of ThrowExceptionTask
static class NotThrowExceptionTask implements Runnable {
private ScheduledExecutorService _timer;
public NotThrowExceptionTask(ScheduledExecutorService timer) {
this._timer = timer;
}
@Override
public void run() {
try {
System.out.println("任务名:NotThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
} finally {
_timer.schedule(new NotThrowExceptionTask(_timer), computeDelay(), TimeUnit.MILLISECONDS);
}
}
} // end of NotThrowExceptionTask
}

View File

@@ -0,0 +1,103 @@
package cn.aofeng.demo.java.util.forkjoin;
import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
/**
* Fork/Join练习。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HelloForkJoin extends RecursiveTask<Long> {
private static final long serialVersionUID = -2386438994963147457L;
private int[] _intArray;
private int _threshold = 10;
private long _result = 0;
public HelloForkJoin(int[] intArray) {
this._intArray = intArray;
}
@Override
protected Long compute() {
if (null == _intArray || _intArray.length <= 0) {
return 0L;
}
if (_intArray.length <= _threshold) { // 如果数组长度小于等于指定的值,执行累加操作
for (int item : _intArray) {
_result += item;
}
System.out.println( String.format("线程:%s累加数组%s中的值结果%d", Thread.currentThread(), Arrays.toString(_intArray), _result) );
} else { // 如果数组长度大于指定的值,做任务分解
int[] temp = new int[_threshold];
System.arraycopy(_intArray, 0, temp, 0, _threshold);
HelloForkJoin subTask1 = new HelloForkJoin(temp);
subTask1.fork();
if (_intArray.length - _threshold > 0) {
int remain[] = new int[_intArray.length - _threshold];
System.arraycopy(_intArray, _threshold, remain, 0, remain.length);
HelloForkJoin task = new HelloForkJoin(remain);
task.fork();
_result += task.join();
}
_result += subTask1.join();
return _result;
}
return _result;
}
public static void main(String[] args) throws InterruptedException {
int count = 10000;
serialCompute(count);
parallelCompute(count);
}
/**
* 使用fork/join并行计算数字累加。
*
* @param count 数字个数从1开始
* @throws InterruptedException
*/
private static void parallelCompute(int count)
throws InterruptedException {
int[] numbers = new int[count];
for (int i = 0; i < count; i++) {
numbers[i] = i+1;
}
ForkJoinPool pool = new ForkJoinPool(4);
HelloForkJoin task = new HelloForkJoin(numbers);
long startTime = System.currentTimeMillis();
pool.submit(task);
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println( String.format("并行计算结果:%d耗时%d毫秒", task._result, System.currentTimeMillis()-startTime) );
}
/**
* 使用for循环串行计算数字累加。
*
* @param count 数字个数从1开始
*/
private static void serialCompute(int count) {
long startTime = System.currentTimeMillis();
int sum = 0;
for (int i = 0; i < count; i++) {
sum += (i+1);
}
System.out.println( String.format("串行计算结果:%d耗时%d毫秒", sum, System.currentTimeMillis()-startTime) );
}
}

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.6" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="java.util.concurrent.Future" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="334" y="85"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<class id="2" language="java" name="java.util.concurrent.FutureTask" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="521" y="477"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</class>
<interface id="3" language="java" name="java.util.concurrent.RunnableFuture" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="521" y="280"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="4" language="java" name="java.util.concurrent.RunnableScheduledFuture" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="130" y="410"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="5" language="java" name="java.util.concurrent.ScheduledFuture" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="131" y="272"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="6" language="java" name="java.util.concurrent.Delayed" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="131" y="119"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="7" language="java" name="java.lang.Runnable" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="521" y="119"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<generalization id="8">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="5"/>
</generalization>
<generalization id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="7"/>
</generalization>
<generalization id="10">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="3"/>
</generalization>
<generalization id="11">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="6"/>
</generalization>
<generalization id="12">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</generalization>
<generalization id="13">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</generalization>
<realization id="14">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="3"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
accessors="true" visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@@ -0,0 +1,106 @@
package cn.aofeng.demo.java.util.future;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
/**
* Future、Callable练习。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HelloFuture {
private static Logger _logger = Logger.getLogger(HelloFuture.class);
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
executeSingleTask();
executeBatchTask();
}
/**
* 执行单个异步任务:<br>
* 1、用submit方法向线程池提交一个任务。<br>
* 2、获取结果时指定了超时时间。<br><br>
* 结果超时后调用者收到TimeoutException但实际上任务还在继续执行。
* @throws InterruptedException
*/
private static void executeSingleTask() throws InterruptedException {
ExecutorService threadpool = Executors.newFixedThreadPool(6);
try {
Future<Integer> f = threadpool.submit(createCallable(5 * 1000));
Object result = f.get(3, TimeUnit.SECONDS);
System.out.println("单个任务的执行结果:"+result);
} catch (Exception e) {
_logger.error("线程执行任务时出错", e);
} finally {
threadpool.shutdown();
threadpool.awaitTermination(10, TimeUnit.SECONDS);
}
}
/**
* 执行多个异步任务:<br>
* 1、用invokeAll方法向线程池提交多个任务并指定了执行的超时时间。<br><br>
* 结果超时后未执行完成的任务被取消在调用Future的get方法时取消的任务会抛出CancellationException执行完成的任务可获得结果。
* @throws InterruptedException
*/
private static void executeBatchTask() throws InterruptedException {
ExecutorService threadpool = Executors.newFixedThreadPool(6);
List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
tasks.add(createCallable(2000));
tasks.add(createCallable(5000));
tasks.add(createCallable(2500));
long startTime = System.currentTimeMillis();
try {
List<Future<Integer>> fs = threadpool.invokeAll(tasks, 3, TimeUnit.SECONDS);
int result = 0;
for (Future<Integer> f : fs) {
try{
result += f.get();
} catch(CancellationException ce) {
// nothing
}
}
System.out.println("执行三个任务共耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");
System.out.println("三个任务的执行结果汇总:"+result);
} catch (Exception e) {
_logger.error("线程执行任务时出错", e);
} finally {
threadpool.shutdown();
threadpool.awaitTermination(10, TimeUnit.SECONDS);
}
}
/**
* 创建需要长时间执行的任务模拟对象。
* @param sleepTimes 线程休眠时间(单位:毫秒)
* @return {@link Callable}对象
*/
private static Callable<Integer> createCallable(final int sleepTimes) {
Callable<Integer> c = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(sleepTimes);
System.out.println(Thread.currentThread().getName() + ": I'm working");
return 9;
}
};
return c;
}
}

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.6" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="java.util.Map" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="417" y="179"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="false" private="false" static="true"/>
</display>
</interface>
<class id="2" language="java" name="java.util.AbstractMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="417" y="423"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="3" language="java" name="java.util.HashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="824" y="422"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="false"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="4" language="java" name="java.util.Hashtable" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="152" y="312"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="5" language="java" name="java.util.IdentityHashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="206" y="423"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="6" language="java" name="java.util.LinkedHashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="824" y="283"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="7" language="java" name="java.util.EnumMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="329" y="528"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="8" language="java" name="java.util.TreeMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="499" y="526"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="9" language="java" name="java.util.WeakHashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="609" y="374"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<generalization id="10">
<end type="SOURCE" refId="9"/>
<end type="TARGET" refId="2"/>
</generalization>
<generalization id="11">
<end type="SOURCE" refId="8"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="12">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</realization>
<generalization id="13">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="14">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</realization>
<realization id="15">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</realization>
<realization id="16">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</realization>
<generalization id="17">
<end type="SOURCE" refId="7"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="18">
<end type="SOURCE" refId="9"/>
<end type="TARGET" refId="1"/>
</realization>
<generalization id="19">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="3"/>
</generalization>
<generalization id="20">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="21">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="1"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
accessors="true" visibility="true">
<attributes public="true" package="true" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="false" private="false" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@@ -0,0 +1,47 @@
package cn.aofeng.demo.java.util.timer;
import java.util.Timer;
import java.util.TimerTask;
import cn.aofeng.demo.util.DateUtil;
/**
* {@link Timer}的使用示例:<br>
* 定时任务1执行过程中会抛出异常。<br>
* 定时任务2执行过程中不会抛出异常。<br>
* <br>
* 目的检测java.util.Timer在执行定时任务的过程中任务内抛出异常没有捕捉时在下一次执行时间到来时是否可以正常执行。<br>
* 测试的JDK版本1.6.xx。<br>
* 结果不通过定时任务抛出异常时整个Timer中止其他定时任务也中止。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer("aofeng-timer-demo");
timer.schedule(new ThrowExceptionTask(), DateUtil.getNextMinute(), 60*1000);
timer.schedule(new NotThrowExceptionTask(), DateUtil.getNextMinute(), 60*1000);
}
static class ThrowExceptionTask extends TimerTask {
@Override
public void run() {
System.out.println("任务名:ThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
throw new IllegalArgumentException();
}
} // end of ThrowExceptionTask
static class NotThrowExceptionTask extends TimerTask {
@Override
public void run() {
System.out.println("任务名:NotThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
}
} // end of NotThrowExceptionTask
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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));
}
}
}

View File

@@ -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 <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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);
}
}

View File

@@ -0,0 +1,42 @@
package cn.aofeng.demo.jetty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* 抓取页面内容。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGet {
public String getSomeThing(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(3000);
urlConn.setRequestMethod("GET");
urlConn.connect();
InputStream ins = null;
try {
if (200 == urlConn.getResponseCode()) {
ins = urlConn.getInputStream();
ByteArrayOutputStream outs = new ByteArrayOutputStream(1024);
IOUtils.copy(ins, outs);
return outs.toString("UTF-8");
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
}

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.jetty;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* {@link HttpGet}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGetTest {
private HttpServerMock _mock;
private HttpGet _httpGet = new HttpGet();
@Before
public void setUp() throws Exception {
_mock = new HttpServerMock();
}
@After
public void tearDown() throws Exception {
if (null != _mock) {
_mock.stop();
}
}
/**
* 用例响应状态码为200且有响应内容。
*/
@Test
public void testGetSomeThing4Success() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain");
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertEquals(response, content);
}
/**
* 用例响应状态码为非200。
*/
@Test
public void testGetSomeThing4Fail() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain", 500);
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertNull(content);
}
}

View File

@@ -0,0 +1,90 @@
package cn.aofeng.demo.jetty;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
/**
* HTTP服务器MOCK可用于单元测试时模拟HTTP服务器的响应。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpServerMock {
public final static int DEFAULT_PORT = 9191;
public final static String DEFAULT_CONTENT_TYPE = "application/json";
public final static int DEFAULT_STATUS_CODE=HttpServletResponse.SC_OK;
private Server _httpServer;
private int _port;
public HttpServerMock() {
_port = DEFAULT_PORT;
}
public HttpServerMock(int port) {
_port = port;
}
/**
* 启动Jetty服务器。默认的响应status code为"200"content type为"application/json"。
* @param content 响应内容
*/
public void start(String content) throws Exception {
start(content, DEFAULT_CONTENT_TYPE, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。默认的响应status code为"200"。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
*/
public void start(String content, String contentType) throws Exception {
start(content, contentType, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
* @param statuCode 响应状态码
*/
public void start(String content, String contentType,
int statuCode) throws Exception {
_httpServer = new Server(_port);
_httpServer.setHandler(createHandler(content, contentType, statuCode));
_httpServer.start();
}
/**
* 停止Jetty服务器。
*/
public void stop() throws Exception {
if (null != _httpServer) {
_httpServer.stop();
_httpServer = null;
}
}
private Handler createHandler(final String content, final String contentType,
final int statusCode) {
return new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType(contentType);
response.setStatus(statusCode);
baseRequest.setHandled(true);
response.getWriter().print(content);
}
};
}
}

View File

@@ -0,0 +1,216 @@
使用Jetty实现Http Server Mock作单元测试
===
在研发过程中发现许多模块对外部系统的调用并没有沉淀成专门的客户端代码中直接调用URL发送请求和获取响应内容导致做单元测试的时候非常麻烦。
特别是对已有系统做改造的时候,要先写单元测试用例,保证后续的重构和修改不偏离原来的业务需求。
这样问题就来了:该如何模拟外部系统返回各种正常和异常的响应内容呢?
办法总是有的这里就用Jetty实现Http Server Mock模拟服务端返回各种响应数据。
预备
---
* [JUnit 4.11](http://junit.org/)
* [Jetty 7.6.9](http://www.eclipse.org/jetty/)
业务示例代码
---
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpGet.java)
```java
package cn.aofeng.demo.jetty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* 抓取页面内容。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGet {
public String getSomeThing(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(3000);
urlConn.setRequestMethod("GET");
urlConn.connect();
InputStream ins = null;
try {
if (200 == urlConn.getResponseCode()) {
ins = urlConn.getInputStream();
ByteArrayOutputStream outs = new ByteArrayOutputStream(1024);
IOUtils.copy(ins, outs);
return outs.toString("UTF-8");
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
}
```
用Jetty实现的Http Server Mock
---
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpServerMock.java)
```java
package cn.aofeng.demo.jetty;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
/**
* HTTP服务器MOCK可用于单元测试时模拟HTTP服务器的响应。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpServerMock {
public final static int DEFAULT_PORT = 9191;
public final static String DEFAULT_CONTENT_TYPE = "application/json";
public final static int DEFAULT_STATUS_CODE=HttpServletResponse.SC_OK;
private Server _httpServer;
private int _port;
public HttpServerMock() {
_port = DEFAULT_PORT;
}
public HttpServerMock(int port) {
_port = port;
}
/**
* 启动Jetty服务器。默认的响应status code为"200"content type为"application/json"。
* @param content 响应内容
*/
public void start(String content) throws Exception {
start(content, DEFAULT_CONTENT_TYPE, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。默认的响应status code为"200"。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
*/
public void start(String content, String contentType) throws Exception {
start(content, contentType, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
* @param statuCode 响应状态码
*/
public void start(String content, String contentType,
int statuCode) throws Exception {
_httpServer = new Server(_port);
_httpServer.setHandler(createHandler(content, contentType, statuCode));
_httpServer.start();
}
/**
* 停止Jetty服务器。
*/
public void stop() throws Exception {
if (null != _httpServer) {
_httpServer.stop();
_httpServer = null;
}
}
private Handler createHandler(final String content, final String contentType,
final int statusCode) {
return new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType(contentType);
response.setStatus(statusCode);
baseRequest.setHandled(true);
response.getWriter().print(content);
}
};
}
}
```
单元测试代码
---
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpGetTest.java)
```java
package cn.aofeng.demo.jetty;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* {@link HttpGet}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGetTest {
private HttpServerMock _mock;
private HttpGet _httpGet = new HttpGet();
@Before
public void setUp() throws Exception {
_mock = new HttpServerMock();
}
@After
public void tearDown() throws Exception {
if (null != _mock) {
_mock.stop();
}
}
/**
* 用例响应状态码为200且有响应内容。
*/
@Test
public void testGetSomeThing4Success() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain");
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertEquals(response, content);
}
/**
* 用例响应状态码为非200。
*/
@Test
public void testGetSomeThing4Fail() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain", 500);
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertNull(content);
}
}
```

View File

@@ -0,0 +1,45 @@
package cn.aofeng.demo.json.gson;
import com.google.gson.Gson;
/**
* 数组的反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ArrayDeserialize {
public <T> T deserialize(String json, Class<T> claz) {
Gson gson = new Gson();
return gson.fromJson(json, claz);
}
public static void main(String[] args) {
ArrayDeserialize ad = new ArrayDeserialize();
// 整型数组
String intArrJson = "[9,7,5]";
int[] intArr = ad.deserialize(intArrJson, int[].class);
System.out.println("---------- 整型数组 ----------");
for (int i : intArr) {
System.out.println(i);
}
// 字符串数组
String strArrJson = "[\"张三\",\"李四\",\"王五\"]";
String[] strArr = ad.deserialize(strArrJson, String[].class);
System.out.println("---------- 字符串数组 ----------");
for (String str : strArr) {
System.out.println(str);
}
// 对象数组
String objArrJson = "[{\"name\":\"小明\",\"age\":10},{\"name\":\"马丽\",\"age\":9}]";
Person[] objArr = ad.deserialize(objArrJson, Person[].class);
System.out.println("---------- 对象数组 ----------");
for (Person person : objArr) {
System.out.println(person);
}
}
}

View File

@@ -0,0 +1,41 @@
package cn.aofeng.demo.json.gson;
import com.google.gson.Gson;
/**
* 数组的序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ArraySerialize {
public void serialize(Object[] arr) {
Gson gson = new Gson();
System.out.println( gson.toJson(arr) );
}
public static void main(String[] args) {
ArraySerialize as = new ArraySerialize();
// 整型对象数组
Integer[] intArr = new Integer[3];
intArr[0] = 9;
intArr[1] = 7;
intArr[2] = 5;
as.serialize(intArr);
// 字符串数组
String[] names = new String[3];
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
as.serialize(names);
// 对象数组
Person[] persons = new Person[2];
persons[0] = new Person("小明", 10);
persons[1] = new Person("马丽", 9);
as.serialize(persons);
}
}

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.json.gson;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
/**
* 集合的反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CollectionDeserialize {
public <T> T deserialize(String json, Type type) {
Gson gson = new Gson();
return gson.fromJson(json, type);
}
public static void main(String[] args) {
CollectionDeserialize cd = new CollectionDeserialize();
//整型List
String intListJson = "[9,8,0]";
List<Integer> intList = cd.deserialize( intListJson,
new TypeToken<List<Integer>>(){}.getType() );
System.out.println("---------- 整型List ----------");
for (Integer obj : intList) {
System.out.println(obj);
}
// 字符串Set
String strSetJson = "[\"Best\",\"World\",\"Hello\"]";
Set<String> strSet = cd.deserialize( strSetJson,
new TypeToken<Set<String>>(){}.getType() );
System.out.println("---------- 字符串Set ----------");
for (String str : strSet) {
System.out.println(str);
}
// Map
String objMapJson = "{\"xiaomin\":{\"name\":\"小明\",\"age\":21},\"marry\":{\"name\":\"马丽\",\"age\":20}}";
Map<String, Person> objMap = cd.deserialize( objMapJson,
new TypeToken<Map<String, Person>>(){}.getType() );
System.out.println("---------- Map ----------");
for (Entry<String, Person> entry : objMap.entrySet()) {
System.out.println(entry);
}
}
}

View File

@@ -0,0 +1,54 @@
package cn.aofeng.demo.json.gson;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
/**
* 集合的序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CollectionsSerialize {
public void serialize(Collection<?> c) {
Gson gson = new Gson();
System.out.println( gson.toJson(c) );
}
public void serialize(Map<?, ?> map) {
Gson gson = new Gson();
System.out.println( gson.toJson(map) );
}
public static void main(String[] args) {
CollectionsSerialize cs = new CollectionsSerialize();
// 整型List
List<Integer> intList = new ArrayList<Integer>();
intList.add(9);
intList.add(8);
intList.add(0);
cs.serialize(intList);
// 字符串Set
Set<String> strSet = new HashSet<String>();
strSet.add("Hello");
strSet.add("World");
strSet.add("Best");
cs.serialize(strSet);
// Map
Map<String, Person> objMap = new HashMap<String, Person>();
objMap.put("marry", new Person("马丽", 20));
objMap.put("xiaomin", new Person("小明", 21));
cs.serialize(objMap);
}
}

View File

@@ -0,0 +1,46 @@
package cn.aofeng.demo.json.gson;
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
/**
* 自定义反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CustomDeserialize {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Person.class, new PersonDeserializer());
Gson gson = builder.create();
String json = "{\"PersonName\":\"aofeng\",\"PersonAge\":32}";
Person obj = gson.fromJson(json, Person.class);
System.out.println(obj); // 输出结果Person [name=aofeng, age=32]
}
public static class PersonDeserializer implements JsonDeserializer<Person> {
@Override
public Person deserialize(JsonElement jsonEle, Type type,
JsonDeserializationContext context)
throws JsonParseException {
JsonObject jo = jsonEle.getAsJsonObject();
String name = jo.get("PersonName").getAsString();
int age = jo.get("PersonAge").getAsInt();
Person obj = new Person(name, age);
return obj;
}
} // end of PersonDeserializer
}

View File

@@ -0,0 +1,42 @@
package cn.aofeng.demo.json.gson;
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
/**
* 自定义序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CustomSerialize {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Person.class, new PersonSerializer());
Gson gson = builder.create();
Person obj = new Person("aofeng", 32);
System.out.println( gson.toJson(obj) ); // 输出结果:{"PersonName":"aofeng","PersonAge":32}
}
public static class PersonSerializer implements JsonSerializer<Person> {
@Override
public JsonElement serialize(Person obj, Type type,
JsonSerializationContext context) {
JsonObject jo = new JsonObject();
jo.addProperty("PersonName", obj.getName());
jo.addProperty("PersonAge", obj.getAge());
return jo;
}
} // end of PersonSerializer
}

View File

@@ -0,0 +1,50 @@
package cn.aofeng.demo.json.gson;
/**
* 简单的Java对象。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Person {
private String name;
private int age;
public Person() {
// nothing
}
@SuppressWarnings("unused")
private void reset() {
name = null;
age = 0;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

View File

@@ -0,0 +1,39 @@
package cn.aofeng.demo.json.gson;
import com.google.gson.Gson;
/**
* Java简单对象的序列化与反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class SimpleObjectSerialize {
/**
* 序列化将Java对象转换成JSON字符串。
*/
public void serialize(Person person) {
Gson gson = new Gson();
System.out.println( gson.toJson(person) );
}
/**
* 反序列化将JSON字符串转换成Java对象。
*/
public void deserialize(String json) {
Gson gson = new Gson();
Person person = gson.fromJson(json, Person.class);
System.out.println( person );
}
public static void main(String[] args) {
SimpleObjectSerialize ss = new SimpleObjectSerialize();
Person person = new Person("NieYong", 33);
ss.serialize(person);
String json = " {\"name\":\"AoFeng\",\"age\":32}";
ss.deserialize(json);
}
}

View File

@@ -0,0 +1,63 @@
package cn.aofeng.demo.misc;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
/**
* 获取本机IP和主机名以及Java环境信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class GetHostInfo {
/**
* @param args
* @throws UnknownHostException
* @throws SocketException
*/
public static void main(String[] args) throws UnknownHostException, SocketException {
InetAddress address = InetAddress.getLocalHost();
System.out.println("计算机名:" + address.getHostName());
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
StringBuilder buffer = new StringBuilder();
NetworkInterface ni = nis.nextElement();
buffer.append("网卡:").append(ni.getName());
buffer.append(" 绑定IP");
Enumeration<InetAddress> ias = ni.getInetAddresses();
int count = 0;
while (ias.hasMoreElements()) {
InetAddress ia = ias.nextElement();
if (count > 0) {
buffer.append(", ");
}
buffer.append(ia.getHostAddress());
}
System.out.println(buffer.toString());
}
System.out.println("Java环境信息:");
System.out.println("---------------------------------------------------:");
Properties pros = System.getProperties();
Set<Entry<Object, Object>> javaEnums = pros.entrySet();
for (Entry<Object, Object> entry : javaEnums) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
System.out.println("");
System.out.println("系统环境信息:");
System.out.println("---------------------------------------------------:");
Map<String, String> envs = System.getenv();
Set<Entry<String, String>> envSet = envs.entrySet();
for (Entry<String, String> entry : envSet) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}

View File

@@ -0,0 +1,50 @@
package cn.aofeng.demo.mockito;
/**
* 商品信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
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;
}
}

View File

@@ -0,0 +1,20 @@
package cn.aofeng.demo.mockito;
import java.util.List;
/**
* 商品存储层。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CommodityDao {
public Commodity queryById(String id) {
return null;
}
public List<Commodity> queryByType(int type) {
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More