0%

Call Stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
at net.sf.cglib.core.CodeEmitter.visitMaxs(CodeEmitter.java:842)
at net.sf.cglib.core.CodeEmitter.end_method(CodeEmitter.java:138)
at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:1230)
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:630)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91)
at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at basic.ATest.testFixedValue(ATest.java:34)

底层用的是ASM

Enhancer
create()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Object createHelper() {
this.preValidate();
Object key = KEY_FACTORY.newInstance(
this.superclass != null ? this.superclass.getName() : null, //basic.A
ReflectUtils.getNames(this.interfaces), //null
this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter),//null
this.callbackTypes, //[Lorg.objectweb.asm.Type;@6b9651f3]
this.useFactory,//true
this.interceptDuringConstruction, //true
this.serialVersionUID//true
);
this.currentKey = key;
Object result = super.create(key);
return result;
}
  • this.superclass = basic.A
  • this.filter=ALL_ZERO=
    1
    2
    3
    4
    5
    new CallbackFilter() {
    public int accept(Method method) {
    return 0;
    }
    };
    AbstractClassGenerator.create(Object key)
    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
    protected Object create(Object key) {
    try {
    ClassLoader loader = this.getClassLoader();
    Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
    AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
    if (data == null) {//这里一般data!=null,所以不走
    Class var5 = AbstractClassGenerator.class;
    synchronized(AbstractClassGenerator.class) {
    cache = CACHE;
    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
    if (data == null) {
    Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
    data = new AbstractClassGenerator.ClassLoaderData(loader);
    newCache.put(loader, data);
    CACHE = newCache;
    }
    }
    }

    this.key = key;
    Object obj = data.get(this, this.getUseCache());
    return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
    } catch (RuntimeException var9) {
    throw var9;
    } catch (Error var10) {
    throw var10;
    } catch (Exception var11) {
    throw new CodeGenerationException(var11);
    }
    }
    AbstractClassGenerator.gen
    1
    2
    3
    4
    5
    6
    7
    8
    public Object get(AbstractClassGenerator gen, boolean useCache) {
    if (!useCache) {//这里=true,所以一直走else
    return gen.generate(this);
    } else {
    Object cachedValue = this.generatedClasses.get(gen);
    return gen.unwrapCachedValue(cachedValue);
    }
    }
    这里只有Object cachedValue = this.generatedClasses.get(gen);最重要,这个get方法如下,其实就是里面维护了个map当cache,cache不命中就this.createEntry(key, cacheKey, v);
    1
    2
    3
    4
    5
    public V get(K key) {
    KK cacheKey = this.keyMapper.apply(key);
    Object v = this.map.get(cacheKey);
    return v != null && !(v instanceof FutureTask) ? v : this.createEntry(key, cacheKey, v);
    }

