Code Monkey home page Code Monkey logo

blog's People

Contributors

charlemaznable avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

blog's Issues

拜占庭将军问题(The Byzantine Generals Problem)

曾经的拜占庭国土辽阔,为了抵御来自各个方向的敌人,军队之间分隔很远,他们之间只能通过信使互相传递消息。
一场新的战役即将爆发,有5支拜占庭军队要共同进退,5个将军都是平级的,他们要怎么达成一起进攻或者一起撤退的共识呢?

最简单的办法就是投票,每个将军都派出信使将自己的意见发给其他四个将军。
对每个将军来说,算上自己的票数,如果进攻票超过2票就会发起进攻,如果少于或者等于2票就撤退。
这是最简单的情况,很合逻辑。那假如是下面的情况呢?

1. 5个将军中有一个是奸细,其他4个将军有两个赞成进攻,2个反对,这个将军给其中2个发去了进攻的意见,给另外2个却是撤退,结果是2支军队进攻,2支军队撤退,没有达成共识。

2. 可能有一个或者多个信使被暗杀,或者被策反。

在这两种情况下,投票的结果不能代表大多数将军的意见。

以上,可以总结出拜占庭将军问题:在可能有叛徒的情况下,其余忠诚的将军如何不受其影响达成一致的协议?

参考原文

Go Proxy

首先是因为有墙

$ export GOPROXY="https://goproxy.io"

的确好用, golang.org/x/...这些包都能go get到了.

然后提交了自己的工具包

release了新版本, 却怎么都go get不到

$ go: get ...... unexpected end of JSON input

见了鬼了, 辣鸡modules

各种尝试, 一顿搜索, 最后:

$ export GOPROXY=""

终于能go get到了.

墙才是最终的辣鸡;

Go 开发笔记

go1.13+ 在init中使用了flag, test时报错: flag provided but not defined: -test.XXX

golang 13 changed the order of the test initializer

添加代码

var _ = func() bool {
    testing.Init()
    return true
}()

或直接在flag.Parse()前添加

testing.Init()

golang/go#31859 (comment)

空方法编译报错?!

这是一个有趣的事情: Java的注释会影响代码的编译

这是一种特殊的情况:
编译器会解析Unicode字符,可能导致代码会在编译时报错

public class CompileError {
    public static void main(String[] args) {
        // \u000d 运行编译报错
    }
}
CompileError.java:3: error: not a statement
       // \u000d 运行编译报错
                 ^
CompileError.java:3: error: ';' expected
       // \u000d 运行编译报错
                       ^
2 errors
Error: Could not find or load main class CompileError
Caused by: java.lang.ClassNotFoundException: CompileError

参考来源: SaminZou/study-prj: Q3/AnnotationProblem.java

使用JUnit5进行Spring Controller/Interceptor/Filter单元测试

