Nothing that has meaning is easy. - 有意义的事从来都不是容易的。

Spring Framework之初识架构

一、前言

Spring Framework框架被人们亲切地称为 “ Spring ”,也就是春天的意思,柳暗花明又一村。
随着Spring的普及,越来越多的开发商加入了Spring的大家庭,Spring究竟为何物?这也需要我们更加全面地去了解,下面我通过以下5点带你们走入Spring的世界:
1.Spring Framework是什么?
2.什么是IOC、DI?
3.什么是AOP?
4.初步实现一个自己的IOC(依赖Spring Framework的核心原理)🌟
5.如何使用Spring Framework?

二、Spring Framework是什么

Spring Framework是由其IOC、DI和AOP为核心的开源项目。
现在已经成为每个公司所必备的底层项目之一,故现在的项目绝大部分都是Srping应用项目。
于 “ 2008-07-10 22:00 ”构建其开源仓库,但它研发的时间却更早。

以Rod Johnson和Juergen Hoeller两人为核心的一个开发团队经过努力,于2004年3月份发布了Spring Framework 1.0版 - 摘自百度百科

三、什么是IOC、DI

IOC ( Inversion Of Control ) 控制反转,指将对象创建的生命周期交给Spring Framework去进行管理、构建。
DI ( Dependency Injection ) 依赖注入,指对象间相互依赖的构建场景下,Spring Framework会自动进行其依赖对象的注入。

四、什么是AOP

AOP ( Aspect Oriented Programming ) 面向切面编程,通过预编译和运行期间动态代理的方式来实现程序统一维护的一种技术。
通过AOP对各业务进行隔离,使其耦合度降低,提高程序的可重用性,同时也提高了其开发效率。

五、初步实现一个自己的IOC(依赖Spring Framework的核心原理)🌟

完成了bean的基本构建、bean的懒加载功能及IOC基础的生命周期功能,接下来我们大概观看整个IOC容器的运行状态。

项目gitee地址:https://gitee.com/xusccn/my-ioc
此实现会让你最简单的了解Spring Framework的基本原理🌟

MainTest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainTest {
static final Logger LOG = LoggerFactory.getLogger(MainTest.class);// 获取日志对象

@Test
public void ApplicationContextTest() {
// 1⃣️:构建应用上下文
ApplicationContext context = new ApplicationContext();
// 2⃣️:注册一个懒加载的User bean
context.registerBeanDefinition(User.class,true,true);
// 3⃣️:刷新应用上下文
context.refresh();
// 4⃣️:获取IOC容器bean对象
Object bean = context.getBean(User.class);
// 触发懒加载
LOG.info("{}",bean);
}
}

1⃣️:构建应用上下文

1
2
3
public ApplicationContext() {
this.beanFactory = new BeanFactory();
}

2⃣️:注册一个懒加载的User bean

1
2
3
4
5
6
7
8
9
10
11
12
13
// 通过类、懒加载属性、单例属性注入bean定义
public void registerBeanDefinition(Class clazz, boolean lazyInitialized, boolean single) {
beanFactory.registerBeanDefinition(clazz,lazyInitialized,single);
}

// 通过类、懒加载属性、单列属性注册bean定义
public void registerBeanDefinition(@NonNull Class clazz, boolean lazyInitialized, boolean single) {
BeanDefinition beanDefinition = new BeanDefinition(clazz, lazyInitialized, single);
beanDefinitionMap.put(beanDefinition.getClassName(),beanDefinition);
if (beanDefinition.isSingle() && !beanDefinition.isLazyInitialized()) {
singleBeanNameList.add(beanDefinition.getClassName());
}
}

3⃣️:刷新应用上下文

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
// 刷新上下文
public void refresh(){
synchronized (this.startupShutdownMonitor) {
// 1.准备刷新
prepareRefresh();

try {
// 2.实例化所有剩余的单列(未懒加载的)
finishBeanFactoryInitialization(beanFactory);

// 3.完成刷新
finishRefresh();
}catch (Exception e) {
// 销毁失败的beans
destroyBeans();

// 退出刷新
cancelRefresh();
throw e;
} finally {
// 4.重置通用缓存
resetCommonCaches();
}
}
}

