Posted in Java, 技术

有趣的译文 《Java Pattern 那些小事》

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

一、现代的玩具 – Java 可以做很多事

一个 Q 与 A 的对话,展示了 Java 那些小事。

Q : 5 是整数吗?

A : 是。


Q : -23 是数吗?

A : 是,但负数使用场景很少。


Q : 5.32 是整数吗?

A : 不是,我们不用这种数值类型表示 5.32 。


Q : 5 属于什么数值类型?

A : int (整型)。
批注: int 是基本类型,int 代表整数。


Q : 快,想出另一个整数!

A : 19?


Q : 那 true 属于什么数值类型?

A : boolean (布尔型)。


Q : false 属于什么数值类型?

A : boolean。


Q : 想得出其他布尔值吗?

A : 没有,布尔值只有 true 和 false。


Q : int 是什么?

A : 一种类型。


Q : boolean 是什么?

A : 另一种类型。


Q : 什么是类型?

A : 类型是一个相关值集合的名称。


Q : 能举例解释下类型吗?

A : 使用类型就像使用一种集合一样。比如使用 boolean 可以表示逻辑值:是与非


Q : 能创建新的类型吗?

A : 我们还不知道怎么创建。


Q : 画出下面类的基本关系。

abstract class Seasoning {}

class Salt extends Seasoning {}

class Pepper extends Seasoning {}

A : 这样画?
图 1.1 Seasoning 类关系图

wechatimg4


Q : 是的。Seasoning 是数据类型,Salt 和 Pepper 是 Seasoning 的具体类型。

A : 好的。三个都是新类型吗?


Q : 是的。那么 new Salt() 是一个 Seasoning 吗?

A : 是的。 new Salt() new 关键字表示创建了 Salt 的一个实例,每个 Salt 的实例也是 Seasoning。


Q : new Pepper() 也是吗?

A : 是的。它也是一个 Seasoning。 new Pepper() new 关键字表示创建了 Pepper 的一个实例,每个 Pepper 的实例也是 Seasoning。


Q : 什么是 abstract , class 和 extends 关键字?

A : 简单地说:
abstract class 表示抽象数据类型
class 表示具体类型
extends 连接了具体类型到数据类型


Q : 还有其他的 Seasoning 具体类型吗?

A : 没有,因为只有 Salt 和 Pepper 继承 Seasoning。


Q : 正确。只有 Salt 和 Pepper 是 Seasoning 的具体类型。我们见过类似 Seasoning 数据类型吗?

A : 没有。但是 boolean 类型有两个值:true 和 false。
批注:值与具体类型是不同的。


Q : 再定义一个 Seasoning 具体类型:

class Thyme extends Seasoning {}

A : 再来一个:

class Sage extends Seasoning {}

Q : 4 个 Seasoning 具体类型了。

A : 是的。


Q : 什么是笛卡尔点?

A : 是一对数字。


Q : 曼哈顿地点位于哪里?

A : 两个城市街道的十字路口。


Q : 看代码,CartesianPt 类和 ManhattanPt 类有啥不同于 Salt 类和 Pepper 类

abstract class Point {}
class CartesianPt extends Point {
    int x;
    int y;
    CartesianPt(int _x, int _y){
        x = _x;
        y = _y;
    }
}
class ManhattanPt extends Point {
    int x;
    int y;
    ManhattanPt(int _x, int _y){
        x = _x;
        y = _y;
    }

}

A : 两个类中 {} 包含了三个元素。x 和 y 是表示点的坐标,但是构造函数里面包含什么呢?(批注:构造函数是同类名的函数)


Q : CartesianPt 和 ManhattanPt 各自的构造函数里面包含各自的字段值。

A : 那怎么使用构造函数呢?


Q : new 关键字作用于构造函数,会创建一个新的该类型实例。

A : 这样子呀。


Q : 比如下面的代码:

new CartesianPt(2,3);

使用 CartesianPt 的构造函数,创建一个 CartesianPt 实例。

A : 这样创建的 CartesianPt 实例 ,其字段 x 值为 2,y 值为 3。

