Linux下Tomcat的使用

以下内容建立在tomcat已安装并可正常启动的前提下
之前的项目使用了SpringBoot,所以在部署的时候都是一键部署
最近负责的项目使用tomcat
其实linux上的tomcat使用与windows上类似
找到tomcat的文件路径并进入到webapps下
例如:/usr/local/tomcat/webapps
将生成的war包放进去,
等到自动编译出以项目命名的文件夹之后即可
在浏览器输入访问路径即可出现对应页面

重启的话就把原来的war包及编译出来的文件夹删除掉
重新导入war包等待编辑即可

Idea插件之五大装逼神器

TOP 5 stackoverflow
遇到报错等选中直接右键处理
说明:至于怎么处理,我也没试过,但看起来挺炫的

TOP 4 Sudoku Plugin
集成自己喜欢的游戏到Idea
说明:至于能添加什么游戏,不清楚,五子棋应该是有的

TOP 3 Nyan progress bar
将系统进度条换成彩虹进度条

TOP 2 Background Image Plus
设置idea背景图片

TOP 1 activate-power-mode OR Power mode II
这个我装了,感觉好牛逼,打代码屏幕在颤动
说明:打代码屏幕不只是在颤动

同样的代码只有你运行报错

条件一:相同的代码且是web项目
条件二:maven package正常,启动正常
条件三:点击某选项报错
今天这个异常我纠结了半天,代码是刚拉下来的,运行就报错;
我一度怀疑是队友坑我,少上传了一部分代码

试过了各种办法都没有效果
这是我注意到我使用jetty启动

于是切换了下tomcat试试
没有异常了

网上搜了下想找到原因
说法很多
还有些是tomcat报错jetty异常的
大多都没有解决方案

结束这段时间的学习计划之后,我想可以深入研究下
Tomcat和Jetty 

找到解决方案
通过这件事情我对自己的学习能力有了一个认识
在有文档或者解决方案的情况下勉强还可以
只有英文文档或者没有文档的情况下,就不行了

没有研究能力的学习能力是不真实的

Docker安装并使用MySQL(Windows下)

下载镜像
docker pull mysql

查看是否下载成功
docker images
看到mysql相关字眼则下载成功

运行 密码为123456 端口为3306
docker run --name mydb -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql

查看时候执行成功
docker ps
看到mysql相关字眼则说明正在启动

映射到主机启动
执行 docker images  看到mysql字眼
执行 docker run -itd -P mysql bash 进行映射
执行 docker ps  看到mysql映射信息,某端口到3306
执行 docker exec -it ID bash 连接到mysql镜像(ID为mysql ID号)
执行 service mysql status 看到mysql没有启动的信息
执行 service mysql start 启动mysql
执行 mysql 查看是否启动成功,此时进入mysql内

如何在外部访问?
此时用navcat连接,输入localhost及上面生成的端口号等
会报出错误:
2003——can't connect to mysql server on localhost(10061)
解决办法:
如果可以拿到my.ini文件
将bind-address = 127.0.0.1
改成
bind-address = 0.0.0.0
如果没有就加上

linux下
docker exec -it ID bash 进入容器,注意ID
cd 到/etc/mysql/mysql.conf.d/mysqld.cnf
cat mysqld.cnf 查看文件确认bind-address是否不正确
如不正确进行修改
sed 's/#bind-address/bind-address/g' mysqld.cnf>mysqlid.cnf.tmp
mv mysqld.cnf.tmp mysqld.cnf (覆盖原文件)
sed 's/127.0.0.1/0.0.0.0/g' mysqld.cnf mysqld.cnf>mysqlid.cnf.tmp
mv mysqld.cnf.tmp mysqld.cnf (覆盖原文件)

查看下文件是否修改正确

停止并删除容器
docker stop ID
docker rm ID

重新启动容器,命令在上方写过

依旧连不上,这段就当记录linux如何修改文件了...

Windows下安装Docker出现的问题