一开始进来是关于要创建的class A的,然后走到Object obj = data.get(this, this.getUseCache());的时候recursive call 自己

  1. 第一次进来key=net.sf.cglib.core.MethodWrapper$MethodWrapperKey
    • gen=net.sf.cglib.core.KeyFactory$Generator@27f723
    • generatedClasses=
      1
      2
      {net.sf.cglib.proxy.Enhancer$EnhancerKey=java.lang.ref.WeakReference@7b69c6ba, basic.A, null, null, {Lnet/sf/cglib/proxy/FixedValue;}, true, true, null=java.util.concurrent.FutureTask@46daef40, 
      net.sf.cglib.core.MethodWrapper$MethodWrapperKey=java.lang.ref.WeakReference@12f41634}
    • 返回net.sf.cglib.core.MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7
  2. 第二次进来key=basic.A的一大串,gen=Enhancer,
    Enhancer.generateClass
    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
    public void generateClass(ClassVisitor v) throws Exception {
    Class sc = this.superclass == null ? Object.class : this.superclass;
    if (TypeUtils.isFinal(sc.getModifiers())) {
    throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());
    } else {
    List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
    this.filterConstructors(sc, constructors);
    List actualMethods = new ArrayList();
    List interfaceMethods = new ArrayList();
    final Set forcePublic = new HashSet();
    getMethods(sc, this.interfaces, actualMethods, interfaceMethods, forcePublic);
    List methods = CollectionUtils.transform(actualMethods, new Transformer() {
    public Object transform(Object value) {
    Method method = (Method)value;
    int modifiers = 16 | method.getModifiers() & -1025 & -257 & -33;
    if (forcePublic.contains(MethodWrapper.create(method))) {
    modifiers = modifiers & -5 | 1;
    }

    return ReflectUtils.getMethodInfo(method, modifiers);
    }
    });
    ClassEmitter e = new ClassEmitter(v);
    if (this.currentData == null) {
    e.begin_class(46, 1, this.getClassName(), Type.getType(sc), this.useFactory ? TypeUtils.add(TypeUtils.getTypes(this.interfaces), FACTORY) : TypeUtils.getTypes(this.interfaces), "<generated>");
    } else {
    e.begin_class(46, 1, this.getClassName(), (Type)null, new Type[]{FACTORY}, "<generated>");
    }

    List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
    e.declare_field(2, "CGLIB$BOUND", Type.BOOLEAN_TYPE, (Object)null);
    e.declare_field(9, "CGLIB$FACTORY_DATA", OBJECT_TYPE, (Object)null);
    if (!this.interceptDuringConstruction) {
    e.declare_field(2, "CGLIB$CONSTRUCTED", Type.BOOLEAN_TYPE, (Object)null);
    }

    e.declare_field(26, "CGLIB$THREAD_CALLBACKS", THREAD_LOCAL, (Object)null);
    e.declare_field(26, "CGLIB$STATIC_CALLBACKS", CALLBACK_ARRAY, (Object)null);
    if (this.serialVersionUID != null) {
    e.declare_field(26, "serialVersionUID", Type.LONG_TYPE, this.serialVersionUID);
    }

    for(int i = 0; i < this.callbackTypes.length; ++i) {
    e.declare_field(2, getCallbackField(i), this.callbackTypes[i], (Object)null);
    }

    e.declare_field(10, "CGLIB$CALLBACK_FILTER", OBJECT_TYPE, (Object)null);
    if (this.currentData == null) {
    this.emitMethods(e, methods, actualMethods);
    this.emitConstructors(e, constructorInfo);
    } else {
    this.emitDefaultConstructor(e);
    }

    this.emitSetThreadCallbacks(e);
    this.emitSetStaticCallbacks(e);
    this.emitBindCallbacks(e);
    if (this.useFactory || this.currentData != null) {
    int[] keys = this.getCallbackKeys();
    this.emitNewInstanceCallbacks(e);
    this.emitNewInstanceCallback(e);
    this.emitNewInstanceMultiarg(e, constructorInfo);
    this.emitGetCallback(e, keys);
    this.emitSetCallback(e, keys);
    this.emitGetCallbacks(e);
    this.emitSetCallbacks(e);
    }

    e.end_class();
    }
    }
    从ClassVisitor名称来看,就是visitor模式,v= DebuggingClassWriter@1014
    sc=basic.A
    methods 拿到了所有A的方法,包括Object里继承的
    methods.toString()=[
    test(Ljava/lang/String;)Ljava/lang/String;,
    test1(Ljava/lang/String;)Ljava/lang/String;,
    equals(Ljava/lang/Object;)Z,
    toString()Ljava/lang/String;,
    hashCode()I,
    clone()Ljava/lang/Object;]

ClassEmitter e就是新生成的代理的信息
e.classInfo.toString()=basic.A$$EnhancerByCGLIB$$a593fb86
basic.A$$EnhancerByCGLIB$$a593fb86 extends A
e.fieldInfo={CGLIB$STATIC_CALLBACKS=net.sf.cglib.core.ClassEmitter$FieldInfo@d45d4ff9, CGLIB$CALLBACK_0=net.sf.cglib.core.ClassEmitter$FieldInfo@6cada385, CGLIB$THREAD_CALLBACKS=net.sf.cglib.core.ClassEmitter$FieldInfo@15b3f091, CGLIB$BOUND=net.sf.cglib.core.ClassEmitter$FieldInfo@4050e674, CGLIB$FACTORY_DATA=net.sf.cglib.core.ClassEmitter$FieldInfo@861434f0}

callbackTypes.toString()=[Lorg.objectweb.asm.Type;@50d0686]就是Lnet/sf/cglib/proxy/FixedValue;然后就交给FixedValueGenerator去generate

ASM

ASM的包里面大量应用了visitor模式,visit package, class, field, method, code block, frame, TryCatchBlock, MultiANewArrayInsn, LocalVariable, LineNumber, MaxsEnd, TypeAnnotation, Parameter.反正就是很细
cglib里也有同一套,加了点null判断什么的,然后就调用了ASM对应的visit方法。
参考cglib的MethodVisitorTee和asm的MethodWriter

这些XXWriter里就是直接写字节码的了,充斥了各种ByteArray,还有硬编码的字符