class CartesianPt extends Point {

还有 extends 关键字表示创建的 CartesianPt 也是 Point。


Q : 对。那么下面代码是创建一个 ManhattanPt 实例吗?

new ManhattanPt(2,3);

A : 是,它也有 x 值为 2,y 值为 3。


Q : 构造函数就这样?

A : 基本是了,但在没有定义过构造函数的代码里,以前用过构造函数。这是怎么实现的呢?


Q : 比如 Salt 和 Pepper 没有任何字段,但是它们有个默认构造函数。

A : 这是正确使用默认构造函数的方式吗?


Q : 是的。默认构造函数没有包含字段值。使用 new 关键字创建实例时,创建的实例没有字段值。

A : 好,下面这段代码呢?

new Point()

Q : Point 是一个抽象类,它是不完整的类,所以 new 关键字不能创建 Point 实例。

A : 这就说得通了,继续。


Q : 以下定义的类是另一种抽象数据类型和它的具体数据类型吗?

abstract class Num {}
class Zero extends Num {}
class OneMoreThan extends Num {
    Num predecessor;
    OneMoreThan (Num p) {
        predecessor = p;
    }
}

A : 是的。它们定义了一种抽象数据类型和两个具体数据类型。
图 1.2 Num 类关系图

wechatimg5


Q : new Zero() 是 Num 的实例吗?

A : 明显是的。就像 new Salt() 是 Salt 的一个实例。


Q : new OneMoreThan(new Zero()) 是 Num 的实例吗?

A : 是。OneMoreThan 继承了 Num,new OneMoreThan() 是一个 OneMoreThan 实例,本身也是一个 Num 实例,并包含了一个 Num 实例(这里是指new Zero())。


Q : 为什么 OneMoreThan 可以这样实现?

A : new Zero() 是一个 Num 实例,OneMoreThan 的实例创建时,包含了 Zero 的实例。


Q : 包含 Zero 实例?

A : OneMoreThan 的实例拥有一个值叫做 predecessor,即这个值可以是 new Zero()。


Q : predecessor 只能是 Zero 的实例?

A : 不是的。Num predecessor 说明了 predecessor 是一个 Num,所以具体表现为 OneMoreThan 或者 Zero 的实例。


Q : 那 new OneMoreThan(new OneMoreThan(new Zero()))呢?

A : 一个 Num 的实例,最外层的 new OneMoreThan 拥有了一个 Num 的实例:new OneMoreThan(new Zero())


Q : 那 new OneMoreThan(0)?

A : 胡说,0 不是一个 Num 。


Q : new Zero() 和 0 一样吗?

A : 不一样,虽然 new Zero() 和 0 概念相似,但不一样。


Q : new OneMoreThan(new Zero()) 和 1 概念相似?

A : 是的,但是他们是不一样的。


Q : new OneMoreThan( new OneMoreThan( new OneMoreThan(new Zero())))和哪个数概念相似?

A : 3


Q : Num 比 boolean 多吗?

A : 多很多。


Q : 比 int 多吗?

A : 不。


Q : 说下 new Zero() 和 0 的区别呢?

A : 我们得区别它们。new Zero() 是 Zero 的一个实例,意味着也是 Num 。0 是一个 int。


Q : 正确。不同数据类型的实例就是不同的。

A : 类型是一系列实例的统称。


Q : 基本类型(int 和 boolean)是不同的。

A : 那些不是基本类型呢?


Q : 类定义不引入基本类型,比如上面提到的 new Zero() 不单单是 Zero 的实例,也是 Num。任何继承 Num 的数据类型都是。

A : 然后呢?


Q : 就像每个数据类型继承 Object。

A : 就是说,所有类都是一个对象。


Q : 正确,下面有所体现。

A : 好的。


建议一:
自定义数据类型时,使用抽象类
自定义具体数据类型时,使用 extends 关键词继承抽象类


Q : 看看下面的定义?

abstract class Layer {}
class Base extends Layer {
    Object o;
    Base(Object o) {
        this.o = o;
    }
}
class Slice extends Layer {
    Layer l;
    Slice(Layer l) {
        this.l = l;
    }
}

A : 定义了 Layer 抽象数据类型和其两个具体数据类型 Base 和 Slice。其中 Base 包含一个值对象 Object。


Q : new Base(new Zero()) 是什么?

A : 一个 Base 的实例,即是 Layer 又是 Object 的实例。


Q : new Base(new Salt()) 是什么?

A : 一个 Base 的实例。但 new Base(new Salt())new Base(new Zero()) 是同一个数据类型吗?


Q : 是的。因为对象实例由 new 关键字创建,而它们都是 Base。

A : 因为 Base 包含的值对象是 Object。所以可以是 new Salt()new Zero()


Q : 任何都是个 Object 吗?

A : new 关键字创建的都是对象。自然 String 、Arrays 也是对象,这里我们不展开讲。


Q : 是的。下面的是 Layer 实例吗?

new Base(5)

A : 5 没有通过 new 创建,所以这个错误的语法。


Q : 下面的是 Layer 实例吗?

new Base(false)

A : false 没有通过 new 创建,所以这个错误的语法。


Q : 下面的是 Layer 实例吗?