4⃣️:获取IOC容器bean对象

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
// 通过类获取bean对象
public Object getBean(Class beanClass) {
return beanFactory.getBean(beanClass);
}

// 通过bean类获取bean对象
public Object getBean(@NonNull Class beanClass){
return getBean(beanClass.getName());
}

// 通过bean名获取bean对象
public Object getBean(String beanName){
BeanDefinition beanDefinition;
if ((beanDefinition = beanDefinitionMap.get(beanName)) == null) {
throw new IllegalStateException("beanName:[ " + beanName +" ] not register");
}
if (beanDefinition.isSingle()) {
return doGetSingleBean(beanName);⬅️
}
return initBean(beanName);
}

// 实际获取bean实现,由子类做具体实现
public abstract Object doGetSingleBean(String beanName);

// 最终获取bean对象的实现
@Override
public Object doGetSingleBean(String beanName) {
Object bean;
if ((bean = this.singletonObjects.get(beanName)) == null) {
//init bean
synchronized (singletonObjects) {
// DCL
if ((bean = this.singletonObjects.get(beanName)) == null) {
bean = initBean(beanName);⬅️
singletonObjects.put(beanName, bean);
}
}
}
return bean;
}

// 初始化bean
public Object initBean(String beanName){
Object bean = null;
BeanDefinition beanDefinition;
if ((beanDefinition = beanDefinitionMap.get(beanName)) == null) {
throw new IllegalStateException("beanName:[ " + beanName +" ] not register");
}

try {
if (beanDefinition.isLazyInitialized()) {
// lazy
bean = ProxyUtil.proxy(beanDefinition);
} else {
// promptly
bean = beanDefinition.getClazz().getDeclaredConstructor().newInstance();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return bean;
}

六、如何使用Spring Framework

依赖导入
gradle

1
testImplementation 'org.springframework:spring-context:5.3.5'

maven

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>

程序使用
步骤一(测试类):SpringFrameworkTest

1
2
3
4
5
6
7
8
9
10
11
public class SpringFrameworkTest {
private Logger log = LoggerFactory.getLogger(SpringFrameworkTest.class);

@Test
public void Test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring.xml"},false);
context.refresh();
log.info("{}",context.getBean("systemPrint"));
log.info("{}",context.getBean("systemPrint"));
}
}

步骤二(配置文件):spring.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="systemPrint" class="SystemPrint">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>

步骤三(类对象):SystemPrint

1
2
3
public class SystemPrint {

}

程序执行输出

1
2
[Test worker] INFO SpringFrameworkTest - bean.SystemPrint@207fec97
[Test worker] INFO SpringFrameworkTest - bean.SystemPrint@207fec97

日志之SLF4J解析(三)

一、前言:

本文只列出重要的知识点,具体如以下3点:
1.SLF4J是什么?
2.SLF4J如何使用?
3.SLF4J加载解析?

二、SLF4J是什么

SLF4J是qos-ch的开源项目slf4j,全称为 “ Simple Logging Facade for Java ”。
SLF4J解决了JUL动态查找的性能消耗,同时也解决了它,是现在使用最广的日志门面。

三、SLF4J如何使用

依赖导入
gradle

1
2
implementation 'org.slf4j:slf4j-api:1.7.30'
implementation 'org.slf4j:slf4j-simple:1.7.30'//SLF4J需要依赖一个日志实现

maven

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
</dependency>

程序使用

1
2
3
4
5
6
7
8
9
10
import org.slf4j.LoggerFactory;

public class Logger {
static final org.slf4j.Logger LOG = LoggerFactory.getLogger(Logger.class);//获取日志对象

public static void main(String[] args) {
LOG.info("Hello JCL");//打印普通日志
LOG.info("{}",LOG.getClass());//打印日志对象类路径
}
}

程序执行输出

1
2
[main] INFO cn.xusc.Logger - Hello JCL
[main] INFO cn.xusc.Logger - class org.slf4j.impl.SimpleLogger

四、SLF4J加载解析

步骤一:程序代码加载

1
LoggerFactory.getLogger(Logger.class);//获取日志对象⬅️

步骤二:LoggerFactory

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
//获取日志实例
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
return logger;
}

public static Logger getLogger(String name) {
//获取日志工厂
ILoggerFactory iLoggerFactory = getILoggerFactory();⬅️
//获取日志实例
return iLoggerFactory.getLogger(name);⬅️
}

