陈晓锋的学习笔记

记录一些日常接触到的专业知识


  • Home

  • Categories

  • Archives

Mock和Spy的区别

Posted on 2018-08-27 | In Java , 测试 | Comments: | Views:

简答

mock方法和spy方法都可以对对象进行mock。但是前者是接管了对象的全部方法,而后者只是将有桩实现(stubbing)的调用进行mock,其余方法仍然是实际调用。

Read more »

JUnit知识点三则

Posted on 2018-08-26 | Edited on 2018-08-27 | In Java , 测试 | Comments: | Views:

@Mock和@InjectMocks的区别

Difference Between @Mock and @InjectMocks

@Mock创建一个mock,@InjectMocks创建一个实例并且将注入由@Mock或者@Spy注解生成的mock。

注意,需要通过@Runwith(MockitoJUnitRunner.class)或者@Mockito.initMocks(this)来初始化这些mocks并注入。

Read more »

Apache Commons Chain

Posted on 2018-08-21 | Edited on 2018-08-26 | In Utils | Comments: | Views:

Apache Commons Chain是什么

Apache Commons Chain是对责任链设计模式的改造封装,让使用者更加方便的使用。

责任链模式

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

关键点:

  • 链上的各个节点可随意拆分和组装

Apache Commons Chain

Commons Chain实现了Chain of Responsebility和Command模式,其中的Catalog + 配置文件的方式使得调用方和Command的实现方的耦合度大大的降低,提高了灵活性。

示例

在下面三种情况下,命令链将结束:

  • 命令的execute方法返回true
  • 运行到链的尽头
  • 命令抛出异常

当有命令抛出错误时,链就会非正常结束。在Commons Chain中,如果有命令抛出错误,链的执行就会中断,将异常抛出给链的调用者。
许多应用需要对在命令之外定义的错误做明确的处理。Commons Chain提供了Filter接口来满足这个要求。

Filter继承了Command,添加了一个名为postprocess的方法。

只要Filter的execute方法被调用,不论链的执行过程中是否抛出错误,Commons Chain都将保证Filter的postprocess方法被调用。

Spring Ordered

Posted on 2018-08-21 | Edited on 2018-08-26 | In Spring框架 | Comments: | Views:

@Order

通过@Order注解或者实现Ordered接口,可以实现Bean之间的排序顺序。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class Cars {
@Autowired
List<Car> cars;

public void printNames() {

for(Car car : cars) {
System.out.println(car.getName());
}
}
}

当Car的子类通过@Order注解或者实现Ordered接口,@Autowired的cars在注入时,会将value
值小的优先加入,实现子类bean的自动排序。

Guava User Guide

Posted on 2018-08-19 | Edited on 2018-08-26 | In Utils | Comments: | Views:

wiki

基本工具类


使用和避免NULL

使用Optional

前置条件

使用前置条件来断言数据

对象常用方法

常见方法

equals

使用Objects.equal可以避免NPE。如下所示:

1
2
3
4
Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true

在JDK 7中有提供等价方法

compare/compareTo

在实现Comparable接口时,通常的做法是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int zipCode;

public int compareTo(Person other) {
int cmp = lastName.compareTo(other.lastName);
if (cmp != 0) {
return cmp;
}
cmp = firstName.compareTo(other.firstName);
if (cmp != 0) {
return cmp;
}
return Integer.compare(zipCode, other.zipCode);
}
}

有了ComparsionChain之后,可以这样实现

1
2
3
4
5
6
7
public int compareTo(Foo that) {
return ComparisonChain.start()
.compare(this.aString, that.aString)
.compare(this.anInt, that.anInt)
.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
.result();
}

Ordering

排序
Ordering支持链式调用。例如,我们想要以下类的一个包含null值的sortedBy字段的比较器。

1
2
3
4
class Foo {
@Nullable String sortedBy;
int notSortedBy;
}

可以这么写:

1
2
3
4
5
Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(new Function<Foo, String>() {
public String apply(Foo foo) {
return foo.sortedBy;
}
});

该Ordering需要从右往左读,先读取sortedBy的值,再null置顶,再按照String类型的自然顺序排序。

异常处理工具类

Throwables

用于过滤异常或者得到异常链等

集合(Collections)


不可变更集合

ImmutableCollections

新的集合类型

New Collection Types

多重集

相同的元素可以出现多次

  • 当做集合时,更像ArrayList
  • 附加的查询操作,更像Map<E, Integer>

多重关连数组(Multimap)

BiMap

一般,我们为了保持value到key的映射,会如下设置:

1
2
3
4
5
6
7
Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();