 new Base(new Integer(5))

A : 是的。 new Integer(5) 创建了一个 int 对象。


Q : 那么怎么创建一个 Layer 实例拥有 false?

A : 简单:

 new Base(new Boolean(false))

Q : int Integer / boolean Boolean 混乱了?

A : 以后会涉及到。


Q : 期待更多吧。

A : 迫不及待。

Posted in Java, 技术

代码片段:基于 JDK 8 time包的时间工具类 TimeUtil

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“知识的工作者必须成为自己时间的首席执行官。”

前言

这次泥瓦匠带来的是一个好玩的基于 JDK 8 time包的时间工具类 TimeUtil。本意,用 JDK 8 写个实战的时间工具类,初版泥瓦匠写的很不好,后来雨神分分钟将这个代码优化优化,谢谢雨神。就此分享下这个代码,让更多的人看到~

一、 Java 8 time包

从 Java 1.0 有个 Date 类,想必大家不陌生,后面有了 Calendar 类(被废弃 )。API 确实比较难用,因此 Java 8 引入 java.time API,这次看看是不是很好用。大致引入了这几个对象:

  • Instant – 瞬间类,表示时间线上的一点(与 Date 类似)
  • Duration – 持续时间,表示两个 Instant 之间的时间
  • LocalDateTime – 本地日期时间,表示一个日期和时间。

本小文重点还是在使用 LocalDateTime 及其格式化类 DateTimeFormatter

二、介绍 LocalDateTime & DateTimeFormatter APIs

LocalDateTime 表示一个日期和时间,存储确定时区中的某个时间点。
例如某一次练书法弹琴活动。(ps:泥瓦匠有个小圈子,里面喜欢加入一些文艺的程序员。爱技术,爱生活,爱艺术~ 雨神就爱弹琴,日语思密达*&#@#% )

常用api:

  • now()
    从系统中获取当前时间
  • parse(CharSequence text, DateTimeFormatter formatter)
    从字符串按格式获取 LocalDateTime 实例

DateTimeFormatter 用于格式化时间,提供了公用的方法入口,打印和解析格式化的时间类。

常用api:

  • format(TemporalAccessor temporal)
    按格式格式化时间
  • ofPattern(String pattern)
    按字符串指定的格式,生成时间格式

 

三、TimeUtil 代码详解

泥瓦匠一一道来这个代码的实现。先看代码:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 基于 JDK 8 time包的时间工具类
 * <p/>
 * Created by bysocket on 16/8/23.
 */
public final class TimeUtil {

    /**
     * 获取默认时间格式: yyyy-MM-dd HH:mm:ss
     */
    private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = TimeFormat.LONG_DATE_PATTERN_LINE.formatter;

    private TimeUtil() {
        // no construct function
    }

    /**
     * String 转时间
     *
     * @param timeStr
     * @return
     */
    public static LocalDateTime parseTime(String timeStr) {
        return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER);
    }

    /**
     * String 转时间
     *
     * @param timeStr
     * @param format  时间格式
     * @return
     */
    public static LocalDateTime parseTime(String timeStr, TimeFormat format) {
        return LocalDateTime.parse(timeStr, format.formatter);
    }

    /**
     * 时间转 String
     *
     * @param time
     * @return
     */
    public static String parseTime(LocalDateTime time) {
        return DEFAULT_DATETIME_FORMATTER.format(time);
    }

    /**
     * 时间转 String
     *
     * @param time
     * @param format 时间格式
     * @return
     */
    public static String parseTime(LocalDateTime time, TimeFormat format) {
        return format.formatter.format(time);
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static String getCurrentDatetime() {
        return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now());
    }

    /**
     * 获取当前时间
     *
     * @param format 时间格式
     * @return
     */
    public static String getCurrentDatetime(TimeFormat format) {
        return format.formatter.format(LocalDateTime.now());
    }

    /**
     * 时间格式
     */
    public enum TimeFormat {

        /**
         * 短时间格式
         */
        SHORT_DATE_PATTERN_LINE("yyyy-MM-dd"),
        SHORT_DATE_PATTERN_SLASH("yyyy/MM/dd"),
        SHORT_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd"),
        SHORT_DATE_PATTERN_NONE("yyyyMMdd"),

        /**
         * 长时间格式
         */
        LONG_DATE_PATTERN_LINE("yyyy-MM-dd HH:mm:ss"),
        LONG_DATE_PATTERN_SLASH("yyyy/MM/dd HH:mm:ss"),
        LONG_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss"),
        LONG_DATE_PATTERN_NONE("yyyyMMdd HH:mm:ss"),

        /**
         * 长时间格式 带毫秒
         */
        LONG_DATE_PATTERN_WITH_MILSEC_LINE("yyyy-MM-dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_SLASH("yyyy/MM/dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_NONE("yyyyMMdd HH:mm:ss.SSS");

        private transient DateTimeFormatter formatter;

        TimeFormat(String pattern) {
            formatter = DateTimeFormatter.ofPattern(pattern);
        }
    }
}

工具类由 final TimeUtil类 及 其内部枚举类TimeFormat时间格式类 组成。
1/ TimeUtil 具有私有构造函数,表示被保护,无法被外部 new 出实例。

声明了默认的  DateTimeFormatter 时间格式:yyyy-MM-dd HH:mm:ss。其他则是提供了 获取当前时间 和 时间与String互转的方法。
2/ TimeFormat 内部枚举类,首先它是单例的。

transient 关键字目的是确保 DateTimeFormatter 无序列化存储。为啥单例,因为 DateTimeFormmatter 是无状态的,可以线程共享。

具体方法解析如下:
1. 获取当前时间

String now = TimeUtil.getCurrentDatetime();
System.out.println(now);
output:
2016-08-28 16:35:23

2. 获取当前相应格式的当前时间

String now = TimeUtil.getCurrentDatetime(TimeUtil.TimeFormat.LONG_DATE_PATTERN_SLASH);
System.out.println(now);
output:
2016/08/28 16:36:24

3. String 转时间
默认格式:yyyy-MM-dd HH:mm:ss

LocalDateTime expectedDateTime = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
LocalDateTime parsedTime = TimeUtil.parseTime("2014-11-11 10:11:11");
assertEquals(expectedDateTime, parsedTime);

其他格式之一:yyyy-MM-dd HH:mm:ss

LocalDateTime expectedDateTime = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
LocalDateTime parsedTime = TimeUtil.parseTime("2014/11/11 10:11:11", LONG_DATE_PATTERN_SLASH);
assertEquals(expectedDateTime, parsedTime);

4. 时间转 String
默认格式:yyyy-MM-dd HH:mm:ss

LocalDateTime time = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
assertEquals(TimeUtil.parseTime(time), "2014-11-11 10:11:11");

其他格式之一:yyyy-MM-dd HH:mm:ss

LocalDateTime time = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
assertEquals(TimeUtil.parseTime(time, LONG_DATE_PATTERN_DOUBLE_SLASH), "2014\\11\\11 10:11:11");

 

四、与 Old 代码互操作

java.time 类与 Old Date 代码互操作如下图:

time

 

五、小结

实战中的 JDK8 ,Show the code。 在用的项目,完善测试时候用起来才是关键。自然需要 JDK 环境支持,升级吧升级吧。
基于 JDK 8 time包的实践,这次只讲了 LocalDateTime 类,慢慢带来更多。
相关代码分享在 Github 主页

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

 

时间啊 时间啊,来发反鸡汤
72e172b3gw1f5tlp60gryj20je0m8ta0

Posted in Java, 技术

fast-framework – 基于 JDK 8 实现的 Java Web MVC 框架

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

fast-framework


轻量级 Java Web 框架 – https://github.com/JeffLi1993/fast-framework

  • 基于 JDK 8 规范
  • 基于 Servlet 3.0 规范
  • 零配置
  • REST 服务接口 基于 JSON 传输
  • 目前提供 MVC … 解决方案

 

一、作者与学习乐园

源码地址:我的GitHub地址
作者: 李强强 也叫 泥瓦匠
QQ群:编程之美 365234583 立即加入

二、快速入门

1. 必备环境

2. 下载 fast-framework 项目及 sample 案例

git clone https://github.com/JeffLi1993/fast-framework.git

3. Maven 编译 sample 案例

cd fast-framework // cd 到工程根目录

mvn clean install // 编译工程

4. 启动 sample 案例

cd fast-sample // cd 到 sample 案例工程根目录
mvn jetty:run  // 启动 sample 工程

Console 出现如下结果,表示服务器成功启动:

...
[INFO] Started ServerConnector@f1a45f8{HTTP/1.1}{0.0.0.0:8080}
[INFO] Started @5133ms
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 10 seconds.

5. 访问 sample

在浏览器中打开:

http://localhost:8080/hello?name=BYSocket

会得到如下结果:
sample结果图

 

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

 

Posted in Java, 技术

Code片段 : .properties属性文件操作工具类 & JSON工具类

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“贵专” — 泥瓦匠

一、java.util.Properties API & 案例

java.util.Properties 是一个属性集合。常见的api有如下:

  • load(InputStream inStream)  从输入流中读取属性
  • getProperty(String key)  根据key,获取属性值
  • getOrDefault(Object key, V defaultValue) 根据key对象,获取属性值需要强转

首先在resources目录下增加/main/resources/fast.properties:

fast.framework.name=fast
fast.framework.author=bysocket
fast.framework.age=1

然后直接上代码PropertyUtil.java:

/**
 * .properties属性文件操作工具类
 *
 * Created by bysocket on 16/7/19.
 */
public class PropertyUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);