//获取日志工厂
public static ILoggerFactory getILoggerFactory() {
return getProvider().getLoggerFactory();⬅️
}

//获取提供者
static SLF4JServiceProvider getProvider() {
//一开始未初始化
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {//DCL(Double Check Lock)
INITIALIZATION_STATE = ONGOING_INITIALIZATION;//正在进行初始化
performInitialization();//执行初始化 -> SPI查找服务提供者1⃣️
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION://成功初始化
return PROVIDER;//提供者
case NOP_FALLBACK_INITIALIZATION://NOP回退初始化
return NOP_FALLBACK_FACTORY;//NOP回退工厂
case FAILED_INITIALIZATION://初始化失败
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);//抛出异常
case ONGOING_INITIALIZATION://正在初始化
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_PROVIDER;//SUBST提供者
}
throw new IllegalStateException("Unreachable code");//提供者获取异常
}

//获取日志工厂
public ILoggerFactory getLoggerFactory();⬅️

步骤三:SimpleServiceProvider(简单服务提供者实现)

1
2
3
4
5
6
7
8
9
10
11
12
//简单服务提供者
public class SimpleServiceProvider implements SLF4JServiceProvider {
private ILoggerFactory loggerFactory;
public ILoggerFactory getLoggerFactory() {
return loggerFactory;⬅️
}
@Override
public void initialize() {
//初始化简单日志工厂
loggerFactory = new SimpleLoggerFactory();
}
}

核心点1⃣️

把步骤二的执行初始化放到这里,主要是为了能更直观清晰的阅读整个流程。

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
//执行初始化
private final static void performInitialization() {
//查找并绑定
bind();⬅️
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
//验证是否为支持版本
versionSanityCheck();
}
}

//服务提供者查找并绑定
private final static void bind() {
try {
//查找所有服务提供者
List<SLF4JServiceProvider> providersList = findServiceProviders();⬅️
//验证服务提供者的唯一性
reportMultipleBindingAmbiguity(providersList);
//验证日志服务提供者是否存在
if (providersList != null && !providersList.isEmpty()) {
PROVIDER = providersList.get(0);//获取第一个服务提供者
PROVIDER.initialize();//调用服务提供者初始化流程
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;//设置成功标识
reportActualBinding(providersList);
} else {
//没有找到服务提供者,设置回退初始化标识、打印错误日志
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("No SLF4J providers were found.");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_PROVIDERS_URL + " for further details.");

//输出到可能存在的日志绑定
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
//提交绑定清理
postBindCleanUp();
} catch (Exception e) {
failedBinding(e);//异常绑定
throw new IllegalStateException("Unexpected initialization failure", e);
}
}

//查找所有的服务提供者
private static List<SLF4JServiceProvider> findServiceProviders() {
//SPI加载机制
ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class);
List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>();
for (SLF4JServiceProvider provider : serviceLoader) {
providerList.add(provider);
}
return providerList;
}

日志之JCL解析(二)

一、前言:

本文只列出重要的知识点,具体如以下3点:
1.JCL是什么?
2.JCL如何使用?
3.JCL加载解析?

二、JCL是什么

JCL是apache的开源项目commons-logging,全称为 “ Jakarta Commons Logging ” ,也可称为 “ Apache Commons Logging ”。
JCL解决了日志框架混乱的原始时代,是首发日志门面的长老人物。

三、JCL如何使用

依赖导入
gradle

1
implementation 'commons-logging:commons-logging:1.2'

maven

1
2
3
4
5
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

程序使用

1
2
3
4
5
6
7
8
9
10
11
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Logger {
static final Log LOG = LogFactory.getLog(Logger.class);//获取日志对象

public static void main(String[] args) {
LOG.info("Hello JCL");//打印普通日志
LOG.info(LOG.getClass());//打印日志对象类路径
}
}

程序执行输出

1
2
3
4
4月 01, 2021 1:33:25 下午 cn.test.log.Logger main
信息: hello jcl
4月 01, 2021 1:33:26 下午 cn.test.log.Logger main
信息: class org.apache.commons.logging.impl.Jdk14Logger

四、JCL加载解析

步骤一:程序代码加载