nameToId.put("Bob", 42);
idToName.put(42, "Bob");
// what happens if "Bob" or 42 are already present?
// weird bugs can arise if we forget to keep these in sync...

BiMap<K, V>是一个保持以下特性的Map<K, V>:

  • 可以通过inverse()来获取BiMap<K, V>的倒置视图
  • 保证了value是唯一的

表

支持行、列,如:

1
2
3
4
5
6
7
Table<Vertex, Vertex, Double> weightedGraph = HashBasedTable.create();
weightedGraph.put(v1, v2, 4);
weightedGraph.put(v1, v3, 20);
weightedGraph.put(v2, v3, 5);

weightedGraph.row(v1); // returns a Map mapping v2 to 4, v3 to 20
weightedGraph.column(v3); // returns a Map mapping v1 to 20, v2 to 5

ClassToInstanceMap

RangeSet

描述一个不相连的,非空的range的集合。连接的range之间会合并,空的range会被忽略。

1
2
3
4
5
6
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); // {[1, 10]}
rangeSet.add(Range.closedOpen(11, 15)); // disconnected range: {[1, 10], [11, 15)}
rangeSet.add(Range.closedOpen(15, 20)); // connected range; {[1, 10], [11, 20)}
rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)}
rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}

RangeMap

集合工具类

CollectionUtilities

  • 静态构造器
  • Iterables
  • Lists: 分段,逆置
  • Sets: 交、并、差等
  • Maps:

    • Maps.uniqueIndex(Iterable, Function生成唯一键可以索引的Map。

      1
      2
      3
      4
      5
      ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings, new Function<String, Integer> () {
      public Integer apply(String string) {
      return string.length();
      }
      });
      • BiMap
      • Multisets
      • Multimaps
      • Tables: customTable,transpose

扩展工具类

Extension Utilities

Caches

Functional idioms

Guava提供两个基本的“函数式”接口:

  • Function<A, B>
  • Predicate<T>

TO BE CONT.

lombok原理简析

Posted on 2018-08-14 | Edited on 2018-08-26 | In Utils | Comments: | Views:

lombok原理分析和功能实现

原理

  • 定义编译期的注解
  • 利用JSR269 API(Pluggable Annotation Processing API)创建编译期的注解处理器
  • 利用tools.jar的javac api处理AST(抽象语法树)
  • 将功能注册jar包

实现

创建Getter注解的处理器

继承AbstractProcessor这个类,基本的框架大体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mythsman.test;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.util.Set;
@SupportedAnnotationTypes("com.mythsman.test.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return true;
}
}

需要着重实现两个方法:

  • init : 通过ProcessingEnvironment来获取编译阶段的一些环境信息
  • process:主要实现具体逻辑的地方,也就是对AST进行处理的地方

init方法

1
2
3
4
5
6
7
8
9
10
11
12
13
private Messager messager;
private JavacTrees trees;
private TreeMaker treeMaker;
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.messager = processingEnv.getMessager();
this.trees = JavacTrees.instance(processingEnv);
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
this.names = Names.instance(context);
}

process方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public synchronized boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);
set.forEach(element -> {
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator() {
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
for (JCTree tree : jcClassDecl.defs) {
if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
jcVariableDeclList.forEach(jcVariableDecl -> {
messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
});
super.visitClassDef(jcClassDecl);
}
});
});
return true;
}

接下来再实现makeGetterMethodDecl方法:

1
2
3
4
5
6
7
8
9
10
private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);
}
private Name getNewMethodName(Name name) {
String s = name.toString();
return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
}

MySQL必知必会

Posted on 2018-08-13 | Edited on 2018-08-26 | In 数据库 | Comments: | Views:

2018-08-08

数据库:保存有组织的数据的容器
表:某种特定类型数据的结构化清单
模式(schema):关于数据库和表的布局及特性的信息

主键(PK):一列(或一组列),其值能够唯一区分表中的每个行。主键列不允许NULL值

应该总是定义主键 虽然并不总是都需要主键

使用MySQL

MySQL的默认端口:3306

USE #{database} 选择一个数据库
SHOW DATABASES 显示可用的数据库列表
SHOW TABLES 显示数据库内的可用表
SHOW COLUMNS FROM #{table} 返回表的详细信息,DESCRIBE #{table}是该命令的一种快捷方式

第4章 检索数据

基本用法

1
2
SELECT prod_name
FROM products;

检索不同的行

1
2
SELECT DISTINCT vend_id
FROM products;

不能部分使用DISTINCT

OAuth2.0协议

Posted on 2018-08-10 | Edited on 2018-08-26 | In 网络 | Comments: | Views:

深入理解OAuth2.0协议