不知道Frame是干什么用的,里面的execute方法很震撼。。。

Spring AspectJ 概念和术语

springAop
在JoinPoint方法的PointCut处塞进一段Advice逻辑。

Install and HelloWorld

Follow this
http://zhoujingxian.iteye.com/blog/667214

Src

App.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
aspect AppAspect {
void around():call(void App.sayHello()){
System.out.println("before transaction....");
proceed();
System.out.println("after transaction....");
}
}
public class App
{
public void sayHello(){
System.out.println("Hello AspectJ.");
}
public static void main( String[] args )
{
App app = new App();
app.sayHello();
}
}

Decompile

decompile 千万不要用idea原生的,反编译出来跟源代码没啥区别,要用JD-GUI http://jd.benow.ca/

AppAspect.class

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
import org.aspectj.runtime.internal.AroundClosure;

public class AppAspect {
public static AppAspect aspectOf() {
if (ajc$perSingletonInstance == null)
throw new org.aspectj.lang.NoAspectBoundException("AppAspect", ajc$initFailureCause);
return ajc$perSingletonInstance;
}

public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}

private static void ajc$postClinit() {
ajc$perSingletonInstance = new AppAspect();
}

static {
try {
} catch (Throwable localThrowable) {
ajc$initFailureCause = localThrowable;
}
}

public void ajc$around$AppAspect$1$9ee7cbd2(AroundClosure ajc$aroundClosure) {
System.out.println("before transaction....");
ajc$around$AppAspect$1$9ee7cbd2proceed(ajc$aroundClosure);
System.out.println("after transaction....");
}

private static Throwable ajc$initFailureCause;
public static AppAspect ajc$perSingletonInstance;

AppAspect() {
}

static void ajc$around$AppAspect$1$9ee7cbd2proceed(AroundClosure this)
throws Throwable {
}
}

App.class

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
import org.aspectj.runtime.internal.AroundClosure;

public class App {
private static final void sayHello_aroundBody1$advice(App target, AppAspect ajc$aspectInstance, AroundClosure ajc$aroundClosure) {
System.out.println("before transaction....");
AroundClosure localAroundClosure = ajc$aroundClosure;
sayHello_aroundBody0(target);
System.out.println("after transaction....");
}


public void sayHello() {
System.out.println("Hello AspectJ.");
}

public static void main(String[] args) {
App app = new App();
App localApp1 = app;
sayHello_aroundBody1$advice(localApp1, AppAspect.aspectOf(), null);
}

private static final void sayHello_aroundBody0(App paramApp) {
paramApp.sayHello();
}

public App() {
}
}

Call Stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:792)
org.springframework.context.support.GenericApplicationContext.registerBeanDefinition(GenericApplicationContext.java:321)
org.springframework.context.annotation.AnnotationConfigUtils.registerPostProcessor(AnnotationConfigUtils.java:219)
org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors(AnnotationConfigUtils.java:164)
org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors(AnnotationConfigUtils.java:135)
org.springframework.context.annotation.AnnotatedBeanDefinitionReader.<init>(AnnotatedBeanDefinitionReader.java:87)
org.springframework.context.annotation.AnnotatedBeanDefinitionReader.<init>(AnnotatedBeanDefinitionReader.java:70)
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.<init>(AnnotationConfigServletWebServerApplicationContext.java:73)
sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
java.lang.reflect.Constructor.newInstance(Constructor.java:423)
org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170)
org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:124)
org.springframework.boot.SpringApplication.createApplicationContext(SpringApplication.java:595)
org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
org.springframework.boot.SpringApplication.run(SpringApplication.java:1255)
org.springframework.boot.SpringApplication.run(SpringApplication.java:1243)
com.fjd.ssm.SSMApplication.main(SSMApplication.java:14)

看看一个request过来是怎么调用到我定义的Controller的。
版本是sprin-webmvc:5.0.5.RELEASE
https://github.com/fanjingdan012/ssm/tree/multi-tenant为例

Call stack

从下往上读

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
com.fjd.ssm.controller.LoginController.member(LoginController.java:28)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
//3. Filter完,进入正文
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:158)
org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:126)
org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:111)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:84)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
//2. 这里开始调用了6个OncePerRequestFilter
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
//1. ThreadPoolExecutor和NioEndpoint 可以看一下
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:745)
  • 使用ThreadPoolExecutor调用线程池,并且使用Nio
  • 默认调用了6次OncePerRequestFilter

代码和数据

filterChain