1
LogFactory.getLog(Logger.class);⬅️

步骤二:LogFacoty

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
//获取日志对象
public static Log getLog(final Class clazz) throws LogConfigurationException {
return getFactory().getInstance(clazz);⬅️
}

//获取日志工厂对象(ps:只要没有额外配置都是 " LogFactoryImpl " 对象)
public static LogFactory getFactory() throws LogConfigurationException {
//上下文类加载器
final ClassLoader contextClassLoader = getContextClassLoaderInternal();
//从缓存工厂获取日志工厂对象
LogFactory factory = getCachedFactory(contextClassLoader);
//缓存工厂有直接返回
if (factory != null) {
return factory;
}
// ...
if (factory == null) {
//构建 " LogFactoryImpl " 工厂实现
factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);⬅️
}
if (factory != null) {
//存入缓存工厂供下一次快速获取
cacheFactory(contextClassLoader, factory);
}
return factory;
}

// 子类实现
public abstract Log getInstance(String name) throws LogConfigurationException;⬅️

步骤三:LogFactoryImpl(默认日志工厂实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//获取日志实例
@Override
public Log getInstance(final Class clazz) throws LogConfigurationException {
return getInstance(clazz.getName());⬅️
}

@Override
public Log getInstance(final String name) throws LogConfigurationException {
//实例表获取缓存日志实例
Log instance = (Log) instances.get(name);
if (instance == null) {
//构建日志实例
instance = newInstance(name);⬅️
//缓存到实例表
instances.put(name, instance);
}
return instance;
}

//构建日志实例
protected Log newInstance(final String name) throws LogConfigurationException {
Log instance;
//没有日志构造进入,有日志构造直接创建
if (logConstructor == null) {
//发现日志实现
instance = discoverLogImplementation(name);⬅️
}
else {
final Object params[] = { name };
instance = (Log) logConstructor.newInstance(params);
}

if (logMethod != null) {
final Object params[] = { this };
logMethod.invoke(instance, params);
}
return instance;
}

//实现类集
private static final String[] classesToDiscover = {
"org.apache.commons.logging.impl.Log4JLogger",
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};

//发现日志实现
private Log discoverLogImplementation(final String logCategory) throws LogConfigurationException {
Log result = null;
// ...
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
//从具体日志类中创建日志实例
result = createLogFromClass(classesToDiscover[i], logCategory, true);⬅️
}
//...
return result;
}

//创建日志实例
private Log createLogFromClass(final String logAdapterClassName,
final String logCategory,
final boolean affectState)
throws LogConfigurationException {
Log logAdapter = null;
Constructor constructor = null;
Class logAdapterClass = null;
//获取应用类加载
ClassLoader currentCL = getBaseClassLoader();

for(;;) {
Class c;
try {
//加载实现类
c = Class.forName(logAdapterClassName, true, currentCL);
} catch (final ClassNotFoundException originalClassNotFoundException) {
// ...
}

//获取类构造
constructor = c.getConstructor(logConstructorSignature);
//构建实例
final Object o = constructor.newInstance(params);⬅️

//日志对象验证
if (o instanceof Log) {
logAdapterClass = c;
logAdapter = (Log) o;
break;
}
}
if (logAdapterClass != null && affectState) {
this.logClassName = logAdapterClassName;//日志适配器类名
this.logConstructor = constructor;//日志构造器
try {
this.logMethod = logAdapterClass.getMethod("setLogFactory", logMethodSignature);
} catch (final Throwable t) {
// ...
}
}
return logAdapter;
}

日志之日志理念(一)

一、前言:

​本文日志针对所有语言,日志门面的选择目前只针对java!!!学日志体系之前,我们要了解以下6点:
​1.日志是什么?
​2.日志解决了什么问题?
​3.日志文件的作用?
​4.日志该如何记录?
​5.日志门面该如何选择?
​6.日志框架该如何选择?

二、日志是什么

​日志是用来记录程序运行时的状况

三、日志解决了什么问题

​随着程序的壮大,出现不兼容的概率就会越来越大,为了了解出现不兼容情况下的真实场景,将会出现问题的地方进行记录下来,方便去改善程序,提供更好的服务

四、日志文件的作用