添加maven依赖
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>${junit-jupiter.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>${junit-jupiter.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
    <scope>test</scope>
</dependency>
单元测试类添加注解
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = YourConfiguration.class)
@WebAppConfiguration // 标注为WebApp测试
@TestInstance(Lifecycle.PER_CLASS) // 为在@BeforeAll方法中使用自动注入的Bean, 方法需为非static, 测试类需添加此注解
public class YourTest {
// ...
}
初始化MockMvc
private static MockMvc mockMvc;

@BeforeAll
public void setup() {
    mockMvc = MockMvcBuilders.standaloneSetup(yourController) // 可添加多个Controller
            .addMappedInterceptors(new String[]{"/**"}, yourInterceptor)
            .addFilters(yourFilter).build();
}
单元测试用例
@Test
public void testSample() {
    val response = mockMvc.perform(MockMvcRequestBuilders.get("/your-req-path")
            .param("key", "value"))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andReturn().getResponse();
    // some assertions
}

浅尝HyperLogLog

看到Hazelcast的分布式数据结构介绍中, 提到了Cardinality Estimator:实现了HyperLogLog算法的数据结构, 于是顺便了解一下

什么是HyperLogLog.

HyperLogLog是针对大数据统计存储应用场景下的知名算法。

HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现。

浅尝Java Instrumentation

昨天晚上, 老大 @bingoohuang提了个问题:

玩转JVM从实际需求出发

提取java中的ArrayList类的源代码
在add方法中,添加最大容器数量限制I(比如最大1万个),超过则抛出异常
替换系统类库中的实现

略微研究, 浅尝辄止, 在这里分享一下答案.

浅尝辄止

汉语成语,拼音是qiǎn cháng zhé zhǐ,意思是略微尝试一下就停下来。
指不深入钻研。
出自清·彭养鸥《黑籍冤魂》第二十四回:
    “此物非不可尝,苟文人墨客,浅尝辄止,用以悦性陶情,有何不可?”

CircularFifoQueue vs EvictingQueue

最近在写一个小玩意, 需要一个FiFo的队列, 其大小是一定的, 往队列中插值时, 如果队列已经填满了, 则移出队列的第一个元素: 即所谓的有限大小的队列.

选项一: org.apache.commons.collections4.queue.CircularFifoQueue

maven依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

注意事项:

  • add/remove/peek/poll/offer操作的时间复杂度为O(1)
  • 其他操作的时间复杂度为O(n)或更低
  • 新增null对象/空队列做remove操作会抛出异常
  • 非线程安全

选线二: com.google.common.collect.EvictingQueue

maven依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>

注意事项:

  • 其内部实现为托管ArrayDeque实现
  • 除remove等操作的时间复杂度为O(n)外, 其他常用操作的时间复杂度为O(1)
  • 新增null对象/空队列做remove操作会抛出异常
  • 非线程安全

做一些摘抄

疑行无名,疑事无功。

且夫有高人之行者,固见非於世;
有独知之虑者,必见敖於民。

愚者闇於成事,
知者见於未萌。

民不可与虑始而可与乐成。

论至德者不和於俗,
成大功者不谋於众。

是以圣人苟可以彊国,不法其故;
苟可以利民,不循其礼。

——《史记·商君列传》

Lombok it!

最近做了个小玩意儿, 要在后端的Java应用里嵌码埋点日志.

手动埋点日志比较简单, 思路就是创建一个独立的logback-LoggerContext, 获取一个干净的logger, 单独配置其Appender处理日志, 只要按slf4j的接口封装出API, 就能基本满足大部分场景的日志需求.

void log(org.slf4j.event.Level level, String msg);
void log(org.slf4j.event.Level level, String format, Object arg);
void log(org.slf4j.event.Level level, String format, Object arg1, Object arg2);
void log(org.slf4j.event.Level level, String format, Object... argArray);
void log(org.slf4j.event.Level level, String msg, Throwable t);

难点在于自动埋点.

第一时间想起的是Instrumentation, 之前也尝试过, [#9 浅尝Java Instrumentation], 只要配合ASM或者Javassist就能实现.
但是有两点不爽的: 一是目标应用启动时要添加agent参数, 而且附带需要分发埋点工具的jar包; 二是已经玩过了, 炒冷饭有点难受.

这时候就想起来一直很好奇的lombok, 它可以SneakyThrows, 可以Cleanup, 还可以Slf4j, 它是如何完成那些神奇的功能的呢?

还是要多学多练.

Java Map 遍历

  • 增强for循环

keySet()

for (String key : map.keySet()) {
    System.out.println(key + " : " + map.get(key));
}

entrySet()

for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " : " + entry.getValue());
}
  • 迭代器

keySet()

Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
    String key = iterator.next();
    System.out.println(key + " : " + map.get(key));
}

entrySet()

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, String> entry = iterator.next();
    System.out.println(entry.getKey() + " : " + entry.getValue());
}

分别对四种遍历方式进行10W次迭代,比较用时。

  • 1st
增强for循环,keySet迭代 -> 14 ms
增强for循环,entrySet迭代 -> 7 ms
迭代器,keySet迭代 -> 9 ms
迭代器,entrySet迭代 -> 3 ms
  • 2nd
增强for循环,keySet迭代 -> 12 ms
增强for循环,entrySet迭代 -> 11 ms
迭代器,keySet迭代 -> 9 ms
迭代器,entrySet迭代 -> 7 ms
  • 3rd