Windows下安装Docker会有很多坑的
安装成功后双击Docker Quickstart Terminal
提示Windows正在查找bash.exe...
错误原因:快捷方式的目标不正确(很可能是因为当初自定义了git的位置)
解决办法:
右键属性,找到目标栏,将值改正确即可,我的值为:
"D:\Program Files\Git\bin\bash.exe" 
--login -i "D:\Program Files\Docker Toolbox\start.sh"
打开Docker Quickstart Terminal后出现报错信息:
ERROR getting IP address...
解决办法:
 打开Oracle VM VirtualBox
 删除默认的虚拟机,如果在运行就停止后删除
 重启Docker Quickstart Terminal,会重新生成一个虚拟机
 启动完成,这个问题应该解决了
打开Docker Quickstart Terminal后出现报错信息:
ERROR creating machine:Error in ...UI-X/AMD-v enabled...
(虚拟化没有开启)
解决办法(以ThinkPad为例):
重启电脑按F1进入BIOS页面
按Tab或上下左右键移动到security
选中其下的visualizaiton选项将值改为enabled
然后F10保存恢复进入系统界面
 打开Oracle VM VirtualBox
 删除默认的虚拟机,如果在运行就停止后删除
 重启Docker Quickstart Terminal,会重新生成一个虚拟机
 启动完成,这个问题应该解决了
在cmd执行docker version出现信息有:
No connection could be made because 
the target machine actively refused it.
问题原因:
docker认为你并没有启动虚拟机
解决办法:
打开Docker Quickstart Terminal
$ docker-machine env default 会显示一串信息
$ export DOCKER_MACHINE_NAME="default"
打开Docker Quickstart Terminal后没有出现error相关字眼
且执行docker run ‘hello-world’出现了一大串信息
且没有出现error相关字眼则说明安装成功!

Java 启动异常