​日志文件通常是系统运行中生成的系统信息,描述着每个业务的运行状况。对异常的状态存在更多的信息,以及导致异常的参数,来保证场景还原,从而快速从根本解决问题

五、日志该如何记录

记录好一个日志并不容易,也不会很难,需要了解日志的特征。

日志输出情况:如果定义级别为Info,那么只有日志等级>=Info级别的日志才会进行输出

日志级别 级别意义 优先级 使用广度
ALL 所有,所有日志级别,包括定制级别 1
Trace 跟踪,指明程序运行轨迹,比DEBUG级别的粒度更细 2
Debug 调试,指明细致的事件信息,对调试应用最有用 3
Info INFO,指明描述信息,从粗粒度上描述了应用运行过程 4
Warn 警告,指明可能潜在的危险状况 5
Error ERROR,指明错误事件,但应用可能还能继续运行 6
Fatal 致命,指明非常严重的可能会导致应用终止执行错误事件 7
Off 关闭,不打印日志 8

六、日志门面该如何选择

日志门面框架目前分为两类,一类是以apache开发的jcl(Jakarta Commons Logging),一类是以qos-ch开发的SLF4J(Simple Logging Facade for Java)。
以前日志体系各异,急需一个完整的门面框架进行管理,这时apache带领jcl杀出来,但是jcl的查找机制对于现在发展而言面临淘汰状况,slf4j依赖于spi机制直接杀进日志抽象框架队伍,出现这种情况的原因是jdk1.6的spi机制产生。slf4j也自然成为了日志门面框架的必然趋势!!!
花费几分钟了解这几位功臣的贡献,这值得我们牢记!(ps:诞生时间并不代表取代时间,因为还有小伙伴在用jcl)

日志门面框架 诞生时间 意义
jcl 2001-08-02 16:27 日志混乱时期,弥补了早期日志差异话带来的影响
slf4j 2005-04-14 17:43 由于JDK1.6的spi机制的出现,导致jcl性能消耗出现了对比,时代的后继者

七、日志框架该如何选择

日志框架主要是用来进行日志输出的,日志框架也对应着其相应的日志门面,这主要和时代有关。
如何运用好各种日志框架,需要我们对他们进行基本熟悉,如下:

JCL
动态查找加载

日志框架 加载顺序(级别越低越先加载) 描述
Log4J 1 自主控制日志输出地,控制每一条日志的输出格式,通过一个配置文件灵活配置,早期流行日志
Jdk14 2 JDK4及以上,jdk自带的日志实现
Jdk13Lumberjack 3 JDK3及以上,为了向下兼容
SimpleLog 4 基础实现,JCL自带实现,不需要额外引入

SLF4J
SPI加载

日志框架 描述
Logback Logback的目的是作为流行的log4j项目的继承者,填补log4j的空白,比log4j特性更好
JDK14 JDK4及以上,jdk自带的日志实现
Log4j 自主控制日志输出地,控制每一条日志的输出格式,通过一个配置文件灵活配置,早期流行日志
Simple 基础实现,slf4j项目,需要额外引入

共性日志框架
log4j2依赖于log4j的加载器进行加载,所以可同时兼容2个日志门面框架

日志框架 描述
Log4j2 性能优于log4j,特性更多,log4j的替代品

MyRE

一、前言:

​ MyRE是一款由个人做的小型规则引擎,它支持判断规则和表达式规则,拥有并行处理多任务的能力,提供任务数据间的互通。MyRE将原本繁杂的if判断条件进行了统一的规范。

二、详细介绍

MyRE网址

MyRE项目地址

Mybatis终极之配置解析流程揭秘(十)

一、前言:

​ 相信你用过xml配置文件配置过mybatis的mapper接口映射体系,或许你热衷于注解来进行配置,也许你交叉混用(小孩子才做选择,我全要!);为啥一个xml或者几个注解就能配置一个mybatis的mapper接口映射体系呢?mybatis的结构体系又如何构建呢?mybatis又是如何解析这些接口映射体系配置的呢?带着问题我们来揭秘mybatis的配置解析流程.

二、mybatis体系生成回顾

avatar

avatar

三、mybatis配置解析

