From 45b28d7c6830da3e8b14638fde81af0745e3f69f Mon Sep 17 00:00:00 2001
From: zsy0216 <594983498@qq.com>
Date: Tue, 18 Aug 2020 17:34:40 +0800
Subject: [PATCH 01/68] fix: fix springboot version
---
...\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md" | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git "a/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md" "b/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md"
index fb79859..4a6b4b4 100644
--- "a/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md"
+++ "b/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md"
@@ -1,7 +1,7 @@
## 前言
前一章已经讲解了SpringBoot的SpringFactoriesLoader类的功能以及作用,即读取spring.factories文件中的工厂类,其中就包括了系统初始化器。在SpringBoot中,系统初始化器名称为ApplicationContextInitializer,它是一个接口,只定义了一个initialize方法。下面将详细介绍下SpringBoot的系统初始化器的原理以及作用,并且自定义一个系统初始化器,并在此基础上讲解下常见的使用场景。
-**SpringBoot版本:5.2.1.RELEASE**
+**SpringBoot 版本:2.2.1.RELEASE**
## 正文
From 8cd23b26c9129e5280aa85b7fb6085913aaa41df Mon Sep 17 00:00:00 2001
From: liuhao <326047736@qq.com>
Date: Wed, 21 Oct 2020 19:11:47 +0800
Subject: [PATCH 02/68] Just add code block language..
---
...15\345\212\241\346\232\264\351\234\262.md" | 8 +--
...0\224\342\200\224 Dubbo\347\232\204URL.md" | 2 +-
...66\357\274\210\344\270\212\357\274\211.md" | 14 ++---
...66\357\274\210\344\270\213\357\274\211.md" | 28 +++++-----
...66\357\274\210\344\270\255\357\274\211.md" | 2 +-
...245\345\255\246\344\271\240ThreadLocal.md" | 10 ++--
...le\345\205\263\351\224\256\345\255\227.md" | 8 +--
...02\357\274\210\344\270\200\357\274\211.md" | 24 ++++-----
...02\357\274\210\344\272\214\357\274\211.md" | 54 +++++++++----------
...25\345\261\202\346\272\220\347\240\201.md" | 18 +++----
...01\344\270\216\345\216\237\347\220\206.md" | 52 +++++++++---------
.../\344\272\214\350\277\233\345\210\266.md" | 4 +-
...26\347\225\245\346\250\241\345\274\217.md" | 28 +++++-----
...50\357\274\210\344\270\212\357\274\211.md" | 44 +++++++--------
...50\357\274\210\344\270\213\357\274\211.md" | 12 ++---
...AnnotationAwareAspectJAutoProxyCreator.md" | 26 ++++-----
...36\347\247\230\351\235\242\347\272\261.md" | 28 +++++-----
...AnnotationAwareAspectJAutoProxyCreator.md" | 34 ++++++------
...0\224\342\200\224SpringFactoriesLoader.md" | 16 +++---
...50\345\212\240\350\275\275\345\231\250.md" | 10 ++--
...13\344\273\266\346\234\272\345\210\266.md" | 34 ++++++------
...35\345\247\213\345\214\226\345\231\250.md" | 14 ++---
...\345\210\235\350\257\206SpringSecurity.md" | 8 +--
...70\345\277\203\351\200\273\350\276\221.md" | 22 ++++----
...46\273\244\345\231\250FilterChainProxy.md" | 34 ++++++------
...04\350\277\207\346\273\244\345\231\250.md" | 20 +++----
26 files changed, 277 insertions(+), 277 deletions(-)
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md"
index 758f10a..eede346 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md"
@@ -1,4 +1,4 @@
-```
+```Java
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
@@ -33,7 +33,7 @@ public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
}
```
-```
+```Java
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
@@ -71,7 +71,7 @@ public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory
}
```
-```
+```Java
package org.apache.dubbo.remoting;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adaptive implements org.apache.dubbo.remoting.Transporter {
@@ -99,7 +99,7 @@ public class Transporter$Adaptive implements org.apache.dubbo.remoting.Transport

JavassistProxyFactory代码如下:
-```
+```Java
public class JavassistProxyFactory extends AbstractProxyFactory {
@Override
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md"
index 1119488..99cea39 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md"
@@ -34,7 +34,7 @@ dubbo://172.17.32.91:20880/org.apache.dubbo.demo.DemoService?anyhost=true&applic
先看下Dubbo中org.apache.dubbo.common包下的URL类源码:
-```
+```Java
public /*final**/
class URL implements Serializable {
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md"
index fc509ca..b0d01fa 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md"
@@ -28,13 +28,13 @@ Java SPI的定义及使用步骤如下:
在com.test.spi包目录下,定义了一个PrintService接口和一个PrintServiceImpl实现类,然后在resources目录下定义了一个META-INF/services/com.test.spi.PrintService,注意这里定义的是一个
全路径名称的文件。
-```
+```Java
public interface Printservice (
void printlnfo();
}
```
-```
+```Java
public class PrintServicelmpl implements Printservice {
@Override
public void printlnfo() {
@@ -43,7 +43,7 @@ public class PrintServicelmpl implements Printservice {
}
```
-```
+```Java
public static void main(String[] args) (
ServiceLoader serviceServiceLoader =
ServiceLoader.load(PrintService.class);
@@ -119,7 +119,7 @@ ExtensionLoader即扩展点加载器,它是Dubbo SPI的核心,负责加载
上图清楚的展示了LoadingStrategy接口及其实现类的关系。LoadingStrategy继承了Prioritized,因而其实现类会有优先级之分,而Dubbo默认是使用的DubboInternalLoadingStrategy,查看其三个类的源码:
-```
+```Java
public class DubboInternalLoadingStrategy implements LoadingStrategy {
// 表示要加载的目录位置
@@ -136,7 +136,7 @@ public class DubboInternalLoadingStrategy implements LoadingStrategy {
}
```
-```
+```Java
public class DubboLoadingStrategy implements LoadingStrategy {
// 表示要加载的目录位置
@@ -160,7 +160,7 @@ public class DubboLoadingStrategy implements LoadingStrategy {
}
```
-```
+```Java
public class ServicesLoadingStrategy implements LoadingStrategy {
// 表示要加载的目录位置
@@ -185,7 +185,7 @@ public class ServicesLoadingStrategy implements LoadingStrategy {
这里的MAX_PRIORITY、NORMAL_PRIORITY和MIN_PRIORITY时定义在Prioritized这个接口中的,查看一下Prioritized中定义的值以及实现的compareTo方法:
-```
+```Java
/**
* The maximum priority
*/
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md"
index 91b2449..d99cbec 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md"
@@ -12,7 +12,7 @@
另外在@SPI注解的value值指定了扩展点默认的实现类名,例如SimpleExt注解由@SPI("impl1")修饰,则表示它的实现类名为:SimpleExtImpl1,查看SPI的配置文件可证:
-```
+```Java
# Comment 1
impl1=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl1#Hello World
impl2=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl2 # Comment 2
@@ -24,13 +24,13 @@ Dubbo通过ExtensionLoader去加载上述SPI配置文件,然后读取到@SPI("
Dubbo SPI的核心逻辑几乎都封装在ExtensionLoader之中,ExtensionLoader存放于dubbo-common模块的extension保重,功能类似于JDK SPI中的java.util.ServiceLoader。
下面展示了ExtensionLoader最常用的使用方式:
-```
+```Java
SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultExtension();
```
首先时调用ExtensionLoader#getExtensionLoader(SimpleExt.class),来获取SimpleExt类型的ExtensionLoader。查看ExtensionLoader源码如下:
-```
+```Java
public static ExtensionLoader getExtensionLoader(Class type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
@@ -51,11 +51,11 @@ SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultEx
}
return loader;
}
-```
+```Java
getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经缓存了该类型的扩展点加载器,如果没有则new一个该类型的ExtensionLoader并添加进EXTENSION_LOADERS中。但需要注意的是ExtensionLoader的构造方法
中,是会先创建默认的ExtensionFactory类型的ExtensionLoader对象,然后调用getAdaptiveExtension()方法创建适配类型的扩展点实现类。
-```
+```Java
private ExtensionLoader(Class> type) {
this.type = type;
// 从此处可以知道,对于默认的ExtensionFactory.class来说,是没有objectFactory熟悉对象值的
@@ -68,7 +68,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
被赋值为AdaptiveExtensionFactory。
下面看下getExtensionClass()方法的逻辑
-```
+```Java
private Class> getExtensionClass(String name) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
@@ -81,7 +81,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
private Map> getExtensionClasses() {
Map> classes = cachedClasses.get();
// 双重检测,防止并发环境下指令重排序,cachedClasses是static类型
@@ -99,7 +99,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
private Map> loadExtensionClasses() {
// 缓存默认的扩展点名称,这里会去读取@SPI注解
cacheDefaultExtensionName();
@@ -140,7 +140,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
// 加载SPI配置文件目录
private void loadDirectory(Map> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
@@ -173,7 +173,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class> clazz, String name,
boolean overridden) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
@@ -218,7 +218,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经

在ExtensionFactory接口上有@SPI注解修饰,而Dubbo会在调用ExtensionFactory时,会去调用ExtensionFactory的SPI配置文件中的扩展点名称以及扩展点实现类,查看下其SPI配置文件:
-```
+```Java
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
```
@@ -232,7 +232,7 @@ AdaptiveExtensionFactory会根据运行时状态来决定给ExtensionFactory赋
下面看下AdaptiveExtensionFactory类:
-```
+```Java
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
@@ -275,7 +275,7 @@ public class AdaptiveExtensionFactory implements ExtensionFactory {
下面看看ExtensionLoader的方法:
-```
+```Java
private Class> getAdaptiveExtensionClass() {
// 获取扩展点实现类,如果缓存中没有则去扫描SPI文件,扫描到扩展点实现类后则存入cachedClasses缓存中
getExtensionClasses(); // ------------------------ ②
@@ -325,7 +325,7 @@ public class AdaptiveExtensionFactory implements ExtensionFactory {
的扩展点实现类,就会去通过Javassist来生成代理代码,即生成对于的Xxx@Adaptive代码。
下面就是通过Javassist代理生产的适配类。(再Dubbo源码中的dubbo-common模块test目录下的org.apache.dubbo.extension包中有对应的测试类)
-```
+```Java
package org.apache.dubbo.common.extension.ext1;
import org.apache.dubbo.common.extension.ExtensionLoader;
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md"
index 58f5d93..d9c7e01 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md"
@@ -65,7 +65,7 @@
接下来的原理分析通过Dubbo源码中的test包下的代码来进行说明。(想学好开源框架,要好好利用开源框架中各种Test用例)
-```
+```Java
@Test
public void test_getDefaultExtension() throws Exception {
SimpleExt ext = getExtensionLoader(SimpleExt.class).getDefaultExtension();
diff --git "a/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md" "b/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md"
index fbe3c0c..0235a9f 100644
--- "a/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md"
+++ "b/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md"
@@ -108,7 +108,7 @@
#### 2.1 ThreadLocal内部使用了哪些数据结构?
首先,我们来看下ThreadLocal中几个比较重要的数据结构。
-```
+```Java
/**
* 用于ThreadLocal内部ThreadLocalMap数据结构的哈希值,用于降低哈希冲突。
*/
@@ -135,7 +135,7 @@ private static int nextHashCode() {
下面将是ThreadLocal最终要的一个数据结构:ThreadLocalMap
-```
+```Java
/**
* ThreadLocalMap其实就是一个用于ThreadLocal的自定义HashMap,它和HashMap很像。在其内部有一个自定义的Entry类,
* 并且有一个Entry数组来存储这个类的实例对象。类似于HashMap,ThreadLocalMap同样的拥有初始大小,拥有扩容阈值。
@@ -197,7 +197,7 @@ static class ThreadLocalMap {
下面回到关于ThreadLocal源码的介绍,先看看set()和get()方法源码:
-```
+```Java
// ThreadLocal中的set()方法
public void set(T value) {
Thread t = Thread.currentThread();
@@ -244,7 +244,7 @@ static class ThreadLocalMap {
-```
+```Java
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
@@ -280,7 +280,7 @@ static class ThreadLocalMap {
知道怎么存储以及获取ThreadLocal之后,还要知道怎么清除ThreadLocal,防止内存泄漏,下面看下remove()源码:
-```
+```Java
// ThreadLocal的remove()方法
public void remove() {
// 获取当前线程中的ThreadLocalMap
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
index 728a6ba..79f3bfd 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
@@ -56,7 +56,7 @@ Lock引起的将当前处理器缓存该变量的数据写回到系统内存中
为了实现volatile的内存语义,编译期在生成字节码时会对使用volatile关键字修饰的变量进行处理,在字节码文件里对应位置生成一个Lock前缀指令,Lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。
下面代码来演示一下禁止指令重排序:
-```
+```Java
a = 1; //语句一
b = 2; //语句二
flag = true; //语句三,flag为volatile变量
@@ -137,7 +137,7 @@ LoadLoad,StoreStore,LoadStore,StoreLoad实际上是Java对上面两种屏障的
下面来谈谈volatile的应用场景:
1. 状态标志:多个线程以一个volatile变量作为为状态标志,例如完成**初始化**或者**状态同步**。典型例子AQS的同步状态:
-```
+```Java
/**
* The synchronization state.
*/
@@ -146,7 +146,7 @@ private volatile int state;
2. 一次性安全发布
最典型的例子就是安全的单例模式:
-```
+```Java
private static Singleton instance;
public static Singleton getInstance() {
//第一次null检查
@@ -164,7 +164,7 @@ public static Singleton getInstance() {
上面这种写法,仍然会出现问题——多线程调用getInstance方法时,有可能一个线程会获得还**没有初始化的对象**!这都是因为重排序的原因,具体分析这里不展开。
解决办法及时用volatile对instance进行修饰
-```
+```Java
private static volatile Singleton instance;
```
这就是经典的“双重检查锁定与延迟初始化”。
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
index 4418513..7b87bdf 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
@@ -30,7 +30,7 @@
#### 1.1 String的修饰符与实现类
打开String源码,可以看到String类的由final修饰的,并且实现了Serializable,Comparable,CharSequence接口。
-```
+```Java
public final class String
implements java.io.Serializable, Comparable, CharSequence {
}
@@ -41,7 +41,7 @@ public final class String
#### 1.2 String类的成员变量
-```
+```Java
public final class String
implements java.io.Serializable, Comparable, CharSequence {
@@ -66,7 +66,7 @@ hash值将用于String类的hashCode()方法的计算,这里先不作具体讲
##### 1.2.4 serialPersistentFields属性
了解过JAVA序列化的,应该清楚transient是用于指定哪个字段不被默认序列化,对于不需要序列化的属性直接用transient修饰即可。而serialPersistentFields用于指定哪些字段需要被默认序列化,具体用法如下:
-```
+```Java
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("name", String.class),
new ObjectStreamField("age", Integer.Type)
@@ -77,15 +77,15 @@ private static final ObjectStreamField[] serialPersistentFields = {
#### 1.3 创建String对象
1. 直接使用"",换句话说就是使用"字面量"赋值
- ```
+ ```Java
String name = "bruis";
```
2. 使用连接符"+"来赋值
- ```
+ ```Java
String name = "ca" + "t";
```
3. 使用关键字new来创建对象
- ```
+ ```Java
String name = new String("bruis");
```
4. 除了上面最常见的几种创建String对象的方式外,还有以下方法可以创建String对象
@@ -126,7 +126,7 @@ JAVA的运行时数据区包括以下几个区域:
#### 2.2 String与JAVA内存区域
下面看看使用""和new的方式创建的字符串在底层都发生了些什么
-```
+```Java
public class TestString {
public static void main(String[] args) {
String name = "bruis";
@@ -296,7 +296,7 @@ SourceFile: "TestString.java"
```
这里有一个需要注意的地方,在java中使用"+"连接符时,一定要注意到"+"的连接符效率非常低下,因为"+"连接符的原理就是通过StringBuilder.append()来实现的。所以如:String name = "a" + "b";在底层是先new 出一个StringBuilder对象,然后再调用该对象的append()方法来实现的,调用过程等同于:
-```
+```Java
// String name = "a" + "b";
String name = new StringBuilder().append("a").append("b").toString();
```
@@ -307,7 +307,7 @@ String name = new StringBuilder().append("a").append("b").toString();
官方文档解释为字符串常量池由String独自维护,当调用intern()方法时,如果字符串常量池中包含该字符串,则直接返回字符串常量池中的字符串。否则将此String对象添加到字符串常量池中,并返回对此String对象的引用。
下面先看看这几句代码,猜猜结果是true还是false
-```
+```Java
String a1 = new String("AA") + new String("BB");
System.out.println("a1 == a1.intern() " + (a1 == a1.intern()));
@@ -325,7 +325,7 @@ String name = new StringBuilder().append("a").append("b").toString();
使用字面量的方式创建字符串,要分两种情况。
① 如果字符串常量池中没有值,则直接创建字符串,并将值存入字符串常量池中;
-```
+```Java
String name = "bruis";
```
对于字面量形式创建出来的字符串,JVM会在编译期时对其进行优化并将字面量值存放在字符串常量池中。运行期在虚拟机栈栈帧中的局部变量表里创建一个name局部变量,然后指向字符串常量池中的值,如图所示:
@@ -335,7 +335,7 @@ String name = "bruis";
2. 使用new的方式创建字符串
-```
+```Java
String name = new String("bruis");
```
首先在堆中new出一个对象,然后常量池中创建一个指向堆中"bruis"的引用。
@@ -343,7 +343,7 @@ String name = new String("bruis");
##### 2.3.2 解析
-```
+```Java
/**
* 首先对于new出的两个String()对象进行字符串连接操作,编译器无法进行优化,只有等到运行期期间,通过各自的new操作创建出对象之后,然后使 用"+"连接符拼接字符串,再从字符串常量池中创建三个分别指向堆中"AA"、"BB",而"AABB"是直接在池中创建的字面量值,这一点可以通过类的反编译来证明,这里就不具体展开了。
*/
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md"
index cd33048..09008df 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md"
@@ -26,7 +26,7 @@
### 1. String的equals方法
String源码的equals方法如下:
-```
+```Java
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
@@ -55,7 +55,7 @@ String源码的equals方法如下:
### 2. String的hashcode方法
String源码中hashcode方法如下:
-```
+```Java
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
@@ -70,7 +70,7 @@ String源码中hashcode方法如下:
}
```
在String类中,有个字段hash存储着String的哈希值,如果字符串为空,则hash的值为0。String类中的hashCode计算方法就是以31为权,每一位为字符的ASCII值进行运算,用自然溢出来等效取模,经过第一次的hashcode计算之后,属性hash就会赋哈希值。从源码的英文注释可以了解到哈希的计算公式:
-```
+```Java
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
```
@@ -78,7 +78,7 @@ s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
这是一个很经典的话题了,下面来深入研究一下这两个方法。由上面的介绍,可以知道String的equals()方法实际比较的是两个字符串的内容,而String的hashCode()方法比较的是字符串的hash值,那么单纯的a.equals(b)为true,就可以断定a字符串等于b字符串了吗?或者单纯的a.hash == b.hash为true,就可以断定a字符串等于b字符串了吗?答案是否定的。
比如下面两个字符串:
-```
+```Java
String a = "gdejicbegh";
String b = "hgebcijedg";
System.out.println("a.hashcode() == b.hashcode() " + (a.hashCode() == b.hashCode()));
@@ -100,7 +100,7 @@ false
### 4. String的compareTo()方法
-```
+```Java
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
@@ -124,7 +124,7 @@ false
### 5. String的startWith(String prefix)方法
-```
+```Java
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
@@ -150,7 +150,7 @@ false
如果参数字符序列是该字符串字符序列的前缀,则返回true;否则返回false;
示例:
-```
+```Java
String a = "abc";
String b = "abcd";
System.out.println(b.startsWith(a));
@@ -161,7 +161,7 @@ true
### 6. String的endsWith(String suffix)方法
查看String的endsWith(String suffix)方法源码:
-```
+```Java
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
@@ -169,7 +169,7 @@ true
其实endsWith()方法就是服用了startsWith()方法而已,传进的toffset参数值时value和suffix长度差值。
示例:
-```
+```Java
String a = "abcd";
String b = "d";
System.out.println(a.endsWith(b));
@@ -179,7 +179,7 @@ true
### 7. String的indexOf(int ch)方法
-```
+```Java
public int indexOf(int ch) {
return indexOf(ch, 0);
}
@@ -209,7 +209,7 @@ true
对于String的indexOf(int ch)方法,查看其源码可知其方法入参为ASCII码值,然后和目标字符串的ASCII值来进行比较的。其中常量Character.MIN_SUPPLEMENTARY_CODE_POINT表示的是0x010000——十六进制的010000,十进制的值为65536,这个值表示的是十六进制的最大值。
下面再看看indexOfSupplementary(ch, fromIndex)方法
-```
+```Java
private int indexOfSupplementary(int ch, int fromIndex) {
if (Character.isValidCodePoint(ch)) {
final char[] value = this.value;
@@ -228,7 +228,7 @@ true
java中特意对超过两个字节的字符进行了处理,例如emoji之类的字符。处理逻辑就在indexOfSupplementary(int ch, int fromIndex)方法里。
Character.class
-```
+```Java
public static boolean isValidCodePoint(int codePoint) {
// Optimized form of:
// codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT
@@ -238,7 +238,7 @@ Character.class
```
对于方法isValidCodePoint(int codePoint)方法,用于确定指定代码点是否是一个有效的Unicode代码点。代码
-```
+```Java
int plane = codePoint >>> 16;
return plane < ((MAX_CODE_POINT + 1) >>> 16);
```
@@ -246,7 +246,7 @@ return plane < ((MAX_CODE_POINT + 1) >>> 16);
### 8. String的split(String regex, int limit)方法
-```
+```Java
public String[] split(String regex, int limit) {
char ch = 0;
if (((regex.value.length == 1 &&
@@ -301,7 +301,7 @@ return plane < ((MAX_CODE_POINT + 1) >>> 16);
split(String regex, int limit)方法内部逻辑非常复杂,需要静下心来分析。
if判断中**第一个括号**先判断一个字符的情况,并且这个字符不是任何特殊的正则表达式。也就是下面的代码:
-```
+```Java
(regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1)
```
@@ -309,17 +309,17 @@ if判断中**第一个括号**先判断一个字符的情况,并且这个字
在if判断中,**第二个括号**判断有两个字符的情况,并且如果这两个字符是以```\```开头的,并且不是字母或者数字的时候。如下列代码所示:
-```
+```Java
(regex.length() == 2 && regex.charAt(0) == '\\' && (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && ((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0)
```
判断完之后,在进行**第三个括号**判断,判断是否是两字节的unicode字符。如下列代码所示:
-```
+```Java
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE)
```
对于下面这段复杂的代码,我们结合示例一句一句来分析。
-```
+```Java
int off = 0;
int next = 0;
boolean limited = limit > 0;
@@ -357,7 +357,7 @@ if判断中**第一个括号**先判断一个字符的情况,并且这个字
#### 8.2 源码分析2
示例代码1:
-```
+```Java
String splitStr1 = "what,is,,,,split";
String[] strs1 = splitStr1.split(",");
for (String s : strs1) {
@@ -375,7 +375,7 @@ split
```
示例代码2:
-```
+```Java
String splitStr1 = "what,is,,,,";
String[] strs1 = splitStr1.split(",");
for (String s : strs1) {
@@ -392,7 +392,7 @@ is
示例代码3:
-```
+```Java
String splitStr1 = "what,is,,,,";
String[] strs1 = splitStr1.split(",", -1);
for (String s : strs1) {
@@ -419,7 +419,7 @@ is
o
```
由于regex为',',所以满足if括号里的判断。一开始next和off指针都在0位置,limit为0,在while里的判断逻辑指的是获取','索引位置,由上图拆分的字符数组可知,next会分别为4,7,8,9,10。由于limited = limit > 0,得知limited为false,则逻辑会走到
-```
+```Java
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
@@ -468,7 +468,7 @@ list集合里就会添加进空字符串""
[what,is, , , ,]
```
当程序走到时,
-```
+```Java
if(!limited || list.size() < limit) {
list.add(substring(off, value.length);
}
@@ -486,7 +486,7 @@ list集合里就会添加进空字符串""
```
这里相信小伙伴们都知道示例1和示例2的区别在那里了,是因为示例2最后索引位置的list为空字符串,所以list.get(resultSize-1).length()为0,则会调用下面的代码逻辑:
-```
+```Java
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
@@ -504,7 +504,7 @@ list集合里就会添加进空字符串""
就以示例代码一为例,对于字符串"what,is,,,,"。
**对于limit > 0**,由于代码:
-```
+```Java
boolean limited = limit > 0; // limited为true
..
..
@@ -528,7 +528,7 @@ what,is,,,,
**对于limit = 0**,由于代码:
-```
+```Java
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
@@ -543,7 +543,7 @@ is
```
**对于limit < 0**,由于代码:
-```
+```Java
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
```
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md"
index b0b44b9..fa16533 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md"
@@ -50,7 +50,7 @@
#### 3.1 线程优先级
优先级代表线程执行的机会的大小,优先级高的可能先执行,低的可能后执行,在 Java 源码中,优先级从低到高分别是 1 到 10,线程默认 new 出来的优先级都是 5,源码如下:
-```
+```Java
// 最低优先级
public final static int MIN_PRIORITY = 1;
@@ -75,7 +75,7 @@ public final static int MAX_PRIORITY = 10;
#### 4.1 start 启动线程
-```
+```Java
// 该方法可以创建一个新的线程出来
public synchronized void start() {
// 如果没有初始化,抛异常
@@ -111,7 +111,7 @@ private native void start0();
下面只贴出部分关键源码:
-```
+```Java
// 无参构造器,线程名字自动生成
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
@@ -161,13 +161,13 @@ private void init(ThreadGroup g, Runnable target, String name,
当我们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。
-```
+```Java
public final void join() throws InterruptedException {
join(0);
}
```
-```
+```Java
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
@@ -206,7 +206,7 @@ private void init(ThreadGroup g, Runnable target, String name,
yield 是个 native 方法,底层代码如下:
-```
+```Java
public static native void yield();
```
@@ -220,7 +220,7 @@ sleep 也是 native 方法,可以接受毫秒的一个入参,也可以接受
接受毫秒和纳秒两个入参时,如果给定纳秒大于等于 0.5 毫秒,算一个毫秒,否则不算。
-```
+```Java
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
@@ -239,7 +239,7 @@ sleep 也是 native 方法,可以接受毫秒的一个入参,也可以接受
sleep(millis);
}
```
-```
+```Java
public static native void sleep(long millis) throws InterruptedException;
```
@@ -252,7 +252,7 @@ interrupt 中文是打断的意思,意思是可以打断中止正在运行的
我们举一个例子来说明如何打断 WAITING 的线程,代码如下:
-```
+```Java
@Test
public void testInterrupt() throws InterruptedException {
Thread thread = new Thread(new Runnable() {
diff --git "a/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md" "b/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md"
index 5a43c54..d3271c8 100644
--- "a/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md"
+++ "b/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md"
@@ -43,7 +43,7 @@ CompletableFuture类提供了非常多的方法供我们使用,包括了runAsy
**runAsync()**,异步运行,
-```
+```Java
@Test
public void runAsyncExample() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
@@ -77,7 +77,7 @@ CompletedFuture...isDown
**supplyAsync()**
supply有供应的意思,supplyAsync就可以理解为异步供应,查看supplyAsync()方法入参可以知道,其有两个入参:
-- Supplier supplier,
+- Supplier\ supplier,
- Executor executor
这里先简单介绍下Supplier接口,Supplier接口是JDK8引入的新特性,它也是用于创建对象的,只不过调用Supplier的get()方法时,才会去通过构造方法去创建对象,并且每次创建出的对象都不一样。Supplier常用语法为:
@@ -85,7 +85,7 @@ supply有供应的意思,supplyAsync就可以理解为异步供应,查看sup
Supplier sup= MySupplier::new;
```
再展示代码例子之前,再讲一个thenAccept()方法,可以发现thenAccept()方法的入参如下:
-- Comsumer super T>
+- Comsumer\ super T\>
Comsumer接口同样是java8新引入的特性,它有两个重要接口方法:
1. accept()
@@ -94,7 +94,7 @@ Comsumer接口同样是java8新引入的特性,它有两个重要接口方法
thenAccept()可以理解为接收CompletableFuture的结果然后再进行处理。
下面看下supplyAsync()和thenAccept()的例子:
-```
+```Java
public void thenApply() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
CompletableFuture cf = CompletableFuture.supplyAsync(() -> { //实现了Supplier的get()方法
@@ -132,8 +132,8 @@ public void thenApply() throws Exception {
从代码逻辑可以看出,thenApply_test等到了pool-1-thread-1线程完成任务后,才进行的调用,并且拿到了supplye()方法返回的结果,而main则异步执行了,这就避免了Future获取结果时需要阻塞或轮询的弊端。
**exceptionally**
-当任务在执行过程中报错了咋办?exceptionally()方法很好的解决了这个问题,当报错时会去调用exceptionally()方法,它的入参为:Function fn,fn为执行任务报错时的回调方法,下面看看代码示例:
-```
+当任务在执行过程中报错了咋办?exceptionally()方法很好的解决了这个问题,当报错时会去调用exceptionally()方法,它的入参为:Function\ fn,fn为执行任务报错时的回调方法,下面看看代码示例:
+```Java
public void exceptionally() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture cf = CompletableFuture.supplyAsync(() -> {
@@ -172,7 +172,7 @@ thenAcceptAsync: helloworld java.lang.RuntimeException: 测试exceptionally...
CompletableFuture类实现了Future接口和CompletionStage接口,Future大家都经常遇到,但是这个CompletionStage接口就有点陌生了,这里的CompletionStage实际上是一个任务执行的一个“阶段”,CompletionStage详细的内容在下文有介绍。
-```
+```Java
public class CompletableFuture implements Future, CompletionStage {
volatile Object result; // CompletableFuture的结果值或者是一个异常的报装对象AltResult
volatile Completion stack; // 依赖操作栈的栈顶
@@ -207,13 +207,13 @@ public class CompletableFuture implements Future, CompletionStage {
runAsync()做的事情就是异步的执行任务,返回的是CompletableFuture对象,不过CompletableFuture对象不包含结果。runAsync()方法有两个重载方法,这两个重载方法的区别是Executor可以指定为自己想要使用的线程池,而runAsync(Runnable)则使用的是ForkJoinPool.commonPool()。
下面先来看看runAsync(Runnable)的源码:
-```
+```Java
public static CompletableFuture runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
```
这里的asyncPool是一个静态的成员变量:
-```
+```Java
private static final boolean useCommonPool =
(ForkJoinPool.getCommonPoolParallelism() > 1); // 并行级别
private static final Executor asyncPool = useCommonPool ?
@@ -221,7 +221,7 @@ private static final Executor asyncPool = useCommonPool ?
```
回到asyncRunStage()源码:
-```
+```Java
static CompletableFuture asyncRunStage(Executor e, Runnable f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = new CompletableFuture();
@@ -230,7 +230,7 @@ private static final Executor asyncPool = useCommonPool ?
}
```
看到asyncRunStage()源码,可以知道任务是由Executor来执行的,那么可想而知Async类一定是实现了Callable接口或者继承了Runnable类,查看Async类:
-```
+```Java
static final class AsyncRun extends ForkJoinTask
implements Runnable, AsynchronousCompletionTask {
CompletableFuture dep; Runnable fn;
@@ -265,7 +265,7 @@ postComplete()的源码还是有点复杂的,先不急着分析。**先看看C
#### Completion
下面先看看Completion的源码:
-```
+```Java
abstract static class Completion extends ForkJoinTask
implements Runnable, AsynchronousCompletionTask {
volatile Completion next;
@@ -291,7 +291,7 @@ volatile Completion stack;
```
由这个属性可以看出,CompletableFuture其实就是一个链表的一个数据结构。
-```
+```Java
abstract static class UniCompletion extends Completion {
Executor executor; // executor to use (null if none)
CompletableFuture dep; // 代表的依赖的CompletableFuture
@@ -322,7 +322,7 @@ abstract static class UniCompletion extends Completion {
```
claim方法要在执行action前调用,若claim方法返回false,则不能调用action,原则上要保证action只执行一次。
-```
+```Java
static final class UniAccept extends UniCompletion {
Consumer super T> fn;
UniAccept(Executor executor, CompletableFuture dep,
@@ -342,7 +342,7 @@ static final class UniAccept extends UniCompletion {
}
}
```
-```
+```Java
final boolean uniAccept(CompletableFuture a,
Consumer super S> f, UniAccept c) {
Object r; Throwable x;
@@ -370,7 +370,7 @@ final boolean uniAccept(CompletableFuture a,
}
```
对于Completion的执行,还有几个关键的属性:
-```
+```Java
static final int SYNC = 0;//同步
static final int ASYNC = 1;//异步
static final int NESTED = -1;//嵌套
@@ -391,14 +391,14 @@ Completion在CompletableFuture中是如何工作的呢?现在先不着急了
- Runable既不产生结果也不消耗结果
下面看看一个Stage的调用例子:
-```
+```Java
stage.thenApply(x -> square(x)).thenAccept(x -> System.out.println(x)).thenRun(() -> System.out.println())
```
这里x -> square(x)就是一个Function类型的Stage,它返回了x。x -> System.out.println(x)就是一个Comsumer类型的Stage,用于接收上一个Stage的结果x。() ->System.out.println()就是一个Runnable类型的Stage,既不消耗结果也不产生结果。
一个、两个或者任意一个CompletionStage的完成都会触发依赖的CompletionStage的执行,CompletionStage的依赖动作可以由带有then的前缀方法来实现。如果一个Stage被两个Stage的完成给触发,则这个Stage可以通过相应的Combine方法来结合它们的结果,相应的Combine方法包括:thenCombine、thenCombineAsync。但如果一个Stage是被两个Stage中的其中一个触发,则无法去combine它们的结果,因为这个Stage无法确保这个结果是那个与之依赖的Stage返回的结果。
-```
+```Java
@Test
public void testCombine() throws Exception {
String result = CompletableFuture.supplyAsync(() -> {
@@ -422,7 +422,7 @@ stage.thenApply(x -> square(x)).thenAccept(x -> System.out.println(x)).thenRun((
下面开始介绍CompletableFuture的几个核心方法:
**postComplete**
-```
+```Java
final void postComplete() {
CompletableFuture> f = this; Completion h; //this表示当前的CompletableFuture
while ((h = f.stack) != null || //判断stack栈是否为空
@@ -444,7 +444,7 @@ final void postComplete() {
postComplete()方法可以理解为当任务完成之后,调用的一个“后完成”方法,主要用于触发其他依赖任务。
**uniAccept**
-```
+```Java
final boolean uniAccept(CompletableFuture a,
Consumer super S> f, UniAccept c) {
Object r; Throwable x;
@@ -476,7 +476,7 @@ final boolean uniAccept(CompletableFuture a,
**pushStack**
-```
+```Java
final void pushStack(Completion c) {
do {} while (!tryPushStack(c)); //使用CAS自旋方式压入栈,避免了加锁竞争
}
@@ -493,7 +493,7 @@ final boolean uniAccept(CompletableFuture a,
```
光分析源码也没法深入理解其代码原理,下面结合一段示例代码来对代码原理进行分析。
-```
+```Java
@Test
public void thenApply() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
@@ -533,19 +533,19 @@ final boolean uniAccept(CompletableFuture a,
CompletedFuture...isDown
*/
-这段示例代码所做的事情就是supplyAsync(Supplier supplier)休眠200秒之后,返回一个字符串,thenAccept(Consumer super T> action)等到任务完成之后接收这个字符串,并且调用thenApply_test()方法,随后输出 hello world。
+这段示例代码所做的事情就是supplyAsync(Supplier\ supplier)休眠200秒之后,返回一个字符串,thenAccept(Consumer\ super T\> action)等到任务完成之后接收这个字符串,并且调用thenApply_test()方法,随后输出 hello world。
代码中让线程休眠200秒是为了方便观察CompletableFuture的传递过程。
下面就描述下程序的整个运作流程。
**①** 主线程调用CompletableFuture的supplyAsync()方法,传入Supplier和Executor。在supplyAsync()中又继续调用CompletableFuture的asyncSupplyStage(Executor, Supplier)方法。

-来到asyncSupplyStage()方法中,调用指定的线程池,并执行execute(new AsyncSupply(d,f)),这里d就是我们的“源任务”,接下来thenApply()要依赖着这个源任务进行后续逻辑操作,f就是Supplier的函数式编程。
+来到asyncSupplyStage()方法中,调用指定的线程池,并执行execute(new AsyncSupply\(d,f)),这里d就是我们的“源任务”,接下来thenApply()要依赖着这个源任务进行后续逻辑操作,f就是Supplier的函数式编程。

AsyncSupply实现了Runnable的run()方法,核心逻辑就在run()方法里。在run()方法里,先判断d.result == null,判断该任务是否已经完成,防止并发情况下其他线程完成此任务了。f.get()就是调用的Supplier的函数式编程,这里会休眠200秒,所以executor线程池开启的线程会在这里阻塞200秒。
**②** 虽然executor线程池线程阻塞了,但是main线程任然会继续执行接下来的代码。

-main线程会在asyncSupplyStage()方法中返回d,就是我们的“依赖任务”,而这个任务此时还处在阻塞中。接下来main线程会继续执行CompletableFuture的thenAccept(Comsumer super T> action)方法,然后调用CompletableFuture的uniAcceptStage()方法。
+main线程会在asyncSupplyStage()方法中返回d,就是我们的“依赖任务”,而这个任务此时还处在阻塞中。接下来main线程会继续执行CompletableFuture的thenAccept(Comsumer\ super T\> action)方法,然后调用CompletableFuture的uniAcceptStage()方法。

在uniAcceptStage()方法中,会将“依赖任务”、“源任务”、线程池以及Comsumer报装程一个UniAccept对象,然后调用push()压入stack的栈顶中。随后调用UniAccept的tryFire()方法。

@@ -555,7 +555,7 @@ main线程会在asyncSupplyStage()方法中返回d,就是我们的“依赖任
**③** 回到“源任务”,虽然main线程已经结束了整个生命周期,但是executor线程池的线程任然阻塞着的,休眠了200秒之后,继续执行任务。

-然后来到了postComplete()方法。这个方法在前面已经介绍到了,它是CompletableFuture的核心方法之一,做了许多事情。最重要的一件事情就是触发其他依赖任务,接下来调用的方法依次为:UniAccept.tryFire(mode) ——> CompletableFuture.uniAccept(..) ——> Comsumer.accept(s) ——> 输出“hello world”,并输出当前调用线程的线程名。因这个调用链已经在②中介绍过了,所以就不再详细介绍其运作逻辑。
+然后来到了postComplete()方法。这个方法在前面已经介绍到了,它是CompletableFuture的核心方法之一,做了许多事情。最重要的一件事情就是触发其他依赖任务,接下来调用的方法依次为:UniAccept.tryFire(mode) ——\> CompletableFuture.uniAccept(..) ——\> Comsumer.accept(s) ——\> 输出“hello world”,并输出当前调用线程的线程名。因这个调用链已经在②中介绍过了,所以就不再详细介绍其运作逻辑。
**小结:** 通过这个小示例,终于理解到了“源任务”和“依赖任务”之间的调用关系,以及CompletableFuture的基本运作原理。然而CompletableFuture还有其他的方法需要去深入分析,由于篇幅所限就不再赘述,感兴趣的读者可以以debug的模式去一点一点分析CompletableFuture其他方法的底层原理。这里不得不说Java并发包作者Doug Lea大神真的太厉害了,阅读他的源码之后,可以发现他写的代码不能以技术来形容,而应该使用“艺术”来形容。
diff --git "a/note/Netty/\344\272\214\350\277\233\345\210\266.md" "b/note/Netty/\344\272\214\350\277\233\345\210\266.md"
index 69bfb3b..97f7667 100644
--- "a/note/Netty/\344\272\214\350\277\233\345\210\266.md"
+++ "b/note/Netty/\344\272\214\350\277\233\345\210\266.md"
@@ -48,7 +48,7 @@
对于二进制运算,记住一个口诀:
1. 与(&)运算
-
+
运算规则:
``` 0&0=0, 0&1=0, 1&0=0, 1&1=1 ```
@@ -143,7 +143,7 @@ a = 10
**在开源框架底层中算法会用到大量的二进制运算,** 例如:在最近学习的Netty底层源码中,DefaultEventExecutorChooserFactory的底层源码有一个方法, 就是通过 a & (-a)来运算的。
-```
+```Java
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
diff --git "a/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md" "b/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md"
index a35adcc..d26db14 100644
--- "a/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md"
+++ "b/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md"
@@ -24,7 +24,7 @@
### 一、先看看初学者都会的多重if-else判断
-```
+```Java
public int count(int num1, int num2, String operation) {
if (operation.equals("+")) {
return num1 + num2;
@@ -45,7 +45,7 @@ public int count(int num1, int num2, String operation) {
### 二、策略模式实现
#### 2.1 定义一个策略接口: Strategy.class
-```
+```Java
public interface Strategy {
public int doOperation(int num1, int num2);
}
@@ -54,7 +54,7 @@ public interface Strategy {
#### 2.2 创建接口的实现类
Add.java
-```
+```Java
public class Add implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -64,7 +64,7 @@ public class Add implements Strategy{
```
Substract.java
-```
+```Java
public class Substract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -74,7 +74,7 @@ public class Substract implements Strategy{
```
Multiply.java
-```
+```Java
public class Multiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -84,7 +84,7 @@ public class Multiply implements Strategy{
```
Divide.java
-```
+```Java
public class Divide implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -95,7 +95,7 @@ public class Divide implements Strategy{
#### 2.3 创建Context类
-```
+```Java
public class Context {
private Strategy strategy;
@@ -110,7 +110,7 @@ public class Context {
```
#### 2.4 创建实现类
-```
+```Java
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context();
@@ -158,7 +158,7 @@ public class StrategyPatternDemo {
在学习BeanDefinitionReader之前,要先了解一下什么是BeanDefinition
接口BeanDefinition.java
-```
+```Java
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
@@ -172,7 +172,7 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
可以看到BeanDefinition作为一个接口,主要是用于存储从XML配置文件读取Bean信息到JVM内存的一个载体,具体是存储在了BeanDefinition的实现类——RootBeanDefinition中,下面来看看RootBeanDefinition。
-```
+```Java
public class RootBeanDefinition extends AbstractBeanDefinition {
@Nullable
private BeanDefinitionHolder decoratedDefinition;
@@ -187,7 +187,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
```
可以看到RootBeanDefinition不是真正存储Bean信息的载体,继续查看BeanDefinitionHolder
-```
+```Java
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition;
@@ -244,13 +244,13 @@ public interface ResourceLoader {
由于```resourceLoader instanceof ResourcePatternResolver为true```,所以走如下逻辑:
AbstractBeanDefinitionReader.java
-```
+```Java
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
```
AbstractApplicationContext.java
-```
+```Java
@Override
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
@@ -258,7 +258,7 @@ AbstractApplicationContext.java
```
PathMatchingResourcePatternResolver.java
-```
+```Java
@Override
public Resource[] getResources(String locationPattern) throws IOException {
diff --git "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md" "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md"
index dc0a23f..436b444 100644
--- "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md"
+++ "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md"
@@ -17,10 +17,10 @@ Spring容器就相当于一个大的水桶,里面装着很多水——bean对
## 进入正题
在Spring容器的设计中,有两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这个接口实现了容器最基本的功能;另一个是ApplicationContext应用上下文,作为容器的高级形态而存在,它用于扩展BeanFactory中现有的功能。ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,简单一点说:ApplicationContext包含BeanFactory的所有功能。绝大多数“典型”的企业应用和系统,ApplicationContext就是你需要使用的。下面展示一下分别使用BeanFactory和ApplicationContext读取xml配置文件的方式:
-```
+```Java
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
```
-```
+```Java
ApplicationContext bf = new ClassPathXmlApplicationContext("applicationContext.xml");
```
下面先介绍Spring最核心的两个类。
@@ -63,7 +63,7 @@ XML配置文件的读取是Spring中最重要的功能,因为Spring的大部
下面演示一个使用ApplicationContext接口获取xml配置,从而实现一个helloword级别的spring程序:
applicationContext.xml
-```
+```Java
```
测试类
-```
+```Java
public class SpringMain {
public static void main(String[] args) {
//使用spring容器
@@ -101,11 +101,11 @@ Person{name='Bruis', age=23}
通过在断点debug,跟踪程序运行。
1. SpringMain.class
-```
+```Java
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
```
2. ClassPathXmlApplicationContext.class
-```
+```Java
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
@@ -123,7 +123,7 @@ public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh,
}
```
3. AbstractRefreshableConfigApplicationContext.class
-```
+```Java
//给configLocations字符串数组设置值,支持多个配置文件已数组方式同时传入。
public void setConfigLocations(String... locations) {
if (locations != null) {
@@ -149,7 +149,7 @@ public void setConfigLocations(String... locations) {

1. AbstractApplicationContext.class
-```
+```Java
/*
简单来说,Spring容器的初始化时右refresh()方法来启动的,这个方法标志着IOC容器的正式启动。具体来说,这里的启动包括了BeanDefinition和Resource的定位、载入和注册三个基本过程。
*/
@@ -198,7 +198,7 @@ public void refresh() throws BeansException, IllegalStateException {
}
```
2. AbstractRefreshableApplicationContext.class
-```
+```Java
/*
通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取。obtain就是指获得的含义,这个方法obtaiinFreshBeanFactory正是实现BeanFactory的地方,也就是经过这个方法,ApplicationContext就已经拥有了BeanFactory的全部功能(也就是BeanFactory包含在了Spring容器里了)。
*/
@@ -246,7 +246,7 @@ protected final void refreshBeanFactory() throws BeansException {
那么,person的属性是怎么被封装到beanFactory里面的呢?请看下面的源码解析。
3. AbstractXmlApplicationContext.class
-```
+```Java
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
@@ -272,7 +272,7 @@ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansE
首先在refreshBeanFactory()方法中已经初始化了DefaultListableBeanFactory,对于读取XML配置文件,还需要使用XmlBeanDefinitionReader。所以在上述loadBeanDefinitions()中就需要初始化XmlBeanDefinitionReader。在DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了。要注意的地方时,在XmlBeanDefinitionReader初始化时就已经把DefaultListableBeanFactory给注册进去了,所以在XmlBeanDefinitionReader读取的BeanDefinition都会注册到DefaultListableBeanFactory中,也就是经过上述的loadingBeanDefinitions(),类型DefaultListableBeanFactory的变量beanFactory就已经包含了所有**解析好的配置**了。
4. AbstractBeanDefinitionReader.class
-```
+```Java
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
@@ -321,7 +321,7 @@ public int loadBeanDefinitions(String location, @Nullable Set actualRe
}
```
5. PathMatchingResourcePatternResolver.class
-```
+```Java
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
@@ -352,7 +352,7 @@ public Resource[] getResources(String locationPattern) throws IOException {
}
```
6. XmlBeanDefinitionReader.class
-```
+```Java
/*
从XML配置文件中获取bean定义信息
*/
@@ -412,7 +412,7 @@ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

1. XmlBeanDefinitionReader.class
-```
+```Java
/*
注册给定DOM文档中包含的bean定义
*/
@@ -424,7 +424,7 @@ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanD
}
```
2. DefaultBeanDefinitionDocumentReader.class
-```
+```Java
/*
此实现根据“spring-beans”XSD解析bean定义
*/
@@ -507,7 +507,7 @@ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate d
```
2. BeanDefinitionParserDelegate.class
-```
+```Java
/*
解析bean定义本身,而不考虑名称或别名,如果解析期间出错则返回null。
*/
@@ -640,7 +640,7 @@ public void parsePropertyElement(Element ele, BeanDefinition bd) {

然后,就会一路返回到refresh()方法里的加载bean定义信息的方法——loadBeanDefinitions(),此时beanFactory里面就会存在一个带有KV对的ConcurrentHashMap,而这个beanFactory会存放在Spring容器里面。
-```
+```Java
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
@@ -673,7 +673,7 @@ bean的创建和初始化过程是在refresh方法里的invokeBeanFactoryPostPro

1. AbstractApplicationContext.class
-```
+```Java
public void refresh() throws BeansException, IllegalStateException {
...
// 实例剩余的(非懒加载)的单例
@@ -719,7 +719,7 @@ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory b
这里的懒加载的意思,指的是bean单例不是在Spring容器初始化的时候就创建的,而是在要使用该bean的时候,才会创建该bean。
2. DefaultListableBeanFactory.class
-```
+```Java
// 实例剩余的(非懒加载)的单例
@Override
public void preInstantiateSingletons() throws BeansException {
@@ -780,7 +780,7 @@ public void preInstantiateSingletons() throws BeansException {
```
3. AbstractBeanFactory.class
-```
+```Java
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 去除name上存在的工厂bean的前缀
@@ -895,7 +895,7 @@ protected T doGetBean(final String name, @Nullable final Class requiredTy
```
4. DefaultSingletonBeanRegistry.class
-```
+```Java
/*
尝试从缓存中获取单例对象,如果缓存中有该单例对象,并且该对象正在被创建,则从缓存中获取。
*/
@@ -970,7 +970,7 @@ public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {

5. AbstractAutowireCapableBeanFactory.class
-```
+```Java
/*
该类的中心方法:创建bean实例,实例化bean实例,应用bean的后置处理器
diff --git "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md" "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md"
index faa364d..7f8691e 100644
--- "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md"
+++ "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md"
@@ -84,7 +84,7 @@ IOC容器的启动过程就是建立Spring上下文的过程,该上下文是
结合着时序图,再去调试源码,思路会清晰很多。
ContextLoaderListener.class
-```
+```Java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
@@ -117,7 +117,7 @@ public class ContextLoaderListener extends ContextLoader implements ServletConte
这里的ContextLoaderListener是Spring的类,但实现了ServletContextListener接口。这个接口是Servlet API中定义的,提供了与Servlet生命周期结合的回调,也就是说Servlet调用contextInitialized()方法初始化容器时,会回调ContextLoaderListener中实现的contextInitialized()方法,Servlet中的contextDestroyed()方法也同理。观察源码可知,在Web容器中,建立WebApplicationContext的过程是在contextInitialized()方法中完成的。
ContextLoader.class
-```
+```Java
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
// 判断在web容器中是否存在WebApplicationContext,因为在配置中只允许申明一次ServletContextListener,多次声明会扰乱Spring的执行逻辑。
@@ -168,7 +168,7 @@ public WebApplicationContext initWebApplicationContext(ServletContext servletCon
由ContextLoader的源码可知,SpringIOC的载入过程是在ContextLoader类的initWebApplicationContext()方法中完成的。
这里还要介绍一个重要的接口——WebApplicationContext
-```
+```Java
public interface WebApplicationContext extends ApplicationContext {
/**
@@ -184,7 +184,7 @@ public interface WebApplicationContext extends ApplicationContext {
}
```
而WebApplicationContext接口是由XMLWebApplicationContext来实现具体的功能,然后再通过ApplicationContext接口与BeanFactory接口对接,完成Spring容器的功能。然而对于具体的一些Spring容器的实现都是在AbstractRefreshableWebApplicationContext中完成的,这一点和**上篇**讲解的AbstractRefreshableConfigApplicationContext功能类似。initWebApplicationContext()方法最后返回的是一个WebApplicationContext接口,而实际返回的就是XMLWebApplicationContext实现类。XMLWebApplicationContext在基本的ApplicationContext功能的基础上,增加了对**Web环境**和XML配置定义的处理。在XMLWebApplicationContext的初始化过程中,Web容器中的IOC容器被建立起来,从而再整个Web容器中建立起Spring应用。
-```
+```Java
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** 默认读取Spring配置文件的根路径,如果指定其他配置文件,则从这个默认的根路径读取。 */
@@ -234,7 +234,7 @@ public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationC
从源码中可以看到,XMLWebApplicationContext中成员变量存放着默认的读取Spring配置文件的根目录,在生成IOC容器过程中,就会从默认路径/WEB-INF/applicationContext.xml配置文件中或者指定的配置文件路径获取,然后再通过熟悉的loadBeanDefinitions()方法来获取Bean定义信息,最终完成整个上下文的初始化过程。
ContextLoader.class
-```
+```Java
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 这里判断使用什么样的类在Web容器中作为IOC容器
Class> contextClass = determineContextClass(sc);
@@ -274,7 +274,7 @@ protected Class> determineContextClass(ServletContext servletContext) {
下面看看默认的IOC容器是什么。有图有真相:

-```
+```Java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
diff --git "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md" "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md"
index 5003158..014dd27 100644
--- "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md"
+++ "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md"
@@ -13,7 +13,7 @@
- TestMain //main测试方法入口
TestBean.java
-```
+```Java
public class TestBean {
private String testStr = "testStr";
@@ -32,7 +32,7 @@ public class TestBean {
```
AspectJTest.java
-```
+```Java
@Aspect
public class AspectJTest {
@@ -96,7 +96,7 @@ aspectTest.xml
```
TestMain.java
-```
+```Java
public class TestMain {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aspectTest.xml");
@@ -157,7 +157,7 @@ http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNam
无图无真相啊,原来Spring将配置文件中的xmlns配置都解析成了一个一个Java命名解析器。回到我们的关注重点——AopNamespaceHandler,查看源码:
AopNamespaceHandler.class
-```
+```Java
public class AopNamespaceHandler extends NamespaceHandlerSupport {
public AopNamespaceHandler() {
}
@@ -171,7 +171,7 @@ public class AopNamespaceHandler extends NamespaceHandlerSupport {
}
```
可以看到,在init()方法里,Spring对aspectj-autoproxy也就是AnnotationAwareAspectJAutoProxyCreator进行了注册。在详细了解注册原理之前,先说明下在Spring中,所有的解析器都是对BeanDefinitionParser接口的同一实现:
-```
+```Java
public interface BeanDefinitionParser {
@Nullable
BeanDefinition parse(Element var1, ParserContext var2);
@@ -180,7 +180,7 @@ public interface BeanDefinitionParser {
解析入口都是从parse方法开始的。
进入AspectJAutoProxyBeanDefinitionParser类中查看parse的实现逻辑:
-```
+```Java
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
...
@Nullable
@@ -196,7 +196,7 @@ class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
AopNamspaceUtils
-```
+```Java
public abstract class AopNamespaceUtils {
public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";
@@ -231,7 +231,7 @@ public abstract class AopNamespaceUtils {
```
-```
+```Java
public abstract class AopConfigUtils {
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
@@ -264,7 +264,7 @@ registerOrEscalateApcAsRequired方法的作用就是获取AnnotationAwareAspectJ
看看如果proxy-target-class和expose-proxy都为true时,代码的逻辑。
-```
+```Java
public abstract class AopConfigUtils {
...
/*
@@ -307,7 +307,7 @@ public abstract class AopConfigUtils {
经过了useClassProxyingIfNecessary()方法的调用,ParserContext对象中存放好了注册的额外信息(proxy-target-class、expose-proxy值等),这里暂且将ParserContext称为解析上下文。由上面的源码可知,在AopNamespaceUtils类的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中,将获取的org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition和解析上下文一起传入registerComponentIfNecessary方法中,进行Component组件注册。
在随后的registerComponentIfNecessary方法中,经过new BeanComponentDefinition()构造方法的调用,已经将AnnotationAwareAspectJAutoProxyCreator的BeanDefinition注册到了SpringIOC中。
-```
+```Java
public abstract class AopConfigUtils {
...
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
@@ -318,14 +318,14 @@ public abstract class AopConfigUtils {
}
}
```
-```
+```Java
public class BeanComponentDefinition extends BeanDefinitionHolder implements ComponentDefinition {
public BeanComponentDefinition(BeanDefinition beanDefinition, String beanName) {
this(new BeanDefinitionHolder(beanDefinition, beanName));
}
}
```
-```
+```Java
public class BeanDefinitionHolder implements BeanMetadataElement {
public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
this(beanDefinition, beanName, (String[])null);
@@ -341,7 +341,7 @@ public class BeanDefinitionHolder implements BeanMetadataElement {
}
```
然后一路返回,将BeanDefinition存放在解析上下文(ParserContext)中,并在AspectJAutoProxyBeanDefinitionParser类的extendBeanDefinition方法中取出。
-```
+```Java
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
private void extendBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
diff --git "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md" "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md"
index a83ee68..026a120 100644
--- "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md"
+++ "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md"
@@ -14,7 +14,7 @@
#### 1.1 引入简单的CGLIB例子
在讲解CGLIB动态代理之前,先看一下最简单的CGLIB动态代理的例子。
-```
+```Java
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
@@ -71,7 +71,7 @@ com.bruis.learnaop.testcglibaop.EnhancerDemo$$EnhancerByCGLIB$$413eae0d@53e25b76
回到SpringAOP源码。在《深入学习SpringAOP源码(二)》中,介绍到DefaultAopProxyFactory源码部分
-```
+```Java
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
@@ -88,7 +88,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
}
```
从createAopProxy()源码中可以看到,创建SpringAOP有两种方式,一、JDK动态代理;二、CGLIB动态代理;点进ObjenesisCglibAopProxy源码,发现它继承了CglibAopFactory
-```
+```Java
class ObjenesisCglibAopProxy extends CglibAopProxy {
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
// 通过增强器获取代理类的class对象
@@ -122,7 +122,7 @@ class ObjenesisCglibAopProxy extends CglibAopProxy {
createProxyClassAndInstance方法和前面总结的CGLIB创建代理的步骤一样。
继续查看CglibAopProxy是如何准备Enhancer增强器以及创建拦截器链的。
-```
+```Java
class CglibAopProxy implements AopProxy, Serializable {
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
@@ -229,7 +229,7 @@ class CglibAopProxy implements AopProxy, Serializable {
#### 1.3 DynamicAdvisedInterceptor都做了些啥工作?
-```
+```Java
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
@@ -280,7 +280,7 @@ class CglibAopProxy implements AopProxy, Serializable {
#### 1.4 啥是拦截器链?拦截器链从哪获取?
啥是拦截器链?从哪获取拦截器链?下面继续深入DefaultAdvisorChainFactory方法的getInterceptorsAndDynamicInterceptionAdvice()方法
-```
+```Java
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
public List