今天项目一直启动不了,报错信息如下:
java.lang.NoSuchMethodError: 
org.springframework.boot.builder.SpringApplicationBuilder
.([Ljava/lang/Object;)V
一下省略
根据报错信息可知,是找不到方法的异常;
手写代码SpringApplicationBuilder这个类是存在的
造成这样错误的原因:
切换pom文件中配置的版本,idea所识别到的配置发生了冲突
也就是通常所说的jar包冲突

解决办法:
打开maven project视图
点击刷新按钮即可
重新启动,项目正常运行
这不是一个很难解决的错误,但却耽误了很长时间,在此记录,希望不再有类似情况发生

java 实现三角形平行四边形等

记得刚开始学习编程的时候,有段时间一直在练习三角形平行四边形这些;
但是这些东西过段时间就会逻辑不清晰,今天回顾了一下,顺便做个记录.
System.out.println("直角在左下的等腰直角三角形");
        for (int i = 1; i <= 9; i++) {
            String row = "";
            for (int j = 1; j <= i; j++) {
               row += " * "; 
            }
             System.out.println(row); 
            } 
System.out.println("--------------------------------------------------------------"); 
            System.out.println("直角在左上的等腰直角三角形"); 
            for (int i = 9; i >= 1; i--) {
            String row = "";
            for (int j = 1; j <= i; j++) {
                row += " * "; 
            } 
            System.out.println(row); 
            } 
System.out.println("--------------------------------------------------------------"); 
            System.out.println("直角在右上的等腰直角三角形"); 
            for (int i = 9; i >= 1; i--) {
            String row = "";
            for (int ij = 9; ij > i; ij--) {
                row += "   ";
            }
            for (int j = 1; j <= i; j++) {
                row += " * ";
            }
            System.out.println(row);
        }
        System.out.println("--------------------------------------------------------------");

        System.out.println("直角在右下的等腰直角三角形");
        for (int i = 1; i <= 9; i++) { 
           String row = ""; 
          for (int ij = 9; ij > i; ij--) {
                row += "   ";
            }
            for (int j = 1; j <= i; j++) {
                row += " * ";
            }
            System.out.println(row);
        }
        System.out.println("--------------------------------------------------------------");

        System.out.println("底边在下的等腰三角形");
        for (int i = 1; i <= 7; i++) { 
            String row = ""; 
            for (int j = 7; j > i; j--) {
                row += "   ";
            }
            for (int ij = 1; ij <= (i * 2 - 1); ij++) {
                row += " * ";
            }
            System.out.println(row);
        }
        System.out.println("--------------------------------------------------------------");

        System.out.println("等边三角形");
        for (int i = 1; i <= 6; i++) { 
           String row = ""; 
            for (int j = 6; j > i; j--) {
                row += " ";
            }
            for (int ij = 1; ij <= i; ij++) {
                if (ij % 2 != 0) {
                    row += "*";
                } else {
                    row += " * ";
                }
            }
            System.out.println(row);
        }
        System.out.println("--------------------------------------------------------------");

        System.out.println("向右偏的平行四边形");
        for (int i = 1; i <= 6; i++) { 
            String row = ""; 
            for (int j = 6; j > i; j--) {
                row += "   ";
            }
            for (int ij = 1; ij <= 6; ij++) {
                row += " * ";
            }
            System.out.println(row);
        }
        System.out.println("--------------------------------------------------------------");

        System.out.println("空心平行四边形");
        //上半部分逻辑
        for (int i = 1; i <= 6; i++) { 
         String row = ""; 
        //左线前的空格逻辑 
         for (int j = 6; j > i; j--) {
                row += "   ";
            }
            //左线逻辑
            for (int ij = 1; ij < 2; ij++) {
                row += " * ";
            }
            //中心逻辑
            for (int ij2 = 1; ij2 < (i * 2 - 2); ij2++) {
                row += "   ";
            }
            //右线逻辑
            for (int ij = 1; ij < 2; ij++) {
                if (i != 1) {
                    row += " * ";
                }
            }
            System.out.println(row);
        }
        //下半部分逻辑
        for (int i = 1; i <= 5; i++) {
            String row = "";
            //左线前空格逻辑
            for (int j = 1; j <= i; j++) {
                row += "   ";
            }
            //左线逻辑
            for (int j = 1; j < 2; j++) {
                row += " * ";
            }
            //中间空白逻辑
            for (int ij = 1; ij < (5 - i) * 2; ij++) {
                row += "   ";
            }
            //右线逻辑
            for (int j = 1; j < 2; j++) {
                if (i != 5) {
                    row += " * ";
                }
            }
            System.out.println(row);
        }
        System.out.println("--------------------------------------------------------------");
测试结果如下:
直角在左下的等腰直角三角形
 * 
 * * 
 * * * 
 * * * * 
 * * * * * 
 * * * * * * 
 * * * * * * * 
 * * * * * * * * 
 * * * * * * * * * 
--------------------------------------------------------------
直角在左上的等腰直角三角形
 * * * * * * * * * 
 * * * * * * * * 
 * * * * * * * 
 * * * * * * 
 * * * * * 
 * * * * 
 * * * 
 * * 
 * 
--------------------------------------------------------------
直角在右上的等腰直角三角形
 * * * * * * * * * 
 * * * * * * * * 
 * * * * * * * 
 * * * * * * 
 * * * * * 
 * * * * 
 * * * 
 * * 
 * 
--------------------------------------------------------------
直角在右下的等腰直角三角形
 * 
 * * 
 * * * 
 * * * * 
 * * * * * 
 * * * * * * 
 * * * * * * * 
 * * * * * * * * 
 * * * * * * * * * 
--------------------------------------------------------------
底边在下的等腰三角形
 * 
 * * * 
 * * * * * 
 * * * * * * * 
 * * * * * * * * * 
 * * * * * * * * * * * 
 * * * * * * * * * * * * * 
--------------------------------------------------------------
等边三角形
     *
    * * 
   * * *
  * * * * 
 * * * * *
* * * * * *  
--------------------------------------------------------------
向右偏的平行四边形
           * * * * * * 
         * * * * * * 
       * * * * * * 
     * * * * * * 
   * * * * * * 
 * * * * * * 
--------------------------------------------------------------
空心平行四边形
           * 
         *   * 
       *       * 
     *           * 
   *               * 
 *                   * 
   *               * 
     *           * 
       *       * 
         *   * 
           * 
--------------------------------------------------------------

Spring提供的JdbcTemplate简单使用

所需maven配置

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-jdbc</artifactId>
 <version>4.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
 <version>4.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>6.0.6</version>
</dependency>
// JDBC模板依赖于连接池来获得数据的连接,所以必须先要构造连接池
         DriverManagerDataSource dataSource = new DriverManagerDataSource();
         dataSource.setDriverClassName("com.mysql.jdbc.Driver");
         dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
         dataSource.setUsername("root");
         dataSource.setPassword("123456");
 
         // 创建JDBC模板
         JdbcTemplate jdbcTemplate = new JdbcTemplate();
         // 这里也可以使用构造方法
         jdbcTemplate.setDataSource(dataSource);
 
         // sql语句
         String sql = "select count(*)  from user";
         Long num = (long) jdbcTemplate.queryForObject(sql, Long.class);
 
         System.out.println(num);

Java对JDBC结果集ResultSet的处理

代码中的Json及JsonList为本人重新创建的类,参考代码时可转为自己常用的相应类
/**
     * 将结果集转化为json对象
     *
     * @param resultSet 操作数据库后的结果集
     * @return json类型的结果集
     * @throws SQLException sql异常
     */
    protected static Json getResultSetByJson(ResultSet resultSet) throws SQLException {
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        int columnCount = resultSetMetaData.getColumnCount();
        Json json = new Json();
        while (resultSet.next()) {
            for (int i = 1; i <= columnCount; i++) {
                String key = resultSetMetaData.getColumnLabel(i);
                String value = resultSet.getString(key);
                json.put(key, value);
            }
        }
        return json;
    }

    /**
     * 将结果集转化为JosnList对象
     *
     * @param resultSet 结果集
     * @return jsonlist类型的结果集
     * @throws SQLException sql异常
     */
    protected static JsonList getResultSetByJsonList(ResultSet resultSet) throws SQLException {
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        int columnCount = resultSetMetaData.getColumnCount();
        JsonList jsonList = new JsonList();
        while (resultSet.next()) {
            Json json = new Json();
            for (int i = 1; i <= columnCount; i++) {
                String key = resultSetMetaData.getColumnLabel(i);
                String value = resultSet.getString(key);
                json.put(key, value);
            }
            jsonList.add(json);
        }
        return jsonList;
    }

    /**
     * 将结果集映射为实体类
     *
     * @param resultSet 结果集
     * @param t         实体
     * @param <T>       泛型
     * @return 实体类型的结果集
     * @throws SQLException              sql异常
     * @throws InvocationTargetException 反射异常
     * @throws IllegalAccessException    反射时访问私有成员权限不足导致的异常
     * @throws NoSuchMethodException     得不到方法异常
     */
    protected static <T> T getResultSetByT(ResultSet resultSet, T t) throws SQLException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        //获取method方法
        Method[] methods = t.getClass().getMethods();

        //获取ResultSet列名
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        int columnCount = resultSetMetaData.getColumnCount();
        String[] columnNames = new String[columnCount];
        for (int i = 0; i < columnCount; i++) {
            columnNames[i] = resultSetMetaData.getColumnLabel(i + 1);
        }
        //遍历ResultSet
        while (resultSet.next()) {
            for (int i = 0; i < columnNames.length; i++) {
                //取得set方法
                String setMethodName = "set" + columnNames[i];
                //遍历method方法
                for (int j = 0; j < methods.length; j++) {
                    if (methods[j].getName().equalsIgnoreCase(setMethodName)) {
                        setMethodName = methods[j].getName();
                        Object value = resultSet.getObject(columnNames[i]);

                        //通过set方法赋值到实体
                        try {
                            if (value != null) {
                                //实体字段类型和RsultSet一样时
                                Method method = t.getClass().getMethod(setMethodName, value.getClass());
                                method.invoke(t, value);
                            }
                        } catch (Exception e) {
                            //实体字段类型和ResultSet不一样时,转为String
                            Method method = t.getClass().getMethod(setMethodName, String.class);
                            method.invoke(t, value.toString());
                        }
                    }
                }
            }
        }
        return t;
    }