filterChain
有7个filter,调用到自己写的Controller正好是第6个:WebMvcMetricsFilter,这7个Filter都可以去看看源代码,处理了什么。有几个很明显的处理Encoding,Context。

这个filterChain哪来的呢?

ApplicationFilterChain 193行

1
2
//chain=this
filter.doFilter(request, response, this);

就是说filterChain就是ApplicationFilterChain
里面维护了一个ApplicationFilterConfig[] filters数组,确实里面就是7个Filter
这个数组的维护代码在ApplicationFilterFactory.createFilterChain115行和131行,调用了ApplicationFilterChain.addFilter(filterConfig),这个createFilterChain也是每个request都要调用一遍
3个参数是什么呢?

  • request不用说了吧
  • wrapper
    wrapper
    wrapper2
  • servlet
    servlet
    这个时候filterChain里还是空的
    ApplicationFilterFactory.java
    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
    public static ApplicationFilterChain createFilterChain(ServletRequest request,
    Wrapper wrapper, Servlet servlet) {

    // If there is no servlet to execute, return null
    if (servlet == null)
    return null;

    // Create and initialize a filter chain object
    ApplicationFilterChain filterChain = null;
    if (request instanceof Request) {//1.这里=true
    Request req = (Request) request;
    if (Globals.IS_SECURITY_ENABLED) {//2.这里=false
    // Security: Do not recycle
    filterChain = new ApplicationFilterChain();
    } else {
    filterChain = (ApplicationFilterChain) req.getFilterChain();//3.其实就是走了这一句
    if (filterChain == null) {
    filterChain = new ApplicationFilterChain();
    req.setFilterChain(filterChain);
    }
    }
    } else {
    // Request dispatcher in use
    filterChain = new ApplicationFilterChain();
    }

    filterChain.setServlet(servlet);
    filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

    // Acquire the filter mappings for this Context
    //4.!!!这里挺关键的,拿到了wrapper.getParent(), 这个context就是
    StandardContext context = (StandardContext) wrapper.getParent();
    FilterMap filterMaps[] = context.findFilterMaps();

    // If there are no filter mappings, we are done
    if ((filterMaps == null) || (filterMaps.length == 0))
    return (filterChain);

    // Acquire the information we will need to match filter mappings
    DispatcherType dispatcher =//5.="REQUEST"
    (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

    String requestPath = null;
    Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
    if (attribute != null){
    requestPath = attribute.toString();
    }//6.attribute="/members/fjd"

    String servletName = wrapper.getName();

    //7.这里就是匹配一下request,把相关的放进filterChain里,这次request是全部都放进去了
    // Add the relevant path-mapped filters to this filter chain
    for (int i = 0; i < filterMaps.length; i++) {
    if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
    continue;
    }
    if (!matchFiltersURL(filterMaps[i], requestPath))
    continue;
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
    context.findFilterConfig(filterMaps[i].getFilterName());
    if (filterConfig == null) {
    // FIXME - log configuration problem
    continue;
    }
    filterChain.addFilter(filterConfig);
    }
    //8.这里filterChain已经有7个了
    // Add filters that match on servlet name second
    for (int i = 0; i < filterMaps.length; i++) {
    if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
    continue;
    }
    //9.这里servletName=dispatcherServlet
    if (!matchFiltersServlet(filterMaps[i], servletName))
    continue;
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
    context.findFilterConfig(filterMaps[i].getFilterName());
    if (filterConfig == null) {
    // FIXME - log configuration problem
    continue;
    }
    //10.这里没走到
    filterChain.addFilter(filterConfig);
    }

    // Return the completed filter chain
    return filterChain;
    }

    注释4这里拿到的context是TomcatEmbeddedContext,已经到了spring boot内嵌的tomcat的包里
    wrapperParentContext
    后面一句话就直接拿到了7个filter:
    filterMaps
    也就是说filter全部都是来自TomcatEmbeddedContext,那这个是哪来的呢?(TODO)

    handler

    DispatcherServlet.doDispatch 991行正式去调用注册的controller

    这个handler哪来的

    1
    2
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    就是mappedHandler.getHandler()拿到的
    mappedHandler是个HandlerExecutionChain,那里有什么东西呢
    mappedHandler
    里面的东西有:
  • handler,就是描述Controller里注册的那个方法的,还包含了它对应的beanFactory,就是DefaultListableBeanFactory
  • interceptors,里面有两个intercepter,是Spring默认就注册了的
    • conversionService WebConversionService,看起来里面就注册了一个converter,就是处理Date和Long的转换的
    • resourceUriProvider resourceUriProvider,这里放了一些path mapper之类的东西,把基础的静态的path map了一下