增强for循环,keySet迭代 -> 18 ms
增强for循环,entrySet迭代 -> 18 ms
迭代器,keySet迭代 -> 8 ms
迭代器,entrySet迭代 -> 4 ms
  • 平均
增强for循环,keySet迭代 -> 15 ms
增强for循环,entrySet迭代 -> 12 ms
迭代器,keySet迭代 -> 9 ms
迭代器,entrySet迭代 -> 5 ms

分别对四种遍历方式进行100W次迭代,比较用时。

  • 1st
增强for循环,keySet迭代 -> 32 ms
增强for循环,entrySet迭代 -> 35 ms
迭代器,keySet迭代 -> 33 ms
迭代器,entrySet迭代 -> 18 ms
  • 2nd
增强for循环,keySet迭代 -> 32 ms
增强for循环,entrySet迭代 -> 23 ms
迭代器,keySet迭代 -> 24 ms
迭代器,entrySet迭代 -> 34 ms
  • 3rd
增强for循环,keySet迭代 -> 31 ms
增强for循环,entrySet迭代 -> 24 ms
迭代器,keySet迭代 -> 27 ms
迭代器,entrySet迭代 -> 25 ms
  • 平均
增强for循环,keySet迭代 -> 32 ms
增强for循环,entrySet迭代 -> 27 ms
迭代器,keySet迭代 -> 28 ms
迭代器,entrySet迭代 -> 26 ms

分别对四种遍历方式进行1000W次迭代,比较用时。

  • 1st
增强for循环,keySet迭代 -> 151 ms
增强for循环,entrySet迭代 -> 92 ms
迭代器,keySet迭代 -> 101 ms
迭代器,entrySet迭代 -> 83 ms
  • 2nd
增强for循环,keySet迭代 -> 108 ms
增强for循环,entrySet迭代 -> 74 ms
迭代器,keySet迭代 -> 102 ms
迭代器,entrySet迭代 -> 150 ms
  • 3rd
增强for循环,keySet迭代 -> 266 ms
增强for循环,entrySet迭代 -> 269 ms
迭代器,keySet迭代 -> 140 ms
迭代器,entrySet迭代 -> 124 ms
  • 平均
增强for循环,keySet迭代 -> 175 ms
增强for循环,entrySet迭代 -> 145 ms
迭代器,keySet迭代 -> 114 ms
迭代器,entrySet迭代 -> 119 ms

参考文档: Java map 详解 - 用法、遍历、排序、常用API等
其中关于Map遍历的结论:

总结
1. 增强for循环使用方便,但性能较差,不适合处理超大量级的数据。
2. 迭代器的遍历速度要比增强for循环快很多,是增强for循环的2倍左右。
3. 使用entrySet遍历的速度要比keySet快很多,是keySet的1.5倍左右。

多次实测结果表明: for循环遍历Map虽的确略慢于迭代器遍历, 但是实际相差并没有其结论那么大.

注解到底是什么

今天写测试用例的时候, 忽然产生了一个疑问: Java里的注解到底是什么?

首先, 注解都是继承自java.lang.annotation.Annotation接口的@interface, 那么它是一个类吗? 或者是一个接口?

注解看起来更 一个接口, 因为可以定义一个类去implements一个注解, 实现注解定义的方法和一个特殊的方法:

Class<? extends Annotation> annotationType();

extends一个注解, 编译则会报错.

那么, 注解是一个 继承了Annotation接口的接口 吗?

好像又不太对, 因为不能定义一个注解继承其他的接口了, 编译器是如何限制这样一个特殊的接口只能被单继承的呢?

或者换一个问题: 能不能不使用@interface关键字, 仅使用常规的定义语法, 来定义一个注解呢?

把这个问题记在这里, 念念不忘, 必有回响.

缓存风险

缓存不是多多益善,它属于有利有弊,是真正到必须使用时才考虑的解决方案。

缓存穿透

如果查询的数据在数据源中根本不存在的话,缓存里自然也不会有,这类请求的流量每次都不会命中,每次都会触及到末端的数据源,缓存就起不到缓解压力的作用了,这种查询不存在数据的现象被称为缓存穿透。