Java实现区块链小实例

通过本文,你将可以做到:

创建自己的区块链
理解 hash 函数是如何保持区块链的完整性的
如何创造并添加新的块
多个节点如何竞争生成块
通过浏览器来查看整个链
所有其他关于区块链的基础知识
但是,对于比如工作量证明算法(PoW)以及权益证明算法(PoS)
这类的共识算法文章中将不会涉及。同时为了让你更清楚得查看
区块链以及块的添加,我们将网络交互的过程简化了,关于 P2P 
网络比如“对等网络”等内容将在将来的文章中讲解。

让我们开始吧!

设置

我们假设你已经具备一点 Java 语言的开发经验,以及maven项目构建经验。
在安装和配置 Java 开发环境后之后,我们新建maven项目,在pom中增加一些依赖:
<!--超小型web框架-->
  <dependency>
    <groupId>com.sparkjava</groupId>
    <artifactId>spark-core</artifactId>
    <version>${spark.version}</version>
  </dependency>
Spark-web Framework是一个基于jetty的超小型框架,我们用它来写http访问的请求处理。
 <dependency>
   <groupId>commons-codec</groupId>
   <artifactId>commons-codec</artifactId>
   <version>${commons.codec.version}</version>
 </dependency>
这个通用包拥有几乎所有加解密算法及常规操作
 <dependency>
 <groupId>com.google.code.gson</groupId>
 <artifactId>gson</artifactId>
 <version>${gson.version}</version>
 </dependency>