mappedHandler哪来的

1
mappedHandler = getHandler(processedRequest);

DispatcherServlet.getHandler代码,是DispatcherServlet.handlerMappings成员变量里根据request匹配到的

1
2
3
4
5
6
7
8
9
10
11
12
13
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
...
for (HandlerMapping hm : this.handlerMappings) {
...
//1.核心就这一句
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
...
return null;
}

handlerMappings里面有什么

handlerMappings

getHandler是怎么匹配的

这个HandlerMapping有两个implementation,这里是AbstractHandlerMapping,所以调用的是AbstractHandlerMapping.getHandler:

1
2
3
4
5
6
7
8
9
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//1.最核心就是这句
Object handler = getHandlerInternal(request);
...
//2.后面是handler还有个default,如果default也没有就返回null
//3.再后面是从handler组装一个HandlerExecutionChain出来
}

AbstractHandlerMapping.getHandlerInternal 是个Abstract方法,有4个实现,在这里call的是AbstractUrlHandlerMapping.javagetHandlerInternal

1
2
3
4
5
6
7
8
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);//1. lookupPath="/members/fjd"
Object handler = lookupHandler(lookupPath, request);//2.最核心就是这句
...
//3.后面是handler找不到有个rootHandler,再没有有个defaultHandler,如果handler是以name表示的话再去context里getBean,再把这个root或者defaultHandler包一下,但是这些都不重要,大多数情况下都是在2这里就完成了
}

然后就看一下lookupHandler的实现

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
@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
//1. 是在handlerMap里找匹配,支持urlPath直接匹配,这个优先于pattern匹配
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}

//2. 支持urlPath pattern 匹配
// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}

String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
//3.如果有一堆匹配,只取第一个
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
//4.这里保证path结尾有没有"/"都一样
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}

// No handler found...
return null;
}

this.handlerMap里其实不会有所有的注册的Mapping,比如本次进来就只有1条,是favico的路径,每次进去都是不一样的handlerMap。

总结

到这里为止已经探究了filterChain的来源和handler怎么匹配的源代码。
这次就先到这儿吧。

Spring 源码阅读
https://github.com/fanjingdan012/ssm/tree/multi-tenant为例,看源码一定要配合数据看,否则容易迷失在代码里

调用顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
st=>start: Start|past:>https://github.com/fanjingdan012/ssm/tree/multi-tenant[blank]
op16=>operation: org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:356)
op15=>operation: org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:144)
op14=>operation: org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:117)
op13=>operation: org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:328)
op12=>operation: org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233)
op11=>operation: org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:273)
op10=>operation: org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:93)
op9=>operation: org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694)
op8=>operation: org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532)
op2=>operation: ...
op1=>operation: com.fjd.ssm.SSMApplication.main(SSMApplication.java:14)
st->op1->op2->op8->op9->op10->op11->op12->op13->op14->op15->op16

ConfigurationClassBeanDefinitionReader的数据