    /** .properties属性文件名后缀 */
    public static final String PROPERTY_FILE_SUFFIX	= ".properties";

    /**
     * 根据属性文件名,获取属性
     *
     * @param propsFileName
     * @return
     */
    public static Properties getProperties(String propsFileName) {
        if (StringUtils.isEmpty(propsFileName))
            throw new IllegalArgumentException();

        Properties  properties  = new Properties();
        InputStream inputStream = null;

        try {

            try {
                /** 加入文件名后缀 */
                if (propsFileName.lastIndexOf(PROPERTY_FILE_SUFFIX) == -1) {
                    propsFileName += PROPERTY_FILE_SUFFIX;
                }

                inputStream = Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream(propsFileName);
                if (null != inputStream)
                    properties.load(inputStream);
            } finally {
                if ( null != inputStream) {
                    inputStream.close();
                }
            }

        } catch (IOException e) {
            LOGGER.error("加载属性文件出错!",e);
            throw new RuntimeException(e);
        }

        return properties;
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @return
     */
    public static String getString(Properties properties, String key){
        return properties.getProperty(key);
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @param defaultValue
     * @return
     */
    public static String getStringOrDefault(Properties properties, String key, String defaultValue){
        return properties.getProperty(key,defaultValue);
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @param defaultValue
     * @param <V>
     * @return
     */
    public static <V> V getOrDefault(Properties properties, String key, V defaultValue){
        return (V) properties.getOrDefault(key,defaultValue);
    }
}

UT如下:

/**
 * {@link PropertyUtil} 测试用例
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class PropertyUtilTest {

    @Test
    public void testGetProperties() {
        Properties properties = PropertyUtil.getProperties("fast");
        String fastFrameworkName = properties.getProperty("fast.framework.name");
        String authorName        = properties.getProperty("fast.framework.author");
        Object age               = properties.getOrDefault("fast.framework.age",10);
        Object defaultVal        = properties.getOrDefault("fast.framework.null",10);
        System.out.println(fastFrameworkName);
        System.out.println(authorName);
        System.out.println(age.toString());
        System.out.println(defaultVal.toString());
    }

    @Test
    public void testGetString() {
        Properties properties = PropertyUtil.getProperties("fast");
        String fastFrameworkName = PropertyUtil.getString(properties,"fast.framework.name");
        String authorName        = PropertyUtil.getString(properties,"fast.framework.author");
        System.out.println(fastFrameworkName);
        System.out.println(authorName);
    }

    @Test
    public void testGetOrDefault() {
        Properties properties = PropertyUtil.getProperties("fast");
        Object age               = PropertyUtil.getOrDefault(properties,"fast.framework.age",10);
        Object defaultVal        = PropertyUtil.getOrDefault(properties,"fast.framework.null",10);
        System.out.println(age.toString());
        System.out.println(defaultVal.toString());
    }
}

Run Console:

1
10
fast
bysocket
1
10
fast
bysocket

相关对应代码分享在 Github 主页


二、JACKSON 案例

首先,加个Maven 依赖:

                


                <!-- Jackson -->
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.13</version>
		</dependency>
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-jaxrs</artifactId>
			<version>1.9.13</version>
		</dependency>

 

然后直接上代码JSONUtil:

/**
 * JSON 工具类
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class JSONUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JSONUtil.class);

    /**
     * 默认JSON类
     **/
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * 将 Java 对象转换为 JSON 字符串
     *
     * @param object
     * @param <T>
     * @return
     */
    public static <T> String toJSONString(T object) {
        String jsonStr;
        try {
            jsonStr = mapper.writeValueAsString(object);
        } catch (Exception e) {
            LOGGER.error("Java Object Can't covert to JSON String!");
            throw new RuntimeException(e);
        }
        return jsonStr;
    }


    /**
     * 将 JSON 字符串转化为 Java 对象
     *
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T toObject(String json, Class<T> clazz) {
        T object;
        try {
            object = mapper.readValue(json, clazz);
        } catch (Exception e) {
            LOGGER.error("JSON String Can't covert to Java Object!");
            throw new RuntimeException(e);
        }
        return object;
    }

}

UT如下:

/**
 * {@link JSONUtil} 测试用例
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class JSONUtilTest {

    @Test
    public void testToJSONString() {
        JSONObject jsonObject = new JSONObject(1, "bysocket", 33);
        String jsonStr = JSONUtil.toJSONString(jsonObject);
        Assert.assertEquals("{\"age\":1,\"name\":\"bysocket\",\"id\":33}", jsonStr);
    }

    @Test(expected = RuntimeException.class)
    public void testToJSONStringError() {
        JSONUtil.toJSONString(System.out);
    }

    @Test
    public void testToObject() {
        JSONObject jsonObject = new JSONObject(1, "bysocket", 33);
        String jsonStr = JSONUtil.toJSONString(jsonObject);
        JSONObject resultObject = JSONUtil.toObject(jsonStr, JSONObject.class);
        Assert.assertEquals(jsonObject.toString(), resultObject.toString());
    }

    @Test(expected = RuntimeException.class)
    public void testToObjectError() {
        JSONUtil.toObject("{int:1}", JSONObject.class);
    }
}

class JSONObject {
    int age;
    String name;
    Integer id;

    public JSONObject() {
    }

    public JSONObject(int age, String name, Integer id) {
        this.age = age;
        this.name = name;
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "JSONObject{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

Run Console(抛出了异常信息):

16/07/19 23:09:13 ERROR util.JSONUtil: JSON String Can't covert to Java Object!
16/07/19 23:09:13 ERROR util.JSONUtil: Java Object Can't covert to JSON String!

三、小结

相关对应代码分享在 Github 主页

请看到的Java小伙伴多交流多评论改进之。

参考 黄勇 smart

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in Java, 技术

图解 & 深入浅出JavaWeb:事务必会必知

事务,大家所熟悉的事务(Transaction),基本上会就往Spring事务靠。其实Spring事务管理基于底层数据库本身的事务处理机制。数据库事务的基础,是掌握Spring事务管理的基础。这篇总结下数据库事务。

一、数据库事务

它的思想:we are 伐木累。就是多个SQL语句(一个团队),要么所有执行success,不然就fail。

它最终的目标:数据不会被破坏。即事务操作成功,数据的结果和业务期待的结果是一致的。这也就是ACID中的一致性(Consistency)。那什么是ACID呢?

 

二、ACID

上面是思想,牛人马上根据思想建模,DBMS中数据库事务满足4各特性,即原子性、一致性、隔离性和持久性。下面一一生动解释:

a)原子性

原子是物质的最小单元,即不可再分。

例如,以MySQL为例,每一个简单的 SQL 语句即包含在一个事务中,具有原子性。这时候有人问了,那多个SQL呢?

BEGIN TRANSACTION;

INSERT INTO `test`.`city` (`state`, `country`, `name`)

VALUES

('1', 'China', 'CHINA','错误语句多了个VALUE');

INSERT INTO `test`.`city` (`state`, `country`, `name`)

VALUES

('1', 'China', 'CHINA');

COMMIT;

 

结果:执行不通过。行3-5:为一个错误SQL。行6-8:是一个正确的SQL。它们各自被包裹在各自的隐式事务中,即Read Uncommited。T-all包裹了上面具有原子性的T-1和T-2,实现了更大的原子,如下图。

2

b)一致性

终极目标:数据不会被破坏。(这不是废话?确实有点)具体说,事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。举个栗子:两句UPDATE语句,从A账户转账到B账户,不管成功失败,A和B账户的总额是不变的。

c)隔离性

隔离:表示互不干扰。事务与事务之间无法干扰,即每个事务独立,不会交叉。这样可以让多个线程并发访问数据库。如图:

3

但是聪明的小伙伴知道,如果事务完全隔离,每次只允许一个事务能访问数据库,那其他都是阻塞。会非常慢。

但是聪明的小伙伴也知道,这样会造成数据的并发问题。(是的,在下面第三节讲)。

d)持久性

数据必须持久化到数据库(存储在磁盘)中。已提交的事务,即使在提交后数据库崩溃,重启数据库时也能够根据日志对未持久化的数据进行重执行操作。(同学会问,那没提交的事务呢?那就悲剧了(>﹏<))

小结:数据的一致性是最终目标,其他特性都是其要求或手段。

 

三、隔离性中的问题:脏读、不可重复读和幻读

对应上面的隔离性,事务并发访问的时候会出现:脏读、不可重复读和幻读。案例转自勇哥博客

脏读:A事务读取了B事务未提交的更改数据。一般数据库事务默认不允许该问题出现。

比如这里查询应该是1500,现在出现了脏读。

时间 事务 A(存款) 事务 B(取款)
T1 开始事务
T2 开始事务
T3 查询余额(1000 元)
T4 取出 1000 元(余额 0 元)
T5 查询余额(0 元)
T6 撤销事务(余额恢复为 1000 元)
T7 存入 500 元(余额 500 元)
T8 提交事务

不可重复读:A事务读取了B事务已提交的更改数据。

幻读:A事务读取了B事务提交的新增数据。

上面的案例脑补吧,主要还是看下面。

 

不可重复读和幻读区别:一个更改,一个新增数据。其实两个区别在于一个是新增(insert语句),处理幻读这个操作需要加表级别的锁,将整个表锁定,防止新增数据造成幻读。另一个则是更改(update delete),这时候避免这个情况只需要添加行级锁组织该行发生变化即可。

 

四、事务隔离级别

既要求高的隔离性(安全性),又要求高并发性。这种是不可能的任务。根据各种锁的操作机制出现了一个事务隔离级别。即相同情况下的输入,不同隔离级别结果不同。为啥了,当然是在并发性和安全性的抉择。如图:

6

按着图说的,根据程序的并发性和安全性的抉择。鱼和熊掌不可兼得也~ 但分布式的时候,可以吧安全性关键的单独分布式锁。

好了,案例说了很多下面代码实战。

 

ps: 休息下,泥瓦匠的代码都会这github上~ ,这段代码地址:https://github.com/JeffLi1993/jee-component-learning

 

五 、JDBC事务实战

下面利用MYSQL JDBC驱动连接MySQL,代码如下:

public class TransactionLevels extends BaseJDBC {
    public static void main(String[] args) {
        try {
            // 加载数据库驱动
            Class.forName(DRIVER);
            // 数据库连接
            Connection conn = DriverManager.getConnection(URL,USER,PWD);
            // 数据库元数据
            DatabaseMetaData metaData = conn.getMetaData();

            // 是否支持事务
            boolean isSupport = metaData.supportsTransactions();
            System.out.println(isSupport);
            // 是否支持的事务
            boolean isSupportLevel = metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE);
            System.out.println(isSupportLevel);
            // 获取默认事务
            int defaultIsolation = metaData.getDefaultTransactionIsolation();
            System.out.println(defaultIsolation);

            /** 关闭数据库连接 */
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

第 5、7行是连接数据库

第 9 行:获取数据库元数据,这是包含数据库连接信息

第 12 行:从元数据中,判断是否支持事务

第 15 行:从元数据中,判断是否支持事务级别 TRANSACTION_SERIALIZABLE

第 18 行:这里可以看出MySQL默认支持的事务级别是 READ_COMMITTED,默认会隔离脏读。

具体源码如下:

4

因此在安全性要求不高,支持高并发的情况下,选择MySQL默认事务等级。但在安全性极高,几乎不会出现高并发情况下,选择更高的事务等级。根据上小节的图一幕了然。

 

六、补充

关于事务,还有大家熟悉的Spring事务管理、具体数据库事务的实现,推荐一本书《MySQL技术内幕InnoDB存储引擎 》。

下一篇:ThreadLocal的工作机制,揭示Spring事务同步管理器的工作原理

如以上文章或链接对你有帮助的话,别忘了分享到朋友圈,让更多的人阅读这篇文章。

Posted in Java, 技术

图解微服务架构演进

图解服务化架构演进

许久没摘记了,继续告诫自己:

要静下心来,低调多做事

前言

来自dubbo的用户手册中的一句话:
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

常规的垂直应用架构就相当于传统的那种,现阶段传统垂直架构改造的核心就是对应用做服务化改造,服务话改造使用的核心技术架构就是分布式服务框架。

其实这篇是概念上的总结,技术概念软文,纪录此文让自己更明白什么是微服务化架构。

服务化架构演进

请看下图,也来自dubbo的用户手册,图中恰恰少了微服务架构的图。
image

那什么是微服务架构呢?
先从第一个图中第一个说起吧。

1.orm – 单一应用架构

我认为是一个高内聚版本,所有功能部署在一起。数据访问框架(orm)成为关键。这个架构很少被人使用,几乎接近灭绝了吧。

优点:成本低,适合功能少又简单 缺点:很多,比如无法适应高流量,二次开发难,部署成本高

2.mvc架构 - 垂直应用架构

当访问量渐渐增大,慢慢演化成用的很多的mvc架构。虽然还是所有的功能都是部署在同一个进程中,但是可以通过双机或者前置负载均衡来实现负载分流。这样应用也可以拆分成不同的几个应用,以提升性能和效率。

此时,mvc架构用于分离前后端逻辑。一方面,有一定的模块化。另一方面,加速和方便了开发。

3.rpc架构 - 分布式服务架构

当mvc垂直应用分成不同应用时,越来越多的情况下。不可避免的事应用a与应用b之间的交互。此时将核心和公共的 业务功能抽出来,作为单独的服务,并实现前后端逻辑分离。

此时则就需要提高业务的复用及整合的分布式rpc框架,例如dubbo等。

4.soa架构 - 流动计算架构

当rpc架构中的服务越来越多时,服务的生命周期的管控,容量的评估等各种问题会出现,使服务化成为瓶颈。需要增加一个调度中心来进行对服务管控,监督等。

然后,提到关键的 --

5.微服务架构

问:什么是微服务架构?

答:它就是将功能分散到各个离散的服务中然后实现对方案的解耦。服务更原子,自治更小,然后高密度部署服务。

下面是对微服务架构的图解:

image

小结

伴随敏捷开发,持续交付,DevOps,Docker等高速发展,微服务必然是未来演进方向。加油~ 多了解吧。

Posted in Java, 技术

google collection工具包的MapMaker使用

 

摘自网上描述语段:

Google Collections中的MapMaker融合了Weak Reference线程安全高并发性能异步超时清理自定义构建元素等强大功能于一身。

常阅读优秀源代码的童鞋都知道,一般叫Maker的对象都是Builder模式,而这个MapMaker就是来”Build“Map的.

一、google collection工具包的MapMaker使用:

public static void main(String[] args) {
        /**
         * expiration(3, TimeUnit.SECONDS)设置超时时间为3秒
         */
        ConcurrentMap<String , String> map = new MapMaker().concurrencyLevel(32).softKeys().weakValues()
                .expiration(3, TimeUnit.SECONDS).makeComputingMap(
                        /**
                         * 提供当Map里面不包含所get的项,可以自动加入到Map的功能
                         * 可以将这里的返回值放到对应的key的value中
                         */
                        new Function<String, String>() {
                            public String apply(String s) {
                                return "creating " + s + " -> Object";
                            }
                        }
                );

        map.put("a","testa");
        map.put("b","testb");

        System.out.println(map.get("a"));
        System.out.println(map.get("b"));
        System.out.println(map.get("c"));

        try {
            // 4秒后,大于超时时间,缓存失效。
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(map.get("a"));
        System.out.println(map.get("b"));
        System.out.println(map.get("c"));
    }

结果如下:

testa
testb
creating c -> Object
creating a -> Object
creating b -> Object
creating c -> Object

 

二、先看下其api的相关demo片段:

// 使用案例:存储验证码
    // <String, String> == <用户唯一,验证码>
    // expiration(15, TimeUnit.MINUTES) 有效期15分钟
    ConcurrentMap<String,String> capthcaMap = new MapMaker().expiration(15, TimeUnit.MINUTES).makeMap();

    // 设置ConcurrentMap的concurrencyLevel参数 ,例如ConcurrentHashMap是用来控制其Segment数组的大小
    ConcurrentMap<String,Object> map1 = new MapMaker().concurrencyLevel(8).makeMap();

    // 构造各种不同reference作为key和value的map
    ConcurrentMap<String,Object> map2 = new MapMaker().softKeys().weakValues().makeMap();

    // 提供当Map里面不包含所get的项,可以自动加入到Map的功能
    ConcurrentMap<String,Integer> map3 = new MapMaker()
            .makeComputingMap(
                    new Function<String, Integer>() {
                        public Integer apply(String key) {
                            return 1;
                        }
                    }
            );

可以看出过了4秒后,缓存失效,所以呈现如此结果。

Posted in Java, 技术

五、网站高可用架构

Writer      :BYSocket(泥沙砖瓦浆木匠)

2016年的书 — A Year Of Books


高可用架构

    主要手段:数据和服务的冗余备份及失效转移。

42b04b4f-0228-44e1-b0a0-9a6db3392de2

负载均衡通过心跳检测监控服务器不可用。

其机制,实现服务器可用实时监控,自动转移,心跳检测。利用负载均衡

906507aa-df96-4723-ba5c-fbbd7379d290

Session集群

1.Session复制

78d9fa72-eca3-4345-885e-e6560a254126

2. Session绑定

利用负载均衡的源地址Hash算法实现。

649f979a-b05e-45ee-a0cc-f546637ed0c7

3. 利用Cookie记录Session

3269f531-2bb7-4a15-bc9c-785683dff255

4.Session服务器

667425fb-2216-4704-9e23-39072659f52b