为了解决缓存穿透,通常会采取下面两种办法:

  • 对于业务逻辑本身就不能避免的缓存穿透,可以约定在一定时间内对返回为空的 Key 值依然进行缓存(注意是正常返回但是结果为空,不应把抛异常的也当作空值来缓存了),使得在一段时间内缓存最多被穿透一次。
  • 对于恶意攻击导致的缓存穿透,通常会在缓存之前设置一个布隆过滤器来解决。如果布隆过滤器给出的判定结果是请求的数据不存在,那就直接返回即可,连缓存都不必去查。

缓存击穿

如果缓存中某些热点数据忽然因某种原因失效了,譬如典型地由于超期而失效,此时又有多个针对该数据的请求同时发送过来,这些请求将全部未能命中缓存,都到达真实数据源中去,导致其压力剧增,这种现象被称为缓存击穿。

要避免缓存击穿问题,通常会采取下面的两种办法:

  • 加锁同步,以请求该数据的 Key 值为锁,使得只有第一个请求可以流入到真实的数据源中,其他线程采取阻塞或重试策略。
  • 热点数据由代码来手动管理,直接由开发者通过代码来有计划地完成更新、失效,避免由缓存的策略自动管理。

缓存雪崩

不是针对单个热点数据的大量请求,而是由于大批不同的数据在短时间内一起失效,导致了这些数据的请求都击穿了缓存到达数据源,同样令数据源在短时间内压力剧增。

要避免缓存雪崩问题,通常会采取下面的三种办法:

  • 提升缓存系统可用性,建设分布式缓存的集群。
  • 启用透明多级缓存,各个服务节点一级缓存中的数据通常会具有不一样的加载时间,也就分散了它们的过期时间。
  • 将缓存的生存期从固定时间改为一个时间段内的随机时间。

缓存污染

缓存污染是指缓存中的数据与真实数据源中的数据不一致的现象,即缓存和数据源间无法保证最终的一致性。

为了尽可能的提高使用缓存时的一致性,已经总结不少更新缓存可以遵循设计模式,譬如 Cache Aside、Read/Write Through、Write Behind Caching 等。其中最简单、成本最低的 Cache Aside 模式是指:

  • 读数据时,先读缓存,缓存没有的话,再读数据源,然后将数据放入缓存,再响应请求。
  • 写数据时,先写数据源,然后失效(而不是更新)掉缓存。

其中在写入缓存时,有必要专门强调两点:

  • 先后顺序是先数据源后缓存。
  • 应当失效缓存,而不是去尝试更新缓存。

Cache Aside 模式也会出现缓存中回填的内容与数据源的实际数据不一致的情况。但这种情况的概率是很低的,所以其仍然是以低成本更新缓存,并且获得相对可靠结果的解决方案。

——《凤凰架构:构建可靠的大型分布式系统》 > 架构师的视角 > 透明多级分流系统 > 服务端缓存 > 缓存风险

不动点组合子

不动点组合子(Fixed-point combinator, 或不动点算子, 使用FIX表示)是计算其他函数的一个不动点的高阶函数.

不动点算子具有以下特性, 对于任何函数f都有:

FIX f = f (FIX f)

不动点组合子中最有名的(也可能是最简单的)是Y组合子:

Y = λf. (λx. f (x x)) (λx. f (x x))

另一个常见不动点组合子是图灵不动点组合子, 即Θ组合子:

Θ = (λx. λy. (y (x x y))) (λx.λy.(y (x x y)))

实现不动点组合子时用到的类型:

| 名称     | Type                                           |
|        |                                                |
| 递归参数   | T                                              |
| 递归返回   | R                                              |
| 递归函数   | Func<T, R>                                     |
| 单步函数   | Func<Func<T, R>, Func<T, R>>                   |
| 不动点组合子 | Func<Func<Func<T, R>, Func<T, R>>, Func<T, R>> |

不动点组合子:

一个以单步函数为参数的返回递归函数的高阶函数

参考使用 Lambda 表达式编写递归 系列文章

浅尝Hazelcast

决定了解一下Hazelcast, 是因为Vert.x.

受到Stay Hungry, Stay Foolish的毒害, 没事总想找点新玩意, 本来想学习一下一直不敢直视的Spring Cloud, 半路杀出个Vert.x, 最后终于越走越远了.