成员变量

  • BeanDefinitionRegistry registry;
    DefaultListableBeanFactory
    是DefaultListableBeanFactory,里面还维护了一个beanDefinitionMap,包含了SSMApplication(程序入口),Controller,Configuration,Service,DataSource,还有Spring自己的一些Processor等,其中自己注册的都是Generic bean,而Spring自己的都是Root bean
  • ResourceLoader resourceLoader;
    AnnotationConfigServletWebServerApplicationContext
    是AnnotationConfigServletWebServerApplicationContext
  • Environment environment
    StandardServletEnvironment
    是StandardServletEnvironment,里面指定了activeProfiles,defaultProfiles,都是LinkedHashSet(非线程安全)的,里面还有个propertySources不知道是干什么的

    方法

  • loadBeanDefinitions(Set<ConfigurationClass> configurationModel) 里传进去的参数里已经有124个·ConfigurationClass
    LinkedHashSet
    有自己定义的ServiceControllerSSMApplicationConfiguration,还有Spring的一系列Configuration,大多数在autoconfigure包里,包括servlet层的一些Dispatcher,出错处理等,还有jdbc的一系列配置,最后还有Mybatis的MybatisAutoConfiguration,还有很细节的一些json处理等等之类的Configuration。有空应该去看看这些源代码是干了什么。

    代码

    ConfigurationClassBeanDefinitionReader.java
    1
    2
    3
    4
    5
    6
    public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
    loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
    }
    对刚才的124个ConfigurationClass逐个load
    loadBeanDefinitionsForConfigurationClass是要干什么事呢? 处理了Import的loadBeanDefinitions,调用了register的registerBeanDefinitions方法
    ConfigurationClassBeanDefinitionReader.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
    TrackedConditionEvaluator trackedConditionEvaluator) {

    if (trackedConditionEvaluator.shouldSkip(configClass)) {
    String beanName = configClass.getBeanName();
    if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
    this.registry.removeBeanDefinition(beanName);
    }
    this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
    return;
    }

    if (configClass.isImported()) {
    registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

registerBeanDefinitions方法最主要就是call DefaultListableBeanFactory的 registerBeanDefinition方法。 这个registerBeanDefinition其实就是维护DefaultListableBeanFactory的成员变量:

  • beanDefinitionMap
  • beanDefinitionNames
  • manualSingletonNames
    这些Map基本上都用了ConcurrentHashMap,保证线程安全,也保证有一定的performance。
    对已有的bean做更新(Override),或者add,或者remove
    DefaultListableBeanFactory.java
    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
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
    try {
    ((AbstractBeanDefinition) beanDefinition).validate();
    }
    catch (BeanDefinitionValidationException ex) {
    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    "Validation of bean definition failed", ex);
    }
    }

    BeanDefinition oldBeanDefinition;

    oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    if (oldBeanDefinition != null) {
    if (!isAllowBeanDefinitionOverriding()) {
    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    "': There is already [" + oldBeanDefinition + "] bound.");
    }
    else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    if (this.logger.isWarnEnabled()) {
    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
    "' with a framework-generated bean definition: replacing [" +
    oldBeanDefinition + "] with [" + beanDefinition + "]");
    }
    }
    else if (!beanDefinition.equals(oldBeanDefinition)) {
    if (this.logger.isInfoEnabled()) {
    this.logger.info("Overriding bean definition for bean '" + beanName +
    "' with a different definition: replacing [" + oldBeanDefinition +
    "] with [" + beanDefinition + "]");
    }
    }
    else {
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Overriding bean definition for bean '" + beanName +
    "' with an equivalent definition: replacing [" + oldBeanDefinition +
    "] with [" + beanDefinition + "]");
    }
    }
    this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
    if (hasBeanCreationStarted()) {
    // Cannot modify startup-time collection elements anymore (for stable iteration)
    synchronized (this.beanDefinitionMap) {
    this.beanDefinitionMap.put(beanName, beanDefinition);
    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
    updatedDefinitions.addAll(this.beanDefinitionNames);
    updatedDefinitions.add(beanName);
    this.beanDefinitionNames = updatedDefinitions;
    if (this.manualSingletonNames.contains(beanName)) {
    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
    updatedSingletons.remove(beanName);
    this.manualSingletonNames = updatedSingletons;
    }
    }
    }
    else {
    // Still in startup registration phase
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName);
    this.manualSingletonNames.remove(beanName);
    }
    this.frozenBeanDefinitionNames = null;
    }

    if (oldBeanDefinition != null || containsSingleton(beanName)) {
    resetBeanDefinition(beanName);
    }
    }

为了做隔离性比较好的multi-tenant app,决定做schema based multi-tenant,这就需要

  • Runtime切换DataSource
  • 为了使添加新tenant不用重启,最好还能实现Runtime添加DataSource

所以分2步实现这两个功能。

代码:https://github.com/fanjingdan012/ssm
目前有3个branch

  • master是基础版Spring+Mybatis+Mariadb,能Read一个DataSource
  • multi-data-source是实现Runtime切换DataSource的
  • multi-tenant是实现现Runtime添加DataSource的

预先定义DataSource,Runtime切换

效果

  • 数据库准备,这里用了mariadb,用了两个schema, test和test2,里面是同样的一张member表,插入一点不同的数据
    这里写图片描述
  • 用header控制Tenant-ID,从而访问不同的DataSource
    这里写图片描述

    代码

    写一个MultitenantDataSource.java, MultitenantDataSource extends AbstractRoutingDataSource

看一下AbstractRoutingDataSource的源代码

1
2
3
4
5
6
7
8
9
10
11
12
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}