开局提示:Mybatis先解析自身体系构建配置的xml,再解析接口和xml的映射体系,最后解析接口和注解的映射体系.

  • xml配置解析 - mybatis体系构建

    • XMLConfigBuilder - xml配置构建器

    avatar

    avatar

    在解析配置xml时会构建Configuration配置总站,里面会初始化mapper接口注册器,在上图解析mapper接口集方法中会触发mapper接口和xml映射解析、mapper接口和注解映射解析.

    avatar

    avatar

    通过mapperElement我们知道mybaits支持单一接口类注册和包路径多接口类注册,我们继续往下瞧

    • Configuration - 配置总站

      avatar

    • MapperRegistry - mapper接口注册器

      avatar

      avatar

  • 注解配置解析 - mapper接口映射构建

    • MapperAnnotationBuilder - mapper接口注解构建器

      avatar

      avatar

      avatar

      avatar

  • xml配置解析 - mapper接口映射构建

    avatar

    • XMLMapperBuilder - xml的mapper接口构建器

      avatar

四、总结

​ mybatis解析配置文件的时候会生成Configuration配置总站,首先会对Configuration配置总站进行初始化,通过MapperAnnotationBuilder进行注解、xml对mapper接口的映射配置,注意这一步先解析xml对mapper接口的映射配置.

​ 小提示:Configuration配置总站是mybatis所有数据的配置中心.

​ 祝各位小伙伴在mybatis源码中尽情遨游!!!

Mybatis进阶之嵌套子查询-循环依赖的揭秘(九)

一、前言:

​ 通过前文Mybatis进阶之简单结果集映射揭秘(八)我们对mybatis查询有了深刻的认识,源码中频繁出现的嵌套子查询究竟为何物?现在我们来揭开它那神秘的面纱,并深究其循环依赖的奥秘.

二、嵌套子查询时序图

avatar

三、嵌套子结果集映射详解

  • CachingExecutor - 缓存执行器

    avatar

  • BaseExecutor - 基础执行器

    特别注意:此时queryStack变量充当整个流程的控制者,EXECUTION_PLACEHOLDER充当一级缓存的空白填充.

    avatar

    avatar

    通过queryFromDatabase进行数据库查询,如下:

    avatar

  • RuseExecutor - 重用执行器

    avatar

  • PreparedStatementHandler - 预处理statement处理器

    avatar

  • DefaultResultSetHandler - 默认结果集处理器

    avatar

    通过handleResultSet进行结果集处理,如下:

    avatar

    通过handleRowValues处理行数据,如下:

    avatar

    通过handleRowValuesForNestedResultMap处理嵌套子查询的结果集行映射,如下:

    avatar

    通过getRowValue对行数据进行映射,如下:

    avatar

    通过applyPropertyMappings对行数据进行手动映射填充,如下:

    avatar

    通过getPropertyMappingValue获取属性映射值时判断其是否是嵌套子查询,如下:

    avatar

    通过getNestedQueryMappingValue获取嵌套查询映射值会判断其加载类型,如下:

    avatar

    avatar

  • ResultLoader - 结果集加载器

    avatar

    整个流程会再次进入BaseExecutor的query中,对queryStack进行累加,只到跳出整个查询.

四、mybatis循环依赖的解决方案

mybatis中BaseExecutor执行器对一级缓存进行管控,利用queryStack标识对最终结果进行处理.一级缓存对没有操作的查询缓存key进行空参填充,在嵌套子查询中会判断是否命中一级缓存,然后将其添加到延迟队列,直到整个查询结束再对其进行延迟队列的加载,填充所有数据.

五、总结

​ mybatis解决循环依赖主要是利用一级缓存和内置的queryStack标识,嵌套子查询的秘密就在于循环依赖的解决,设计美妙绝伦,各位小伙伴得深究其奥秘所在,领略其精华.

​ mybatis如何解析注解配置和xml配置的呢?让我们共同揭秘…

Mybatis进阶之简单结果集映射揭秘(八)

一、前言:

​ 通过前文Mybatis进阶之深入了解查询机制(七)我们了解到sql会话的查询最终会交给Executor执行器去进行query查询,Mybatis之门面会话(三)带我们观看了Executor执行器的生成策略,本文将揭秘Executor执行器的简单结果集映射.

二、简单结果集映射时序图

avatar