以下是Vert.x中定义概念跟其它框架和语言定义概念的比较,同一行中的概念可被认为是相似的:

Vert.x Akka Spring EJB Node.js Go
Standard Verticle - - - Reactor -
Worker Verticle - - Stateless Session Bean - -
Multiple ThreadedWorker Verticle - Bean(Singleton) - - -
Handler Actor - - - -
Coroutine - - - - Goroutine

Vert.x最令人兴奋的特点之一, 就是它开箱即用的集群化与高可用能力, 通过可插拔的集群管理器实现集群, 其默认的集群管理器就是采用Hazelcast.

于是我念了两句诗(并没有), 就拐个弯开始学习Hazelcast了.

Maven的多继承

名为多继承, 其实是利用<scope>import</scope>向目标项目(子项目)导入一批dependencyManagement管理的可选依赖, 从而实现多继承的效果.

源项目(父项目)pom配置示例

<!-- 省略不重要的内容 -->

<groupId>source.group</groupId>
<artifactId>source-artifact</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>utils.group</groupId>
            <artifactId>utils-artifact</artifactId>
            <version>0.2.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</dependencyManagement>

目标项目(子项目)pom配置示例

<!-- 省略不重要的内容 -->

<groupId>target.group</groupId>
<artifactId>target-artifact</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>
    <!-- 添加需要"继承"的依赖 -->
    <!-- 可缺省依赖坐标的版本号 -->
    <!-- 即: 使用源项目(父项目)中指定的依赖版本号 -->
    <dependency>
        <groupId>utils.group</groupId>
        <artifactId>utils-artifact</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <!-- 添加依赖管理 -->
    <!-- 注意: type为pom, scope为import -->
    <dependencies>
        <dependency>
            <groupId>source.group</groupId>
            <artifactId>source-artifact</artifactId>
            <version>0.1.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

领域实体类

领域实体类

一些只保存数据的类。

  • DTO类:Data Transfer Object,数据传输对象类,泛指用于展示层与服务层之间的数据传输对象。

  • VO类:VO有两种说法,一个是ViewObject,一个是ValueObject。

  • PO类:Persisent Object,持久对象。它们是由一组属性和属性的get和set方法组成。PO是在持久层所使用,用来封装原始数据。

  • BO类:Business Object,业务对象层,表示应用程序领域内“事物”的所有实体类。

  • DO类:Domain Object,领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。

等等。

这些我们统称为领域模型中的实体类。

浅尝Kotlin

从Java转向Kotlin, 就像从Objective-C转向Swift一样.

Go with Oracle

在go中连接Oracle, 需要使用OracleClient提供的ODPI: Oracle Database Programming Interface库.

在macOS中安装ODPI:
参考文档: https://oracle.github.io/odpi/doc/installation.html#macos

主要步骤:

  1. 下载压缩包并解压, 下载地址:
    https://www.oracle.com/technetwork/topics/intel-macsoft-096467.html

  2. $HOME/lib/usr/local/lib添加link:

$ ln -s unzipped/path/to/libclntsh.dylib ~/lib/
  1. 如果使用的是Instant Client 11.2, 需要将链接库文件拷贝到上述路径下:
$ cp unzipped/path/to/{libclntsh.dylib.11.1,libnnz11.dylib,libociei.dylib} ~/lib/
  1. 添加hosts配置:
127.0.0.1 XXX.local

其中XXX.local/系统偏好设置/共享中的本机访问地址, 例如

WX20190614-122556@2x

导入oracle驱动module

$ go get gopkg.in/goracle.v2

数据源配置字符串格式

oracle://username:password@[//]host[:port][/service_name][:server][/instance_name]

Java:Double Brace Initialization