里面有一个resolvedDataSources的Map,存储了多个DataSource,会调用determineCurrentLookupKey()来Runtime决定使用哪个DataSource,如果没有指定的那个Key,那么就会使用DefaultDataSource
所以使用它就是需要

  • Override determineCurrentLookupKey()方法,定义tenantId作为key
    1
    2
    3
    4
    5
    6
    public class MultitenantDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
    return TenantContext.getCurrentTenant();
    }
    }
  • 调用setTargetDataSources方法去把Map填进去
  • 调用setDefaultTargetDataSource 方法把DefaultDataSource设置好,最好是将DefaultDataSource设置成tenant管理数据库,保存tenant相关信息,但是作为一个demo,本项目就比较简单,直接把它设为一个tenant数据库
    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
    @Configuration
    public class MultitenantConfiguration {

    @Bean
    public MultitenantDataSource multitenantDataSource() {
    Map<Object,Object> resolvedDataSources = new HashMap<>();
    //db1
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create()
    .url("jdbc:mysql://localhost/test")
    .username("root")
    .password("******");
    resolvedDataSources.put("tenant1",dataSourceBuilder.build());
    resolvedDataSources.put("Default",dataSourceBuilder.build());

    //db2
    DataSourceBuilder dataSourceBuilder2 = DataSourceBuilder.create()
    .url("jdbc:mysql://localhost/test2")
    .username("root")
    .password("******");
    resolvedDataSources.put("tenant2",dataSourceBuilder2.build());


    MultitenantDataSource dataSource = new MultitenantDataSource();
    dataSource.setDefaultTargetDataSource(resolvedDataSources.get("Default"));
    dataSource.setTargetDataSources(resolvedDataSources);


    dataSource.afterPropertiesSet();

    return dataSource;
    }

  • 在Controller里添加从header读取tenantId的逻辑
    XXController.java
    1
    2
    3
    4
    public Member member(@PathVariable("name") String name, @RequestHeader("X-TenantID") String tenantName){
    TenantContext.setCurrentTenant(tenantName);
    ...
    }

Runtime 添加DataSource

效果

  • 注册新的tenant,直接把jdbc url,username, password通过request parameter传入,返回success
  • 通过header控制tenantId,访问新的tenant(DataSource)的数据
    这里写图片描述

    代码

  • 写一个MultitenantDataSourceRegister.java (implements ImportBeanDefinitionRegistrar), 就要实现 registerBeanDefinitions方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void registerBeanDefinitions(AnnotationMetadata annotaion, BeanDefinitionRegistry registry) {
    System.out.println("registerBeanDefinitions");
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClass(MultitenantDataSource.class);
    beanDefinition.setSynthetic(true);
    MutablePropertyValues mpv = beanDefinition.getPropertyValues();

    mpv.addPropertyValue("defaultTargetDataSource", getDefaultDataSources().get("Default"));
    mpv.addPropertyValue("targetDataSources",getDefaultDataSources());
    registry.registerBeanDefinition("dataSource", beanDefinition);
    }
  • MultitenantConfiguration上添加@Import(MultitenantDataSourceRegister.class)
    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    @Import(MultitenantDataSourceRegister.class)
    public class MultitenantConfiguration {

    @Autowired
    private MultitenantDataSource multitenantDataSource;

    }
  • MultitenantDataSource里维护一个Map用来管理DataSources
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class MultitenantDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
    return TenantContext.getCurrentTenant();
    }
    private ConcurrentHashMap<String, DataSource> backupTargetDataSources = new ConcurrentHashMap<>();

    public void addDataSourceToTargetDataSource(String key ,DataSource ds){
    this.backupTargetDataSources.put(key, ds);
    this.setTargetDataSource(this.backupTargetDataSources);
    }


    public void setTargetDataSource(Map targetDataSource){
    super.setTargetDataSources(targetDataSource);
    this.afterPropertiesSet();
    }
    }
  • XXController里添加注册DataSource的API
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @GetMapping("/api/tenant/register")
    @ResponseBody
    public String tenantRegister(@RequestParam String username, @RequestParam String password, @RequestParam String url, @RequestParam String tenantName) {
    MultitenantDataSource multitenantDataSource = ctx.getBean(MultitenantDataSource.class);
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create()
    .url(url)
    .username(username)
    .password(password);
    multitenantDataSource.addDataSourceToTargetDataSource(tenantName, dataSourceBuilder.build());
    return "success";
    }

Set up

1
2
3
hexo init
cd themes
git clone git@github.com:theme-next/hexo-theme-next.git

Problem

hexo d not working
try this in _config.yml

1
2
3
4
deploy:
type: git
repo: https://{yourname}:{yourpassword}@github.com/{yourname}/{yourname}.github.io.git
branch: master

Md sequence and flowchart

