maven的依赖机制

传递依赖关系(Transitive Dependencies

依赖调解(Dependency mediation

这个机制决定了当遇到多个版本作为依赖项时,将选择该 artifact 的哪个版本。maven 使用 最近定义(nearest definition)的机制,就是说,它在maven关系树中使用最接近项目的以来关系版本。可以通过在当前工程的 pom 文件中在确保某个依赖的版本。当在pom中指定了某个依赖的不同版本时,以先声明的依赖版本为准

(nearest definition)它在maven关系树中使用最接近项目的以来关系版本,考虑下面的依赖树

1
2
3
4
5
6
A
├── B
│ └── C
│ └── D 2.0
└── E
└── D 1.0

定义了 A B C 三个模块,其中有两个依赖关系 A->B->C->D ,A->E->D,当构建 A时,使用的D是1.0 版本,因为 A-E-D这个依赖关系更短,如果要在构建A时强制使用D2.0版本,可以在A的依赖中明确指定 D2.0,依赖树如下图

1
2
3
4
5
6
7
8
A
├── B
│ └── C
│ └── D 2.0
├── E
│ └── D 1.0

└── D 2.0

依赖域(Dependency Scope

  • compile
    This is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.
  • provided
    This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. A dependency with this scope is added to the classpath used for compilation and test, but not the runtime classpath. It is not transitive.
  • runtime
    This scope indicates that the dependency is not required for compilation, but is for execution. Maven includes a dependency with this scope in the runtime and test classpaths, but not the compile classpath.
  • test
    This scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases. This scope is not transitive. Typically this scope is used for test libraries such as JUnit and Mockito. It is also used for non-test libraries such as Apache Commons IO if those libraries are used in unit tests (src/test/java) but not in the model code (src/main/java).
  • system
    This scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository.
  • import
    This scope is only supported on a dependency of type pom in the <dependencyManagement> section. It indicates the dependency is to be replaced with the effective list of dependencies in the specified POM’s <dependencyManagement> section. Since they are replaced, dependencies with a scope of import do not actually participate in limiting the transitivity of a dependency.

在maven多模块项目中,为了保持模块间依赖的统一,常规做法是在parent model中,使用dependencyManagement预定义所有模块需要用到的dependency(依赖)

复制代码;)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependencyManagement>
<dependencies>
<!-- Feign是一种声明式、模板化的HTTP客户端:以HTTP接口的形式暴露自身服务 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>${spring-cloud-starter-feign.version}</version>
</dependency>
<!-- 支持面向方面的编程即AOP,包括spring-aop和AspectJ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectjrt.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

复制代码;)

然后,子model根据实际需要引入parent中预定义的依赖

复制代码;)

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>

复制代码;)

好处:

1、依赖统一管理(parent中定义,需要变动dependency版本,只要修改一处即可);

2、代码简洁(子model只需要指定groupId、artifactId即可)

3、dependencyManagement只会影响现有依赖的配置,但不会引入依赖,即子model不会继承parent中dependencyManagement所有预定义的depandency,只引入需要的依赖即可,简单说就是“按需引入依赖”或者“按需继承”;因此,在parent中严禁直接使用depandencys预定义依赖,坏处是子model会自动继承depandencys中所有预定义依赖;

但是,问题也出现了:

单继承:maven的继承跟java一样,单继承,也就是说子model中只能出现一个parent标签;

parent模块中,dependencyManagement中预定义太多的依赖,造成pom文件过长,而且很乱;

如何让这些依赖可以分类并清晰的管理?

问题解决:import scope依赖

如何使用:

1、maven2.9以上版本

2、将dependency分类,每一类建立单独的pom文件

3、在需要使用到这些依赖的子model中,使用dependencyManagement管理依赖,并import scope依赖

3、注意:scope=import只能用在dependencyManagement里面,且仅用于type=pom的dependency

示例:

复制代码;)

1
2
3
4
5
6
7
8
9
10
11
12
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<!-- 重要:版本号要和父模块中预定义的spring boot版本号保持一致 -->
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

复制代码;)

maven编译后,下载了pom文件,在maven的本地仓库下查看pom文件如下:

img

好处分析:

1、单一职责原则,根据依赖的分类,细化每一个单一职责的pom文件

2、解决单继承问题,通过import pom文件达到依赖的目的(典型的非继承模式),从而不用从父类中引用依赖

3、父模块的pom就会非常干净,容易维护

java8tomap

1
2
3
4
final Map<String, JSONObject> moudlesDataMap =
allModulesData.stream()
.collect(Collectors.toMap(
item -> (item.get("name").toString()), item -> item, (oldValue, newValue) -> newValue));

JWT Token基础

spring跨域配置

以下内容来源于 spring 官方网站

https://spring.io/blog/2015/06/08/cors-support-in-spring-framework

CORS support in Spring Framework

https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#disqus_thread)

For security reasons, browsers prohibit AJAX calls to resources residing outside the current origin. For example, as you’re checking your bank account in one tab, you could have the evil.com website in another tab. The scripts from evil.com shouldn’t be able to make AJAX requests to your bank API (withdrawing money from your account!) using your credentials.

Cross-origin resource sharing (CORS) is a W3C specification implemented by most browsers that allows you to specify in a flexible way what kind of cross domain requests are authorized, instead of using some less secured and less powerful hacks like IFrame or JSONP.

Spring Framework 4.2 GA provides first class support for CORS out-of-the-box, giving you an easier and more powerful way to configure it than typical filter based solutions.

Spring MVC provides high-level configuration facilities, described bellow.

Controller method CORS configuration

You can add to your @RequestMapping annotated handler method a @CrossOrigin annotation in order to enable CORS on it (by default @CrossOrigin allows all origins and the HTTP methods specified in the @RequestMapping annotation):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/account")
public class AccountController {

@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}

It is also possible to enable CORS for the whole controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}

In this example CORS support is enabled for both retrieve() and remove() handler methods, and you can also see how you can customize the CORS configuration using @CrossOrigin attributes.

You can even use both controller and method level CORS configurations, Spring will then combine both annotation attributes to create a merged CORS configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

@CrossOrigin(origins = "http://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}

@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}

If you are using Spring Security, make sure to enable CORS at Spring Security level as well to allow it to leverage the configuration defined at Spring MVC level.

1
2
3
4
5
6
7
8
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}

Global CORS configuration

In addition to fine-grained, annotation-based configuration you’ll probably want to define some global CORS configuration as well. This is similar to using filters but can be declared withing Spring MVC and combined with fine-grained @CrossOrigin configuration. By default all origins and GET, HEAD and POST methods are allowed.

JavaConfig

Enabling CORS for the whole application is as simple as:

1
2
3
4
5
6
7
8
9
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}

If you are using Spring Boot, it is recommended to just declare a WebMvcConfigurer bean as following:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class MyConfiguration {

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}COPY

You can easily change any properties, as well as only apply this CORS configuration to a specific path pattern:

1
2
3
4
5
6
7
8
9
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}

If you are using Spring Security, make sure to enable CORS at Spring Security level as well to allow it to leverage the configuration defined at Spring MVC level.

XML namespace

It is also possible to configure CORS with the mvc XML namespace.