在使用SonarQube检查使用Jmockit进行单元测试的Java代码时, 报出了一个Code Smell和一个Bug, 分别是:squid:S1171(Only static class initializers should be used)squid:S3599(Double Brace Initialization should not be used), 原因是使用了Jmockit的录制功能:

    @Test
    public void testInstanceMockingByExpectation() {
        AnOrdinaryClass instance = new AnOrdinaryClass();
        // 直接把实例传给Expectations的构造函数即可Mock这个实例
        new Expectations(instance) {
            {
                // 尽管这里也可以Mock静态方法,但不推荐在这里写。静态方法的Mock应该是针对类的
                // mock普通方法
                instance.ordinaryMethod();
                result = 20;
                // mock final方法
                instance.finalMethod();
                result = 30;
                // native, private方法无法用Expectations来Mock
            }
        };
        Assert.assertTrue(AnOrdinaryClass.staticMethod() == 1);
        Assert.assertTrue(instance.ordinaryMethod() == 20);
        Assert.assertTrue(instance.finalMethod() == 30);
        // 用Expectations无法mock native方法
        Assert.assertTrue(instance.navtiveMethod() == 4);
        // 用Expectations无法mock private方法
        Assert.assertTrue(instance.callPrivateMethod() == 5);
    }

搜索了一下Double Brace Initialization, 记录在此.
参考: Java:Double Brace Initialization

日常学习笔记 2024

模块复用原则

REP 复用/发布等同原则
复用/发布等同原则Reuse/Release Equivalency Principle):软件复用的最小粒度应等同于其发布的最小粒度。

直白地说,就是要复用一段代码就把它抽成模块。

CCP 共同闭包原则
共同闭包原则Common Closure Principle):为了相同目的而同时修改的类,应该放在同一个模块中。

对大部分应用程序而言,可维护性的重要性远远大于可复用性,由同一个原因引起的代码修改,最好在同一个模块中,如果分散在多个模块中,那么开发、提交、部署的成本都会上升。

CRP 共同复用原则
共同复用原则Common Reuse Principle):不要强迫一个模块依赖它不需要的东西。

相信你一定有这种经历,集成了模块A,但模块A依赖了模块B、C。即使模块B、C 你完全用不到,也不得不集成进来。这是因为你只用到了模块A的部分能力,模块A中额外的能力带来了额外的依赖。如果遵循共同复用原则,你需要把A拆分,只保留你要用的部分。

复用原则竞争关系
REPCCPCRP 三个原则之间存在彼此竞争的关系。REPCCP 是黏合性原则,它们会让模块变得更大,而 CRP 原则是排除性原则,它会让模块变小。
遵守 REPCCP 而忽略 CRP ,就会依赖了太多没有用到的模块和类,而这些模块或类的变动会导致你自己的模块进行太多不必要的发布;
遵守 REPCRP 而忽略 CCP,因为模块拆分的太细了,一个需求变更可能要改n个模块,带来的成本也是巨大的。

image
模块复用原则竞争关系张力图

优秀的架构师应该能在上述三角形张力区域中定位一个最适合目前研发团队状态的位置,例如在项目早期,CCPREP更重要,随着项目的发展,这个最合适的位置也要不停调整。

浅尝Java ClassLoader

老大 @bingoohuang又提了个问题:

玩转JVM从实际需求出发

有两个版本的class,相同包名,相同类名(exactly full qualified long name)
暂且称之为v1和v2两个版本,怎么让他们在一个jvm实例**存,使得方法1调用v1,方法2调用v2

@zhangcunxin提出:

只要classloader不一样就可以让同名类共存

动手实现, 在这里分享一下答案.

浅尝Cobra

WhaleShark-Team/cobra

Source Code Security Audit (源代码安全审计)

官网地址: http://cobra.feei.cn

什么是"源代码安全审计(白盒扫描)"?

由于开发人员的技术水平和安全意识各不相同,导致可能开发出一些存在安全漏洞的代码。攻击者可以通过渗透测试来找到这些漏洞,从而导致应用被攻击、服务器被入侵、数据被下载、业务受到影响等等问题。“源代码安全审计”是指通过审计发现源代码中的安全隐患和漏洞,而Cobra可将这个流程自动化。

Cobra为什么能从源代码中扫描到漏洞?

对于一些特征较为明显的可以使用正则规则来直接进行匹配出,比如硬编码密码、错误的配置等。对于OWASP Top 10的漏洞,Cobra通过预先梳理能造成危害的函数,并定位代码中所有出现该危害函数的地方,继而基于Lex(Lexical Analyzer Generator, 词法分析生成器)和Yacc(Yet Another Compiler-Compiler, 编译器代码生成器)将对应源代码解析为AST(Abstract Syntax Tree, 抽象语法树),分析危害函数的入参是否可控来判断是否存在漏洞(目前仅接入了PHP-AST,其它语言AST接入中)。