sequence

code:

1
2
3
Alice->Bob: Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!

result:

1
2
3
Alice->Bob: Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!

flowchart

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End:>http://www.google.com
op1=>operation: My Operation|past
op2=>operation: Stuff|current
sub1=>subroutine: My Subroutine|invalid
cond=>condition: Yes
or No?|approved:>http://www.google.com
c2=>condition: Good idea|rejected
io=>inputoutput: catch something...|request

st->op1(right)->cond
cond(yes, right)->c2
cond(no)->sub1(left)->op1
c2(yes)->io->e
c2(no)->op2->e

result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
st=>start: Start|past:>http://www.google.com[blank]
e=>end: End:>http://www.google.com
op1=>operation: My Operation|past
op2=>operation: Stuff|current
sub1=>subroutine: My Subroutine|invalid
cond=>condition: Yes
or No?|approved:>http://www.google.com
c2=>condition: Good idea|rejected
io=>inputoutput: catch something...|request

st->op1(right)->cond
cond(yes, right)->c2
cond(no)->sub1(left)->op1
c2(yes)->io->e
c2(no)->op2->e

让 Hexo 搭建的博客支持 LaTeX

memory.png

存储技术(书6)

  • RAM(Random Access Memory)

    • SRAM(Static RAM)

      • 快,10倍速度DRAM
      • 贵,100倍价格DRAM
      • 做Cache
    • DRAM(Dynamic RAM)

      • 主存
      • frame buffer
      • 增强版DRAM
        • SDRAM(Synchronous DRAM)
        • Double Data-Rate SDRAM(DDR SDRAM)
          • DDR(2bit prefetch buffer)
          • DDR2(4bit …)
          • DDR3(8bit …)
    • 访问主存


      • movq A, %rax

        1. CPU通过总线接口发起读事务,把地址A放到系统总线上
        2. A走过I/O桥接器,走到存储器总线
        3. 主存感觉并获取A,读取A的内容,写到存储器总线
        4. I/O桥接器翻译转换成系统总线信号
        5. CPU感觉并获取数据,copy给%rax

      • movq %rax A

        1. CPU把A放在总线上
        2. 主存从总线获取A,并等待数据到达
        3. CPU把%rax的数据copy到总线
        4. 主存拿到数据存入A
  • 断电可以存储的

    • ROM(Read-only memory)
    • PROM(Programmable ROM) 可以烧制一次程序
    • EPROM(Eraseable PROM) 可以重复烧制
    • EEPROM(Electrically Eraseable PROM) 比如Flash memory
    • 磁盘
      见图

Locality局部性

  • 时间局部性:一个变量在集中的时间里一直访问就会一直cache hit
  • 空间局部性:相关数据放在一起(一个 block)就会一起进缓存

Hierarchy

memory-hierarchy.png

Disk

见上图

Cache

见上图

  • 上层是下层的子集
  • 数据以块传输
  • 下层更大,慢,便宜
  • 读:hit的话直接返回,miss的话replace
    • cache hit
    • cache miss
      • cold miss、compulsory miss(强制性不命中)
      • conflict miss,e.g.用了下一层的块i放在上一层的块(i mod 4)中的放置策略,然后访问块0,块4,块0,块4。。。就会一直miss
      • capacity miss:缓存太小了
    • replacing、evicting victim block,只要发生了不命中就会执行某替换策略
      • 策略
        • 换掉谁?e.g.Least Recent Used(LRU), Frequently(LFU)
        • 新的放哪? e.g. 随机, 下一层的块i放在上一层的块(i mod 4)中
  • 写:
    • write hit
      • write-through:直接更新主存
      • write-back(真的用的):推迟更新主存,减少bus的使用,需要额外维护一个dirty bit,标记是不是改过
    • write miss
      • write-allocate(真的用的):先加载到cache,再更新+write-back
      • not-write-allocate:直接更新主存+write-through
  • 保存啥的
    • i-cache:存指令
    • d-cache:存数据
    • unified cache:两个都存,e.g. L2
  • KPI
    • miss rate = 1- hit rate
    • hit time(命中花多久)、 miss penalty(不命中花多久)

Virtual Memory(书10.1-10.8)

Why Virtual Memory

  • 4k/4m/…一个page,搞cache
  • memory管理,每个进程有自己的地址空间, 2G的物理内存虚拟出4G没问题
  • memory保护,可以做权限管理,在PTE上加权限位,MMU负责检查

    How

    见上图

    Memory mapping

Heap & Malloc(书10.9)