Google的json包,当然你可以使用你喜欢的其他json包。

最后,增加log相关的包
<!--log start-->
<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>${log4j.version}</version>
 </dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
 <version>${slf4j.version}</version>
 </dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>${slf4j.version}</version>
 </dependency>
<!-- log end -->
 相关版本属性设置
<properties>
<commons.codec.version>1.9</commons.codec.version>
<spark.version>2.6.0</spark.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.17</log4j.version>
<gson.version>2.8.2</gson.version>
</properties>

数据模型

我们来定义一个Block类,它代表组成区块链的每一个块的数据模型:
public class Block {
    /**是这个块在整个链中的位置*/
    private int index;
    /**显而易见就是块生成时的时间戳*/
    private String timestamp;
    /**虚拟资产。我们要记录的数据*/
    private int vac;
    /**是这个块通过 SHA256 算法生成的散列值*/
    private String hash;
    /**指向前一个块的 SHA256 散列值*/
    private String prevHash;
    /** getters and setters**/ 
}
接着,我们再定义一个结构表示整个链,
最简单的表示形式就是一个 Block 的 顺序表:
ArrayList<Block> blockChain
我们使用散列算法(SHA256)来确定和维护链中块
和块正确的顺序,确保每一个块的 PrevHash 值等于
前一个块中的 Hash 值,这样就以正确的块顺序构建出链:

 [ index:0| hash:"xxxw"| preHash:""] - [ index:1| hash:"xxxx"| 
preHash:"xxxw"] - [ index2| hash:"xxxy"| preHash:"xxxx"]

散列和生成块

我们为什么需要散列?主要是两个原因:

在节省空间的前提下去唯一标识数据。散列是用整个块的数据计算得出,
在我们的例子中,将整个块的数据通过 SHA256 计算成一个定长不可伪造的字符串。
维持链的完整性。通过存储前一个块的散列值,我们就能够确保每个块在链中的正确顺序。
任何对数据的篡改都将改变散列值,同时也就破坏了链。以我们从事的医疗健康领域为例,
比如有一个恶意的第三方为了调整“人寿险”的价格,而修改了一个或若干个块中的代表不
健康的 VAC 值,那么整个链都变得不可信了。
我们接着写一个函数,用来计算给定的数据的 SHA256 散列值:
public static String calculateHash(Block block) {
       String record = (block.getIndex()) 
         + block.getTimestamp() 
         + (block.getVac()) 
         + block.getPrevHash();
       return SHA256.crypt(record);
}
接下来我们就能得到一个生成块的函数:

public static Block generateBlock(Block oldBlock, int vac) {
       Block newBlock = new Block();
       newBlock.setIndex(oldBlock.getIndex() + 1);
       newBlock.setTimestamp(
         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
          .format(new Date()));
       newBlock.setVac(vac);
       newBlock.setPrevHash(oldBlock.getHash());
       newBlock.setHash(calculateHash(newBlock));
       return newBlock;
}
其中,Index 是从给定的前一块的 Index 递增得出,
时间戳是直接通过 new Date() 函数来获得的,
Hash 值通过前面的 calculateHash 函数计算得出,
PrevHash 则是给定的前一个块的 Hash 值。

校验块

搞定了块的生成,接下来我们需要有函数帮我们判断一
个块是否有被篡改。检查 Index 来看这个块是否正确得递增,
检查 PrevHash 与前一个块的 Hash 是否一致,再来通过 
calculateHash 检查当前块的 Hash 值是否正确。
通过这几步我们就能写出一个校验函数:

public static boolean isBlockValid(Block newBlock, Block oldBlock) {
       if (oldBlock.getIndex() + 1 != newBlock.getIndex()) {
           return false;
       }
       if (!oldBlock.getHash().equals(newBlock.getPrevHash())) {
           return false;
       }
       if (!calculateHash(newBlock).equals(newBlock.getHash())) {
           return false;
       }
       return true;
    }
除了校验块以外,我们还会遇到一个问题:
两个节点都生成块并添加到各自的链上,那我们应该以谁为准?
这里的细节我们留到下一篇文章,这里先让我们记住一个原则:始终选择最长的链。
[block 1] -> [block 2] -> [block 3] -> [block 4] -> [block 5] -> 认可
 [block 1] -> [block 2] -> [block 3] -> [block 4] ->  丢弃
通常来说,更长的链表示它的数据(状态)是更加新的,
所以我们需要一个函数能帮我们将本地的过期的链切换成最新的链:
public void replaceChain(ArrayList<Block> newBlocks) {
       if (newBlocks.size() > blockChain.size()) {
           blockChain = newBlocks;
       }
    }
到这一步,我们基本就把所有重要的函数完成了。
接下来,我们需要一个方便直观的方式来查看我们的链,包括数据及状态。
通过浏览器查看 web 页面可能是最合适的方式。

Web 服务

借助 Spark Web Framework,来完成我们的 web 服务,代码如下:
public static void main(String[] args) {
       // port(5678); //默认端口是4567,你可以设置别的端口
}
OK,完成,对,你没看错,就是一个空的main方法,就可以了。
接下来我们定义不同 endpoint 以及对应的 handler。
例如,对“/”的 GET 请求我们可以查看整个链,对“/”的 POST 请求可以创建一个新的块。

GET 请求的 handler:
get("/", (q, a) ->{return gson.toJson(blockChain)});
为了简化,我们直接以 JSON 格式返回整个链,
你可以在浏览器中访问 localhost:4567 或者 127.0.0.1:4567 来查看
POST 请求的 handler 稍微有些复杂,我们先来定义一下 POST 请求的 payload:
public class Message {
    private int vac;
//getters and setters
}
再看看 handler 的实现:
post("/", (q, a) -> {
String body = request.body();
              Message m = gson.fromJson(body, Message.class);
              if (m == null) {
                  return "vac is NULL";
              }
              int vac = m.getVac();
              Block lastBlock = blockChain.get(blockChain.size() - 1);
              Block newBlock = generateBlock(lastBlock, vac);
              if (isBlockValid(newBlock, lastBlock)) {
                  blockChain.add(newBlock);
                  LOGGER.debug(gson.toJson(blockChain));
              } else {
                  return "HTTP 500: Invalid Block Error";
              }
              return "success!";
});
我们的 POST 请求体中可以使用上面定义的 payload,比如:
{"vac":7500}
还记得前面我们写的 generateBlock 这个函数吗?
它接受一个“前一个块”参数,和一个 VAC 值。
POST handler 接受请求后就能获得请求体中的 VAC 值,
接着借助生成块的函数以及校验块的函数就能生成一个新的块了!

除此之外,你也可以:

使用new GsonBuilder().setPrettyPrinting().create()
这个函数可以以非常美观和方便阅读的方式将数据json化打印在控制台里,方便调试。
测试 POST 请求时,可以使用 POSTMAN 这个 chrome 插件,相比 curl它更直观和方便。
也可以使用RESTClient这个FireFox插件。

快要大功告成了

接下来,我们把这些关于区块链的函数,web 服务的函数“组装”起来
:最重要的是,我们需要产生第一个块(创世块),来作为区块链的头。
//创世块
       Block genesisBlock = new Block();
       genesisBlock.setIndex(0);
       genesisBlock.setTimestamp(
         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
         .format(new Date()));
       genesisBlock.setVac(0);
       genesisBlock.setPrevHash("");
       genesisBlock.setHash(calculateHash(genesisBlock));
       blockChain.add(genesisBlock);
这里的 genesisBlock (创世块)是 main 函数中最重要的部分,
通过它来初始化区块链,毕竟要有一个开始,第一个块的 PrevHash 是空的

让我们来启动它

在终端中,我们可以看到 web 服务器启动的日志信息,并且打印出了创世块的信息:

[INFO ] 2018-02-08 10:58:26 SparkWeb@(SparkWeb.java:132):[
{
"index": 0,
"timestamp": "2018-02-08 10:58:25",
"vac": 0,
"hash": "7c2d2db62a82ac8aa3d843ff837c604d8bd17800f4c466d472c5df185b8967fa",
"prevHash": ""
}
]
[INFO ] 2018-02-08 10:58:26 Log@(Log.java:192):Logging initialized @1267ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO ] 2018-02-08 10:58:26 EmbeddedJettyServer@(EmbeddedJettyServer.java:127):== Spark has ignited ...
[INFO ] 2018-02-08 10:58:26 EmbeddedJettyServer@(EmbeddedJettyServer.java:128):>> Listening on 0.0.0.0:4567
[INFO ] 2018-02-08 10:58:26 Server@(Server.java:372):jetty-9.4.4.v20170414
[INFO ] 2018-02-08 10:58:26 DefaultSessionIdManager@(DefaultSessionIdManager.java:364):DefaultSessionIdManager workerName=node0
[INFO ] 2018-02-08 10:58:26 DefaultSessionIdManager@(DefaultSessionIdManager.java:369):No SessionScavenger set, using defaults
[INFO ] 2018-02-08 10:58:26 HouseKeeper@(HouseKeeper.java:149):Scavenging every 600000ms
[INFO ] 2018-02-08 10:58:27 AbstractConnector@(AbstractConnector.java:280):Started ServerConnector@4c7573c5{HTTP/1.1,[http/1.1]}{0.0.0.0:4567}
[INFO ] 2018-02-08 10:58:27 Server@(Server.java:444):Started @1669ms
接着我们打开浏览器,访问 http://localhost:4567 这个地址,
我们可以看到页面中展示了当前整个区块链的信息(当然,目前只有一个创世块):

{
"index": 0,
"timestamp": "2018-02-08 10:58:25",
"vac": 0,
"hash": "7c2d2db62a82ac8aa3d843ff837c604d8bd17800f4c466d472c5df185b8967fa",
"prevHash": ""
}
接着,我们再通过 RESRClient来发送一些 POST 请求:post http://localhost:4567/  {"vac":15} [send];
或者使用curl命令:curl -X POST -i http://localhost:4567/ --data '{"vac":125}'。
刷新刚才的http://localhost:4567 页面,现在的链中多了一个块,正是我们刚才生成的,同时可以看到,块的顺序和散列值都正确。
源码:https://github.com/Mignet/blockchain

下一步

刚刚我们完成了一个自己的区块链,虽然很简单很简陋,
但它具备块生成、散列计算、块校验等基本能力。
接下来你就可以继续深入的学习区块链的其他重要知识,
比如工作量证明、权益证明这样的共识算法,或者是智能合约、Dapp、侧链等等。
当然,最重要的一点,作为去中心化的技术,维护一个在启动时可以连接的对等节点列表,
进行peer to peer的通讯也是区块链技术必不可少的核心部分。

文章来源:https://www.cnblogs.com/mignet/p/120_line_java_blockchain.html