开放授权的例子:泊车钥匙,让渡部分权利(启动发动机,行驶一段距离)给服务生。

用 用户名/密码 授权的缺点:

  1. 无法保证第三方应用不缓存
  2. 无法设置部分访问的权限
  3. 无法撤销授权,除非修改密码

开放授权(Open Authorization):帮助用户将资源授权给第三方,支持细粒度的权限控制

开放授权的主流方法有:OAuth协议和IAM服务,后者主要用户离线授权或者预先授权。

OAuth协议的特点在于在线授权(现场授权)。

image.png

如图1所示,协议的基本流程如下:
(1) Client请求RO的授权,请求中一般包含:要访问的资源路径,操作类型,Client的身份等信息。

(2) RO批准授权,并将“授权证据”发送给Client。至于RO如何批准,这个是协议之外的事情。典型的做法是,AS提供授权审批界面,让RO显式批准。这个可以参考下一节实例化分析中的描述。

(3) Client向AS请求“访问令牌(Access Token)”。此时,Client需向AS提供RO的“授权证据”,以及Client自己身份的凭证。

(4) AS验证通过后,向Client返回“访问令牌”。访问令牌也有多种类型,若为bearer类型,那么谁持有访问令牌,谁就能访问资源。

(5) Client携带“访问令牌”访问RS上的资源。在令牌的有效期内,Client可以多次携带令牌去访问资源。

(6) RS验证令牌的有效性,比如是否伪造、是否越权、是否过期,验证通过后,才能提供服务。

Question

4.1 为何引入authorization_code?

(1) 浏览器的redirect_uri是一个不安全信道,此方式不适合于传递敏感数据(如access_token)。因为uri可能通过HTTP referrer被传递给其它恶意站点,也可能存在于浏览器cacher或log文件中,这就给攻击者盗取access_token带来了很多机会。另外,此协议也不应该假设RO用户代理的行为是可信赖的,因为RO的浏览器可能早已被攻击者植入了跨站脚本用来监听access_token。因此,access_token通过RO的用户代理传递给Client,会显著扩大access_token被泄露的风险。 但authorization_code可以通过redirect_uri方式来传递,是因为authorization_code并不像access_token一样敏感。即使authorization_code被泄露,攻击者也无法直接拿到access_token,因为拿authorization_code去交换access_token是需要验证Client的真实身份。也就是说,除了Client之外,其他人拿authorization_code是没有用的。 此外,access_token应该只颁发给Client使用,其他任何主体(包括RO)都不应该获取access_token。协议的设计应能保证Client是唯一有能力获取access_token的主体。引入authorization_code之后,便可以保证Client是access_token的唯一持有人。当然,Client也是唯一的有义务需要保护access_token不被泄露。

(2) 引入authorization_code还会带来如下的好处。由于协议需要验证Client的身份,如果不引入authorization_code,这个Client的身份认证只能通过第1步的redirect_uri来传递。同样由于redirect_uri是一个不安全信道,这就额外要求Client必须使用数字签名技术来进行身份认证,而不能用简单的密码或口令认证方式。引入authorization_code之后,AS可以直接对Client进行身份认证(见步骤4和5),而且可以支持任意的Client认证方式(比如,简单地直接将Client端密钥发送给AS)。

微信静默授权

Posted on 2018-08-09 | Edited on 2018-08-26 | In 网络 | Comments: | Views:

微信公众平台的静默授权和网页授权区别详解

微信网页授权

Q1: 静默授权(snsapi_base)有什么作用?
网页授权(snsapi_userinfo)需要用户点击同意,允许后,可以获取用户的基本信息。已关注的用户进入网页授权页,也是静默的,无需用户同意。静默授权(snsapi_base)只能拿到唯一标识openId。从现状看,静默授权的作用,也就是用来判断用户的唯一性,查看用户是否关注了该公众号

ApplicationReadyEvent

Posted on 2018-08-08 | Edited on 2018-08-26 | In Spring框架 | Comments: | Views:

ApplicationReadEvent

事件的来源是SpringApplication,事件发生时,所有的初始化步骤都已经完成。

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class ApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent>, Ordered {

@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
System.out.println("############started");
}

@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

配合EventListener使用

1
2
3
4
5
6
7
8
@Component
class MyClassWithEventListeners {

@EventListener({ApplicationReadyEvent.class})
void contextRefreshedEvent() {
System.out.println("event happened");
}
}
123

陈晓锋

没有什么干货啦

23 posts
13 categories
20 tags
© 2018 陈晓锋
Powered by Hexo v3.7.1
|
Theme – NexT.Mist v6.4.0