三、简单结果集映射详解

  • CachingExecutor - 缓存执行器

    avatar

  • BaseExecutor - 基础执行器

    avatar

    avatar

    通过queryFromDatabase进行数据库查询,如下:

    avatar

  • RuseExecutor - 重用执行器

    avatar

  • PreparedStatementHandler - 预处理statement处理器

    avatar

  • DefaultResultSetHandler - 默认结果集处理器

    avatar

    通过handleResultSet进行结果集处理,如下:

    avatar

    通过handleRowValues处理行数据,如下:

    avatar

    通过handleRowValuesForSimpleResultMap处理简单的结果集行映射,如下:

    avatar

    通过getRowValue对行数据进行映射,如下:

    avatar

    avatar

四、行数据映射解析

  • 自动映射

    未配置对象属性和结果集列名的对应关系,自动映射会生成对应的关联,进行对象的属性注入.

    avatar

    avatar

  • 手动映射

    通过注解或着xml配置的对象属性和结果集列映射关系,进行对象的属性注入.

    avatar

    avatar

五、总结

​ 简单结果集映射分为简单结果集自动映射、简单结果集手动映射,这就是mybatis查询一个结果集到java对象的秘密哟~

​ 源码中频繁出现的嵌套子查询是什么呢?让我们慢慢揭秘…

Mybatis进阶之深入了解查询机制(七)

一、前言:

​ 前文我们了解了Mybatis之Executor执行器(一)Mybatis之接口代理(二)Mybatis之门面会话(三)Mybatis之jdbc处理器(四)四大核心,还有jdbc之statement执行器进行具体数据查询组件,本文我们将深入了解其查询机制.

二、原生查询

avatar

三、mybatis查询

avatar

四、mybatis查询机制剖析

  • SqlSessionFactoryBuilder - sql会话工厂构建器

    avatar

    avatar

    这里主要是读取xml配置构建一个Configuration配置总站,生成一个默认的sql会话工厂.

    完成步骤:

    avatar

  • SqlSessionFactory - sql会话工厂

    avatar

    avatar

    从SqlSessionFactoryBuilder中会构建一个DefaultSqlSessionFactory,这边通过openSession方法构建一个DefaultSqlSession.

    完成步骤:

    avatar

  • 查询方式

    共性:

    avatar

    • sql会话查询

      avatar

      avatar

    • 接口代理查询

      avatar

      具体接口代理实现请观看Mybatis之接口代理(二),调用接口代理实现对象方法最终会执行MapperProxy中的invoke方法如下:.

      avatar

      avatar

      avatar

      avatar

      最终会使用sqlseesion执行数据获取的,后续再深入说明.

      完成步骤:

      avatar

五、总结

​ mybatis中有两种数据操作方式(statemnt key 、接口代理对象),从SqlSessionFactoryBuilder到Executor执行器查询,我们已深入了解其查询机制,Executor执行器查询机制背后还隐藏着什么?让我们慢慢揭秘…

Mybatis进阶之二级缓存的使用及解析(六)

一、前言:

​ 前文Mybatis进阶之揭开二级缓存的神秘面纱(五)深度剖解了二级缓存,相信大家对mybatis二级缓存已经不在陌生了,接下来我们将全面介绍二级缓存的使用.

二、mybatis二级缓存配置策略

  • 注解配置

    avatar

  • xml配置

    avatar

    特别注意:注解方式开启的二级缓存空间和xml方式开启的二级缓存空间,同名的缓存空间名只能存在一个!!!

三、mybatis二级缓存的引用策略

  • 注解引用

    avatar

  • xml引用

    avatar

    缓存引用主要是针对有相同特性或者相关联的事物共用同一个缓存空间.

四、@CacheNamespace注解属性详解

avatar

五、Cache责任链构建解析

  • MapperAnnotationBuilder - mapper注解构建器

    avatar

    avatar

  • MapperBuilderAssistant - mapper构建器辅助器

    avatar

  • CacheBuilder - 缓存构建器

    avatar

    avatar

六、总结

​ 切记同一缓存空间名只能存在一个缓存器(注解、xml二选一),关于相同特性或者相关联的事物可以共用同一个缓存空间,责任链也可以自定义组装,相信大家对二级缓存使用以及原理可以自信说出那句话 ‘二级缓存感觉就像回到家一样’.

请我喝杯咖啡吧~

支付宝
微信