This minimal XML configuration enable CORS on /** path pattern with the same default properties than the JavaConfig one:

1
2
3
<mvc:cors>
<mvc:mapping path="/**" />
</mvc:cors>COPY

It is also possible to declare several CORS mappings with customized properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
<mvc:cors>

<mvc:mapping path="/api/**"
allowed-origins="http://domain1.com, http://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="false"
max-age="123" />

<mvc:mapping path="/resources/**"
allowed-origins="http://domain1.com" />

</mvc:cors>

If you are using Spring Security, don’t forget to enable CORS at Spring Security level as well:

1
2
3
4
5
<http>
<!-- Default to Spring MVC's CORS configuration -->
<cors />
...
</http>

How does it work?

CORS requests (including preflight ones with an OPTIONS method) are automatically dispatched to the various HandlerMappings registered. They handle CORS preflight requests and intercept CORS simple and actual requests thanks to a CorsProcessor implementation (DefaultCorsProcessor by default) in order to add the relevant CORS response headers (like Access-Control-Allow-Origin). CorsConfiguration allows you to specify how the CORS requests should be processed: allowed origins, headers, methods, etc. It can be provided in various ways:

Filter based CORS support

As an alternative to other methods presented above, Spring Framework also provides a CorsFilter. In that case, instead of using @CrossOrigin or WebMvcConfigurer#addCorsMappings(CorsRegistry), you can for example declare the filter as following in your Spring Boot application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class MyConfiguration {

@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}

取余和取模的区别

取余%取模 mod 两种运算,既有相同之处也有不同的地方

a % ba mod b 的公式都为 result = a - b * c ;c = a / b (b ≠ 0);

两者的区别是 在于 c 的计算方式

a % b 时 ,c的结果向零取整,如 a = 36, b = -10 则 c=-3;

a mod b 时,c的结果向负无穷取整,如 a = 36, b = -10 则 c=-4因为比 -3.6 小的最接近的一个整数是 -4 ;

所以 有如下的运算

36 % -10 = 36 - (-10) * c = 36 - (-10) *(-3) = 36 -30 = 6,结果为 6

36 mod 10 = 36 - (-10) * c = 36 - (-10) * (-4) = 36 -40 = -4 ,结果为 -4

更一般的规律

取余(%)运算中,运算结果的符号和被除数(a)保持一致,即 a 为正,运算的结果就为正, a 为负,运算的结果就为负

取模(mod)运算中,运算结果的符号和除数(b)保持一致,即 b 为正,运算的结果就为正, b 为负,运算的结果就为负

java 中的运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
System.out.println(36 % 10); //6
System.out.println(36 % -10); //6
System.out.println(-36 % 10); //-6
System.out.println(-36 % -10); //-6
System.out.println(Math.floorMod(36, 10)); //6
System.out.println(Math.floorMod(36, -10)); //-4
System.out.println(Math.floorMod(-36, 10)); //4
System.out.println(Math.floorMod(-36, -10)); //-6
}

打印结果如下
6
6
-6
-6
6
-4
4
-6

java in puzzles summary

第二章 Expressive Puzzles

第三章 with Character

Puzzle 11 The Last Laugh

执行下面的代码

1
2
3
4
5
6
public class LastLaugh {
public static void main(String args[]) {
System.out.print("H" + "a");
System.out.print(’H’ + ’a’);
}
}

预期的打印结果是

1
HaHa

但是实际情况如下

1
Ha169

第二个打印语句实际上是将两个字符的ASCII码值相加的结果

下面的语句是可以将两个字符连起来的

1
2
System.out.print(""+'H' + 'a');
// 输出Ha

下面的例子中 不会输出两个数字相加的结果,而是将数字相加的操作作为字符串打印出来

1
2
3
System.out.println("2 + 2 = " + 2+2);
// 输出2 + 2 = 22
显然我们期望的结果是 2 + 2 = 4

从 Java1.5 开始,下面的语句也可以将 两个 char ,作为字符串链接输出

1
System.out.printf("%c%c", 'H', 'a');

结论

the + operator performs string concatenation if and only if at least one of its operands is of type String; otherwise, it performs addition.

当且仅当运算符中的至少一个是字符串类型时,+ 运算符才执行字符串连接,在其他情况下执行加操作

如果需要进行字符串连接的几个值之间没有一个是 String 类型,那么有以下几个选择:

  1. 增加一个空字符串 ""
  2. 使用 String.valueOf() 进行显式转换;
  3. 使用 StringBuffer/StringBuilder
  4. 使用上面提到的 System.out.printf("%c%c", 'H', 'a');的写法

Puzzle12 ABC

运行下面的代码

1
2
3
String letters = "ABC";
char[] numbers = { '1', '2', '3' };
System.out.println(letters + " easy as " + numbers);

预期输出

1
ABC easy as 123

实际输出

1
ABC easy as [C@5e2de80c

之所以会出现上面的结果是因为 在打印的时候 numbers 调用了 ObjecttoString 方法:

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

所以会导致 出现上面的字符串

为了解决上面的问题,可以调用 String.valueOf(char[] data) 方法,该方法会答应出字符数组内所有的字符:

1
2
3
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.println(letters + " easy as " + String.valueOf(numbers));

上面的代码输出

1
ABC easy as 123

也可以使用下面的分开输出的方法

调用 println(char[] data) 方法:

1
2
3
4
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.print(letters + " easy as ");
System.out.println(numbers);

总结

char arrays are not strings. To convert a char array to astring, invoke String.valueOf(char[]) .char

数组不是字符串。 要将char数组转换为字符串,请调用String.valueOf(char [])。

Puzzle 14 Escape Rout

这里实际上是使用 Unicode 标识 ASCII 字符

1
System.out.println("a\u0022.length() + \u0022b".length());

打印结果是 2

等同于

1
System.out.println("a".length() + "b".length());

使用转义之后,即可打印字符串的长度

1
2
System.out.println("a\".length() + \"b".length());
// 16

结论

In summary, prefer escape sequences to Unicode escapes in string and character literals.

总之,相对于字符串和字符文字中的Unicode转义,更喜欢转义序列。

Do not use Unicode escapes to represent ASCII characters.

不要使用Unicode转义符表示ASCII字符。

Puzzle 20 What’s My Class?

下面的代码是将字符串中的所有点号转换成斜杠

1
2
3
4
5
public static void main(String[] args) {
System.out.println(MyTest.class.getName());
System.out.println(
MyTest.class.getName().replaceAll(".", "/") + ".class");
}

最后打印的结果是

1
2
com.bruceliu.algorithms.puzzles.MyTest
//////////////////////////////////////.class

出现这个结果的原因是 replaceAll()方法的第一个参数是一个正则表达式(regular expression),而不是一个字符串序列

正则表达式“。” 匹配任何单个字符,因此类名的每个字符都用斜杠替换,产生我们看到的输出。

1
2
3
4
5
6
public static void main(String[] args) {
System.out.println(MyTest.class.getName());
System.out.println(
MyTest.class.getName().replaceAll("\\.", "/") + ".class");
}

JAVA 1.5 开始,也可以使用以下代码

1
2
System.out.println(MyTest.class.getName().
replaceAll(Pattern.quote("."), "/") + ".class");

要仅匹配句点字符,正则表达式中的句点必须通过在其前面加上反斜杠(\)进行转义。 因为反斜杠字符在字符串文字中有特殊含义-它开始一个转义序列-后面的斜杠本身必须用第二个反斜杠转义。

Puzzle: 21

执行下面的代码

1
System.out.println(MeToo.class.getName().replaceAll("\\.", File.separator) + ".class");

这段代码在 windows 平台会报错

File.separatorwindows 平台是反斜杠 backslash,反斜杠会对其之后的字符进行转义。而在上面的例子中,反斜杠后面没有其他字符了,所以第二个参数是一个无效的输入。

java 1.5 之后有两种方法解决这个问题,方法一:

1
System.out.println(MyTest.class.getName().replaceAll("\\.", Matcher.quoteReplacement(File.separator))+ ".class");

方法二:

1
2
System.out.println(MyTest.class.getName().replace(".", File.separator) + ".class");
注意 replace 方法的第一个参数是 字符序列 而不是正则表达式

java 1.5 之前同样也有解决方法

1
System.out.println(MyTest.class.getName().replace('.', File.separatorChar) + ".class");

Puzzle: 23 No Pain,No Gain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Random;

public class Rhymes {
private static Random rnd = new Random();

public static void main(String[] args) {
StringBuffer word = null;
switch (rnd.nextInt(2)) {
case 1:
word = new StringBuffer(’P’);
case 2:
word = new StringBuffer(’G’);
default:
word = new StringBuffer(’M’);
}
word.append(’a’);
word.append(’i’);
word.append(’n’);
System.out.println(word);
}
}
上面代码的目的是 打印出 Pain,Gain或者Main
但是运行结果是 只打印 `ain`

有如下三个bug

  1. rnd.nextInt(2) 的取值范围是[0,2),2是永远不可能取到的,这里应该改成 rnd.nexInt(3);
  2. switch 语句没有 break,应该给每一个分支增加 break;
  3. new StringBuffer(char) 实际调用的是 new StringBuffer(int capacity),即给 StringBuffer 初始化大小,并不是 new StingBuffer(String str) 初始化字符串

可以进行如下的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.Random;

public class Rhymes {
private static Random rnd = new Random();

public static void main(String[] args) {
StringBuffer word = null;
switch (rnd.nextInt(3)) {
case 1:
word = new StringBuffer("P");
break;
case 2:
word = new StringBuffer("G");
break;
default:
word = new StringBuffer("M");
break;
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}

这个代码还可以写得更加优雅

1
System.out.println("PGM".charAt(rnd.nextInt(3)) + "ain");

第四章 Loopy Puzzlers

Puzzle 24: A Big Delight in Every Byte

ElasticsSearch例子

查询

原始数据结构如下

这是一个银行账户的索引

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
{
"state":"open",
"settings":{
"index":{
"creation_date":"1590560065840",
"number_of_shards":"1",
"number_of_replicas":"1",
"uuid":"qJ2GvjkCTXG5hkL-puXqQA",
"version":{
"created":"7070099"
},
"provided_name":"bank"
}
},
"mappings":{
"_doc":{
"properties":{
"account_number":{
"type":"long"
},
"firstname":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"address":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"balance":{
"type":"long"
},
"gender":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"city":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"employer":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"state":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"age":{
"type":"long"
},
"email":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
},
"lastname":{
"type":"text",
"fields":{
"keyword":{
"ignore_above":256,
"type":"keyword"
}
}
}
}
}
},
"aliases":[

],
"primary_terms":{
"0":1
},
"in_sync_allocations":{
"0":[
"0xyG_6vxQ2WWQ6Z0lGVFBQ"
]
}
}
  1. 查询所有账户,并按账户号码升序排序

    1
    2
    3
    4
    5
    6
    7
    GET /bank/_search
    {
    "query": { "match_all": {} },
    "sort": [
    { "account_number": "asc" }
    ]
    }

    默认情况下,返回 10 条数据,可以通过指定 size 参数来改变返回的最大数量

  2. 分页查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    GET /bank/_search
    {
    "query": { "match_all": {} },
    "sort": [
    { "account_number": "asc" }
    ],
    "from": 10,
    "size": 10
    }
  3. 模糊匹配

    1
    2
    3
    4
    5
    6
    GET /bank/_search
    {
    "query": {
    "match": { "address": "mill lane" }
    }
    }

    返回地址中包含 milllane 的记录

  4. 复合查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    GET /bank/_search
    {
    "query": {
    "bool": {
    "must": [
    { "match": { "age": "40" } }
    ],
    "must_not": [
    { "match": { "state": "ID" } }
    ]
    }
    }
    }

    返回所有年龄在 40 岁,但是没有居住在 ID (爱荷华州)

    注意 mustshould 会影响评分

    must_not 不会影响评分

  5. 范围查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    GET /bank/_search
    {
    "query": {
    "bool": {
    "must": { "match_all": {} },
    "filter": {
    "range": {
    "balance": {
    "gte": 20000,
    "lte": 30000
    }
    }
    }
    }
    }
    }

    查询出工资在 [20000,30000] 的数据

  6. 查出每个州的账户数量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /bank/_search
    {
    "size": 0,
    "aggs": {
    "group_by_state": {
    "terms": {
    "field": "state.keyword"
    }
    }
    }
    }

    返回结果如下

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    {
    "took": 29,
    "timed_out": false,
    "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
    },
    "hits" : {
    "total" : {
    "value": 1000,
    "relation": "eq"
    },
    "max_score" : null,
    "hits" : [ ]
    },
    "aggregations" : {
    "group_by_state" : {
    "doc_count_error_upper_bound": 20,
    "sum_other_doc_count": 770,
    "buckets" : [ {
    "key" : "ID",
    "doc_count" : 27
    }, {
    "key" : "TX",
    "doc_count" : 27
    }, {
    "key" : "AL",
    "doc_count" : 25
    }, {
    "key" : "MD",
    "doc_count" : 25
    }, {
    "key" : "TN",
    "doc_count" : 23
    }, {
    "key" : "MA",
    "doc_count" : 21
    }, {
    "key" : "NC",
    "doc_count" : 21
    }, {
    "key" : "ND",
    "doc_count" : 21
    }, {
    "key" : "ME",
    "doc_count" : 20
    }, {
    "key" : "MO",
    "doc_count" : 20
    } ]
    }
    }
    }

    由于查询的时候指定 size=0,所以 hits 字段长度为 0

  7. 返回每个州的的账户数量,以及平均工资

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    GET /bank/_search
    {
    "size": 0,
    "aggs": {
    "group_by_state": {
    "terms": {
    "field": "state.keyword"
    },
    "aggs": {
    "average_balance": {
    "avg": {
    "field": "balance"
    }
    }
    }
    }
    }
    }

    指定已平均工资降序排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    GET /bank/_search
    {
    "size": 0,
    "aggs": {
    "group_by_state": {
    "terms": {
    "field": "state.keyword",
    "order": {
    "average_balance": "desc"
    }
    },
    "aggs": {
    "average_balance": {
    "avg": {
    "field": "balance"
    }
    }
    }
    }
    }
    }

java初始化值

在类的成员变量中,java的基本数据(此处包含 String)类型将会赋初值

Data Type Default Value (for fields)
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char ‘\u0000’
String (or any object) null
boolean false

局部变量略有不同。编译器永远不会将默认值分配给未初始化的局部变量。如果您无法在声明它的地方初始化本地变量,请确保在尝试使用它之前为其分配一个值。访问未初始化的局部变量将导致编译时错误。

官方文档

rabbitmq进阶

基础知识

交换机(exchange)的属性

autodelete 当最后一个绑定到 Exchange 上的队列删除后,自动删除该 Exchange。

internal 当前Exchange 是否用于 RabbitMq 内部使用,默认为 false。

arguments 扩展参数,用于 AMQP 协议自制订化使用

消息如何保障 100 % 的投递成功

什么是生产端的可靠性投递

  1. 保障消息成功发出。
  2. 保障MQ 节点的成功接收。
  3. 发送端到MQ节点的(Broker)确认应答。
  4. 完善的消息进行补偿机制。

解决方法

  1. 消息落库,对消息状态进行打标

    发送消息时,将消息持久化到数据库,并设置一个状态,客户端会送应答之后进行状态变更。

  2. 消息的延迟投递,做二次确认,回调检查。

    1. 业务数据入库

    2. 发送业务消息,并生成二次检查的消息

    3. 消费端收到这个消息正确处理之后,再生成一条消息,该消息由 call-back 进行监听,callback将其入库

    4. 生产段 经过几分钟之后 发送二次检查消息到 call-back,查看刚才的业务是否处理成功,如果成功则不进行任何操作,如果失败 call-back 会发起一个 rpc 请求,让生产端进行重发。

幂等性的概念

可以借鉴数据库乐观锁的机制

比如执行如下的一条sql

1
update t_reps set count = count-1,version=version+1 where id='xiaomi9'

如果现在某个手机在数据库中 count=1, 而且 version=1,在执行上面的 sql 的时候,应该首先查询数据库,多个进程应该查询数据库

1
select count,version from t_reps where id ='xiaomi9'; 

多个消费者进程可能都会查到这条数据,假设 count = 1, version=1

将上面的 sql 更改为

1
update t_reps set count = count-1,version=version+1 where id='xiaomi9' and version = 1

可以避免多个进程的重复操作。

消费端-幂等性保障

  1. 唯一id+指纹码 机制

    唯一id + 指纹码机制,利用数据库主键去重

    1
    select count(*) from t_order where id = 唯一id+指纹码

    好处 实现简单

    坏处 高并发下有数据库写入的性能瓶颈

    解决方案 数据库路由

  1. 使用 redis 的原子性特性

    redis 进行幂等,需要考虑的问题:

    1. 我们是否要进行数据的落库,如果落库的话,关键问题是数据库和缓存如何做到原子性
    2. 如果不进行落库,那么都存储到缓存中,如何设置定时的同步策略。