SimpleDateFormat的隐藏技能

今天偷闲, 在公司内部的SonarQube上看看Bugs/漏洞/坏味道, 碰见简单的就顺手改改.
改了一堆诸如:

  • printStackTracelog.error
  • 工具类添加private constructor
  • duplicated constant string
  • ......

正要犯困, 忽然眼前一亮, 看见一个新奇的Bug:

SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMdd");

提示信息如下:

Make sure that Week Year "YYYY" is expected here instead of Year "yyyy".

详细的提示信息中, 引用Javadoc的内容如下:

A week year is in sync with a WEEK_OF_YEAR cycle.
All weeks between the first and last weeks (inclusive) have the same week year value.
Therefore, the first and last days of a week year may have different calendar year values.

For example, January 1, 1998 is a Thursday.
If getFirstDayOfWeek() is MONDAY and getMinimalDaysInFirstWeek() is 4 (ISO 8601 standard compatible setting), then week 1 of 1998 starts on December 29, 1997, and ends on January 4, 1998.
The week year is 1998 for the last three days of calendar year 1997.
If, however, getFirstDayOfWeek() is SUNDAY, then week 1 of 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three days of 1998 then are part of week 53 of 1997 and their week year is 1997.

简而言之, 在一年中的大部分时间里, Y所表示的week yeary所表示的year是相同的, 使用这样的格式化字符串不会有问题.
但是, 在年初或者年末的时候, 有可能存在这样的问题:

  • 年初的某几天有可能会被计算在前一年的最后一周内, 计算获得的week year为前一年的年份
  • 年末的某几天有可能会被计算在后一年的第一周内, 计算获得的week year为后一年的年份

在这种情况下格式化的时间字符串, 就会发生提前一年或延迟一年的错误.
接下来, 用实践检验一下:

public class DateFormatterTest {
    @SneakyThrows
    @Test
    public void testWeekYearBug() {
        val originDateString = "2015/12/31";
        val errorDateString = "2016/12/31";
        val parsedDate = new SimpleDateFormat("yyyy/MM/dd").parse(originDateString);
        val formatString = new SimpleDateFormat("YYYY/MM/dd").format(parsedDate);
        assertNotEquals(originDateString, formatString);
        assertEquals(errorDateString, formatString);
    }
}

测试通过, 说明:

使用"yyyy/MM/dd"解析"2015/12/31"获取Date,
再使用"YYYY/MM/dd"格式化Date,
获得的字符串为"2016/12/31"

信息熵

1948年,博士毕业后就在贝尔实验室里研究通讯技术的电子工程师克劳德 • 香农(Claude Shannon, 1916-2001)在《贝尔系统技术杂志》(Bell System Technology Journal)上分两期发表了他一生中也许是最有名的一篇论文:《通讯的数学理论》(A mathematical theory of communications,1948),引入了一条全新的思路,震撼了整个科学技术界,开启了现代信息论研究的先河。在这一伟大的贡献中,他引进的“信息熵”之一般概念举足轻重:它在数学上量化了通讯过程中“信息漏失”的统计本质,具有划时代的意义。

香农最初并没有借用“熵”这个词汇来表达他关于信息传输中的“不确定性”的度量化。他甚至都不太知晓他所考虑的量与古典热力学熵之间的类似性。他想把它称为“information(信息)”,但又认为这个名词太过大众化,已被普通老百姓的日常话语用滥了。他又考虑过就用单词“uncertainty(不确定性)”,但它却更像抽象名词,缺乏量化的余地,确实难于定夺。终于有一天,他遇见了天才的数学家冯 • 诺依曼(John von Neumann, 1903-1957)。真是找对了人!冯·诺依曼马上告诉他:

就叫它熵吧,这有两个好理由。

一是你的不确定性函数已在统计物理中用到过,在那里它就叫熵。

第二个理由更重要:没人真正理解熵为何物,这就让你在任何时候都可能进能退,立于不败之地。

作者:返朴
链接:https://www.zhihu.com/question/22178202/answer/667876061
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.