数据结构
数组
Array、Array List...
特点
char[] cs = new char[5];
char[] cs2 = new char[]{'fish','bone'};
cs[0] = "fish";
...
- 内存地址连续,使用前需要指定数组长度。
- 有下标。依据下标来获取、设置值。查询效率高。
- 增删操作效率较低。(需要考虑下标越界问题,动态扩缩容)
总结
增删操作效率较低主要指:
- 新增。
- 初始长度假如是5,不停的新增,当增加到第6个元素时,则需要从触发<font color="red">扩容</font>操作(一般会提前触发不会等数组满了才触发),扩容操作则需要重新建立一个数组,将之前数据中元素<font color="red">移动</font>过来。
- 初始长度范围内的新增效率很高。
- 修改。
- 如果要修改某个元素,比如将数组中'bone'修改为‘bones’则需要遍历查抄,然后修改。
- 直接通过下标修改效率比较高。
- 删除。
- 如果删除虽然元素不在,但是内存依然存在,且空间是连续的。比较占用内存。
综上所述,数组适用于,查询操作较多。修改、新增、删除操作较少的定长数据情况。
链表
单向链表|双向链表(Linked List)
特点
- 灵活的空间要求,存储空间不要求连续。
- 不支持下标访问,查询效率低,支持顺序遍历检索(next)。
- 针对增删操作效率会比较高,只需要操作节点,无需移动元素。
总结
链表适合于新增,删除操作较多的情况,由于无法通过下标快速获取到某个位置元素,所以对查询操作不够友好。
数组、链表问题
-
在<font color="red">连续新增</font>100W条数据的情况下,ArrayList和LinkedList哪一个效率更快?为什么?
- <font color="blue">错误回答:</font>LinkedList,因为他是链表结构,链表接口新增效率高。ArrayList是数组接口,新增慢。
- <font color="blue">分析:</font>这个问题主要考虑理解深度,不能简单因为链表新增快就回答LinkedList。所有的快慢都是需要分情况的。
- <font color="blue"> 正确回答:</font> ArrayList效率高。因为LinkedList每次新增都要new Node()节点。此外还需要操作prev、next两个节点。而ArrayList如果初始就把长度固定则不需要创建那么多对象出来。所以数组的效率就要高于链表。
public static void main(String[] args) { long startTime=System.currentTimeMillis(); //获取开始时间 arrayTest(); long endTime=System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间1: "+(endTime-startTime)+"ms"); long startTime1 = System.currentTimeMillis(); //获取开始时间 linkTest(); long endTime1 = System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间2: "+(endTime1-startTime1)+"ms"); } private static void linkTest() { LinkedList linkedList = new LinkedList(); for(int i=0;i<1000000;i++){ linkedList.add(i); } } private static void arrayTest() { ArrayList array = new ArrayList(1000000); for(int i=0;i<1000000;i++){ array.add(i); } }
// 执行结果 程序运行时间1: 18ms 程序运行时间2: 28ms Process finished with exit code 0
树(Tree)
二叉树
只有左右两个叉的树结构。
二叉树中包含:
- 平衡二叉树
- AVL树,通过自旋转实现平衡。
- 不平衡二叉树
- 二叉查找树一般都是非平衡的。
- 完全平衡二叉树
- 不完全平衡二叉树(红黑树)。
特点
- 某节点的左侧树节点值仅包含小于该节点的值。
- 某节点的右侧树节点值仅包含大于该节点的值。
- 左右树节点每个也必须是二叉树。
- 顺序排列。
红黑树
自平衡二叉树。(黑平衡二叉树)
特点
- 每个节点要么是红,要么是黑。
- 根节点必须是黑色。
- 每个叶子节点【NIL】必须是黑色。
- 每个红色节点的子节点必须是黑色。
- 任意节点向下到任意叶子节点【NIL】的路径包含相同数量的黑色节点(黑平衡二叉树)。
BTree
平衡多路查找树,节点不只是2个。
每个节点都会存储数据。
B+Tree
1kb = 1024b , 1字节 = 8 位。 UUID 32位
平衡多路查找树,节点不只是2个。
是对BTree的优化,数据仅存储在叶子结点,且叶子节点以链表的形式存在。
集合
TreeMap
特点
- 本质是红黑树的实现
- 有序的
HashMap
数组+链表。数组+红黑树
链表数据长度>8的时候转化为红黑树。
HashSet
HashSet底层是HashMap,本质是一个没有重复元素的集合,通过hashmap实现。
HashSet hashSet = new HashSet();
hashSet.add(1);
//源码
public HashSet(){
map = new HashMap<>();
}
public boolean add(E e){
return map.put(e,PRESENT)==null;
}
TreeSet
TreeSet底层是TreeMap,本质是将数据保存在TreeMap中,key为添加的数据,value是一个固定的值。
Spring
Spring 特点
Spring 主要为了使开发人员能够更专注也业务本身,尽可能的减少其他非业务的东西而出现。
为了实现他这个目的,Spring 一方面提供了三大核心功能(IOC,DI,AOP)帮助开发人员管理Bean的生命周期。
另一方面 将自己变成一个万能胶,利用自身提供的一些扩展点,能够对市面上的大部分框架和中间建进行集成,方便使用。
同时也不断的优化自身,从最开始的XML到现在的注解一切都是为了方便开发人员,提高开发效率。
IOC(控制反转)
Spring的IOC主要是为了实现对Bean的统一管理,将Bean的初始化、创建,销毁都交由Spring的IOC容器来实现。
BeanFactory(IOC容器的定义类)
1. 对IOC的基本行为做了定义。(getBean,constainsBean,isSingleton,isPropotype,isTypeMatch,getType)
2. 主要实现类ListableBeanFactory、HierarchialBeanFactory(有继承关系)、AutowireCapableBeanFactory定义自动装配规则。
3. 最终实现类是DefaultListableBeanFactory,该类包含beanDefinitionMap等集合。
ApplicationContext(高级的IOC容器)
1. 除了提供IOC容器的基本操作外(AbstractApplicationContext)还提供了一些附加功能。
2. 主要实现类ClassPathXMLApplicaitonContext、AnnotationConfigApplicaitonContext
BeanDefinition
1. Bean对象的描述类,描述了Bean对象的基本信息以及相互关系(类名、作用域、属性、依赖关系)。
2. 主要实现类AbstuctBeanDefinition、RootBeanDefinition、GenericBeanDenifition。
BeanDefinitionReader
1. Bean对象的解析类。
2. AbstractBeanDefinitionReader、XmlBeanDefinitionReader、PropertiesBeanDefinitionReader
IOC的过程实际是为了完成BeanDefinition的注册,放在beanDefinitionMap中。
首先这个过程中初始化的配置定义是多样的,有XML、注解等,所以需要使用不同的策略来去读取。这里就包含两个主要的核心类ClassPathXMLApplicaitonContext、AnnotationConfigApplicaitonContext,以及对应的解析器XmlBendifinitionReader、AnnotatedBeanDefinitionReader,为了方便管理又使用了BeanDefinition来统一配置标准。
他的核心流程大概分为三个阶段:定位—》加载—》注册。
Web IOC为例:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--Spring前端控制器,拦截所有请求-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
首先从web.xml配置的DispatcherServlet为起点(tomcat启动时也会调用ContextLoaderListener类contextInitialized方法),首先调用HttpServlet类的init()方法,init()方法或者contextInitialized()中会调用initWebApplicationContext方法,进行初始化,然后会调用到AbstractApplicationContext的refresh()方法,这个也是Spring的核心类。refresh()方法中会调用obtainFreshBeanFactory()来完成IOC容器的注册和依赖注入。在obtainFreshBeanFactory()方法中会调用子类的refreshBeanFactory()方法,进行creatBeanFactory()、loadBeanDefinitions()、doLoadBeanDefinition()、registerBeanDefinition()、doRegisterBeanDefinitions()最终调用的是DefaultListableBeanFactory类的registerBeanDefinition()方法来完成Bean的注册。
DI
BeanWrapper( Bean的包装,包含Bean的属性、方法,数据 )
核心流程大致分为两个阶段:实例化-》依赖注入
DI操作由BeanFactory的getBean()方法开始,实际调用的是AbstructBeanFactoty()的getBean()方法,在这里会直接进行doGetBean()的操作。在doGetBean()方法中需要调用一个getSingleton()方法,该方法参数需要一个函数接口,最终调用的是函数接口实现的creatBean方法AbstractAutowireCapableBeanFactory类(多例的会直接调用creatBean方法),然后会调用doCreateBean()方法,在这会实例化BeanWrapper对象,这里边有几个核心方法createBeanInstance()创建bean实例,addSingletonFactory向容器中缓存单例模式的Bean对象,以防循环依赖,populateBean并将Bean实例对象封装,并且执行DI操作,填充Bean属性。initializeBean执行一些BeanPostProcessor,Before,After处理、AOP,以及InitializingBean的afterPropertiesSet方法。
循环依赖
思考:什么是值传递?什么是引用传递?
Spring中解决循环依赖其实是利用了引用传递的特性,允许对象初始化未填充属性阶段先让其他依赖对象完成引用,后续再赋值。
问题:
哪些情况可以解决?哪些不可以解决?
- 构造函数循环依赖。(NO)
- 多例Prototype Field Setter 类型循环依赖。(NO)
- 单例Singleton Field Setter 类型循环依赖。(Yes)
Spring 如何解决循环依赖
Spring在解决循环依赖问题时,主要通过三级缓存,利用引用传递的特性。在依赖注入之前,将A未完成初始化的bean放入第三级缓存中,key<BeanName,ObjectFactory<?>>(ObjectFactory为函数式接口实现,调用getEarlyBeanReference方法),执行DI操作时需要完成B对象实例化,B对象执行DI操作依赖A时,可以直接从三级缓存中获取到未完成的A对象完成B对象的实例化过程,由于引用传递,后续A对象在实例化完成后,B对象中的A对象也完成实例操作。
假如A依赖B,B依赖A,此时先初始化A:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
//真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//如果指定的是别名,将别名转换为规范的Bean名称
final String beanName = transformedBeanName(name);
Object bean;
// 先从缓存中取是否已经有被创建过的单态类型的Bean
// 对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
// 此时A 明显获取不到A 对象
// 当此时为实例化B,为B实例注入A对象调用doGetBean(A)方法情况时,则可以拿到A对象,然后返回,具体看最下方代码
Object sharedInstance = getSingleton(beanName);
//IOC容器创建单例模式Bean实例对象
if (sharedInstance != null && args == null) {
// 省略...
}
else {
// 省略 ...
try {
if (mbd.isSingleton()) {
//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//显式地从容器单例模式Bean缓存中清除实例对象
destroySingleton(beanName);
throw ex;
}
});
//获取给定Bean的实例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
}
...
return (T) bean;
}
}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// ...
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ...
beforeSingletonCreation(beanName);
// ...
try {
// 调用函数式接口getObject()实现方法,等价于 createBean(beanName, mbd, args);
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
}
finally {
// 执行后续方法
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
//真正创建Bean的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
//封装被创建的Bean对象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建实例(此时未赋值属性)
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获得过早曝光对象bean。
final Object bean = instanceWrapper.getWrappedInstance();
// ...
//向容器中缓存单例模式的Bean对象,以防循环引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//向第三级缓存添加 对象实例
//同时生成 函数接口实现,一并添加到singletonObjects中,后续会调用。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
// Assert.notNull(singletonFactory, "Singleton factory must not be null");
// synchronized (this.singletonObjects) {
// if (!this.singletonObjects.containsKey(beanName)) {
// this.singletonFactories.put(beanName, singletonFactory);
// this.earlySingletonObjects.remove(beanName);
// this.registeredSingletons.add(beanName);
// }
// }
//}
}
// Initialize the bean instance.
//Bean对象的初始化,依赖注入在此触发
//这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
Object exposedObject = bean;
try {
//将Bean实例对象封装,并且Bean定义中配置的属性值赋值给实例对象
// 此时发现A依赖于B,回去执行B的getBean(),然后同A初始化.
// 在B实例化到此处时,发现B引用了A实例,那么就会尝试获取A实例对象(调用getBean(A)方法)。
populateBean(beanName, mbd, instanceWrapper);
//初始化Bean对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
//获取指定名称的已注册的单例模式Bean对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//根据名称获取的已注册的Bean和正在实例化的Bean是同一个
if (exposedObject == bean) {
//当前实例化的Bean初始化完成
exposedObject = earlySingletonReference;
}
//当前Bean依赖其他Bean,并且当发生循环引用时不允许新创建实例对象
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
}
}
}
// Register bean as disposable.
//注册完成依赖注入的Bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
}
//DefaultSingletonBeanRegistry
//Spring利用三级缓存来解决循环依赖,singletonObjects(1),earlySingletonObjects(2),singletonFactories(3),
// singletonsCurrentlyInCreation 这个缓存也非常重要,在bean开始创建时存入,创建完成后移除,只存储正在创建中的bean。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 用于存储完全实例化好的bean 对象。
// 首先尝试从实例化好缓存中 获取对象 此时(循环依赖情况)肯定获取不到。
Object singletonObject = this.singletonObjects.get(beanName);
// 追加判断 该对象正在创建中(是的)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 未创建完成,提前曝光的bean对象,用于解决循环依赖
// 尝试从提前曝光的Bean中获取对象,对于 初始化A(A依赖B,B依赖A) 显然获取不到A对象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 尝试再从三级缓存单例工厂中获取,此时A(A依赖B,B依赖A)很明显也获取不到。 返回null.
// 此时为初始化A对象(在A对象初始化中,发现依赖B,初始化B,发现B依赖A,然后尝试获取A对象情况
// 能够从singletonFactory中拿到A对象。
// addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 这里 getObject()方法,实际调用getEarlyBeanReference(beanName, mbd, bean)
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
Aop
Aop核心
- 切面 Aspect,面向规则,具有相同规则的<font color="red">方法</font> (methonCache)集合。
- 通知 Advice,调用(回调)操作。
- 切点 PointCurd,需要代理的具体方法。
- 目标对象 Target,需要代理的对象()。
- 代理 Proxy,JDK ,CGLIB。
- 前置通知、后置通知、异常通知。
概述
简单来说,AOP就是通过一定的规则,来当做切面。在Spring 实例化DI阶段,基于规则判断是否满足,满足则使用代理来代替目标对象。在使用代理对象,调用方法时,会从methodCache中获取后续执行链(chain),如果存在,则表示改方法有AOP代理需求,执行后续链操作。如果没有直接利用反射调用目标类方法。
在chain不为null时,Spring 使用责任链模式,来执行操作(有一定的执行顺序)。
// 缓存切点 拦截器
Map<MethodCacheKey, List<Object>> methodCache;
注意
AOP是基于一定的规则来匹配方法(通过拦截器实现),是方法层面的代理和织入。但是由于代理针对的是类,所以在DI操作进行规则验证时,只需要验证类的层面满足就可以。在生成代理对象后调用方法时,才会对方法层面进行验证(chain),通过反射来实现方法的调用。
AOP采用策略模式,使用JDK(有实现接口时),和CGLIB两种方式来实现代理。
-
AOP操作的开始点是在DI执行之后。
populateBean(beanName, mbd, instanceWrapper); //初始化Bean对象 // 包含AOP 初始化,AOP属于后置处理。 实现了 BeanPostProcessor接口。 exposedObject = initializeBean(beanName, exposedObject, mbd); //AOP 代理类 public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { }
MVC
Spring扩展点
InitializingBean 类
// 在完成bean 的注入(populateBean())之后 调用
// 针对单个bean使用。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
调用流程见BeanPostProcessor 类。
BeanPostProcessor 类
public interface BeanPostProcessor {
//在Bean 完成定义BeanDefinition、完成依赖注入getBean()->populateBean(),调用无参构造函数完成实例化前执行
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//在Bean 完成定义BeanDefinition、完成依赖注入getBean(),调用无参构造函数完成实例化后执行。
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 所有的类都会触发
}
调用详解
//AbstractAutowireCapableBeanFactory
// getBean() 流程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... 省略 无关代码
// Instantiate the bean.
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 这里处理 特殊的 MergedBeanDefinitionPostProcessor
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 这里完成 bean 初始化DI 工作
populateBean(beanName, mbd, instanceWrapper);
// 这里 initializeBean 方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// ... 省略无关代码
return exposedObject;
}
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//下边 看看这个方法干了点啥
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
// 判断是否 是InitializingBean 的接口,如果实现了InitializingBean,则先执行 afterPropertiesSet 方法。
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
//不知道这个initMethodName是什么东西。反正if判断不会过
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
BeanFactoryPostProcessor 类
@FunctionalInterface
public interface BeanFactoryPostProcessor {
// 所有类都会触发
/**
* Modify(修饰 改变) the application context's internal(内部的) bean factory after its standard(使标准化) initialization.
* 在应用程序上下文标准工厂类 定义后,修饰(改变)工厂内部的bean对象 初始化过程。
* All bean definitions will have been loaded, but no beans will have been instantiated yet.
(调用时机)在所有的bean定义都被加载,但没有实例化之前。
* This allows for overriding or adding
* properties even to eager-initializing beans.
这允许重写或添加属性甚至可以初始化bean。
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
调用时机
//
registerBeanPostProcessors
//PostProcessorRegistrationDelegate
final class PostProcessorRegistrationDelegate {
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
//beanFactory基本都实现了 BeanDefinitionRegistry 接口
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 先执行 BeanDefinitionRegistryPostProcessor。其实是BeanFactoryPostProcessor的子接口之一
// public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
// 其实是BeanFactoryPostProcessor的子接口之一
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 其实是BeanFactoryPostProcessor的子接口之一
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// 这里才是真正获取实现类的地方。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
// 判断是否有执行过
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
// 是否实现 PriorityOrdered 接口,实现了优先执行
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
//是否实现 Ordered 接口,实现了优先执行
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 开始真正执行流程
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
}
// PostProcessorRegistrationDelegate
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}
FactoryBean
MyBatis
执行流程
- 解析配置,通过SqlSessionFactoryBuilder创建SqlSessionFactory。
- 通过SqlSessionFactory获取SqlSession,执行操作前需要open。
- 通过SqlSession获取Mapper代理对象。
- 执行操作调用excute执行器,执行xml的sql。
- 接收后需要关闭SqlSession。
public void testSelectList() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
如何实现Mapper 调用xml方法?
SqlSessionFactory
SqlSessionFactory在创建过程中,会做一系列的初始化操作,包括插件(分页插件)的初始化,实体对象和jdbc类型处理器的初始化,以及最重要的mapper初始化。
对于mapper来说,实际是由MapperRegistry类(Configuration)的knownMappers属性来对每一个mapper接口(xml中的namespace)绑定一个MapperProxyFactory对象,作为后续sql操作的处理类。
MapperProxyFactory其实并没有目标对象的概念,他仅仅只是提供一个入口,这里主要是为了将mapper接口中的方法匹配到xml的sql(statement),进而执行。(所以无论是否实现mapper接口,均不会调用实现类!)
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >>
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析XML,最终返回一个 DefaultSqlSessionFactory >>
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
public class XMLConfigBuilder extends BaseBuilder {
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// XPathParser,dom 和 SAX 都有用到 >>
// 注意 这里直接获取 mybatis配置文件 configuration 节点下内容
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 对于全局配置文件各种标签的解析
propertiesElement(root.evalNode("properties"));
// 解析 settings 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 插件
pluginElement(root.evalNode("plugins"));
// 用于创建对象
objectFactoryElement(root.evalNode("objectFactory"));
// 用于对对象进行加工
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工具箱
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// settings 子标签赋值,默认值就是在这里提供的 >>
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 创建了数据源 >>
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析引用的Mapper映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 不同的定义方式的扫描,最终都是调用 addMapper()方法(添加到 MapperRegistry)。这个方法和 getMapper() 对应
// package 包
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// resource 相对路径
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析 Mapper.xml,总体上做了两件事情 >>
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// url 绝对路径
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// class 单个接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
}
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
// 总体上做了两件事情,对于语句的注册和接口的注册
if (!configuration.isResourceLoaded(resource)) {
// 1、具体增删改查标签的解析。
// 一个标签一个MappedStatement。 >>
// 获取mapperbiao标签 准备注册 接口 和语句
configurationElement(parser.evalNode("/mapper"));
// 加载 xml 的 <mapepr>节点资源
configuration.addLoadedResource(resource);
// 2、把namespace(接口)和工厂类绑定起来,放到一个map。
// 一个namespace 一个 MapperProxyFactory >>
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void bindMapperForNamespace() {
// 获取nameSpace
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 获取接口类型
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
// 添加到 MapperRegistry,本质是一个 map,里面也有 Configuration >>
configuration.addMapper(boundType);
}
}
}
}
}
// mybatis 全局配置类
public class Configuration {
//Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
// 存储NameSpace 和 mapper代理工厂对象的对照关系。
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
//MapperRegistry 类中方法
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// Map<Class<?>, MapperProxyFactory<?>> 存放的是接口类型,和对应的工厂类的关系
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 注册了接口之后,根据接口,开始解析所有方法上的注解,例如 @Select >>
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
OpenSession
openSession 实际返回defaultSqlSession对象。在这个过程中主要功能有:创建事务、初始化执行器、二级缓存、注册Excute插件(装饰器 + 责任链+jdk动态代理模式,一层一层的装饰excute对象,并返回结果)。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取环境相关配置
final Environment environment = configuration.getEnvironment();
// 获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务 默认jdbc
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据事务工厂和默认的执行器类型,创建执行器 >>
// 执行器中 初始化 插件、二级缓存等
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
// 默认 SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
// 二级缓存开关,settings 中的 cacheEnabled 默认是 true
// mybatis 一级缓存是基于一次会话(openSession),
// 二级缓存 是基于一个nameSpace,所以每一个命名空间都需要一个CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 植入插件的逻辑,至此,四大对象已经全部拦截完毕 装饰器 + 责任链模式+jdk动态代理
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
//MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
// Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
// ParameterHandler (getParameterObject, setParameters)
// ResultSetHandler (handleResultSets, handleOutputParameters)
// StatementHandler (prepare, parameterize, batch, update, query)
}
getMapper
获取代理对象,执行后续操作。最终返回代理对象为 MapperProxy。
public class DefaultSqlSession implements SqlSession {
public <T> T getMapper(Class<T> type) {
// 这里configuration对象是在 创建DefaultSqlSession时传入的,全局对象引用。
// mapper的代理对象是存放在 MapperRegistry 类 中 knownMappers属性中
// Configuration中包含MapperRegistry的引用(MapperRegistry也包含Configuration引用)
// 次数实际调用的 是 mapperRefistry 中的方法。(符合DDD领域驱动设计规范)
return configuration.getMapper(type, this);
}
}
//MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// mapper 接口 动态代理实现类。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 1:类加载器:2:被代理类实现的接口、3:实现了 InvocationHandler 的触发管理类
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 初始化 目标代理类
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
Sql执行 mapper.Method()
由于此时mapper已经是MapperProxy代理对象,所以在执行方法时会调用MapperProxy的invoke方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// toString hashCode equals getClass等方法,无需走到执行SQL的流程
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke
// 普通方法会走到 PlainMethodInvoker(本类的 内部类) 的 invoke
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// Java8 中 Map 的方法,根据 key 获取值,如果值是 null,则把后面Object 的值赋给 key
// 如果获取不到,就创建
// 获取的是 MapperMethodInvoker(接口) 对象,只有一个invoke方法
return methodCache.computeIfAbsent(method, m -> {
// 判断 执行 method是否是默认方法。
// 一般都不是(没见过需要写default方法的mapper接口情况)。
if (m.isDefault()) {
// 接口的默认方法(Java8),只要实现接口都会继承接口的默认方法,例如 List.sort()
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 创建了一个 MapperMethod
// configuration对象 被传来传去
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// SQL执行的真正起点
return mapperMethod.execute(sqlSession, args);
}
}
}
核心点
缓存
mybatis中分为一级缓存和二级缓存,一级缓存默认开启(基于一次会话)。二级缓存需要手动开启,在nameSpace中<setting>标签设置,cacheEnabled=true,二级缓存基于一个命名空间。一般新增,修改,删除操作会更新缓存。为了防止递归查询重复处理缓存,还使用了queryStack来统计执行次数。二级缓存模式使用LRU最少使用策略来做内存淘汰。
Spring+Mybatis
Spring集成Mybatis后,如何完成mapper代理对象的创建和注入?
Spring集成Mybatis后,sqlSessionFactory怎么创建,怎么获得sqlSession,怎么实现打开关闭?
Spring集成Mybatis是通过mybatis-spring.jar包来实现的,基于一些Spring提供的扩展点(FactoryBean,InitializingBean),mybatis完成了与Spring的集成。
Spring集成Mybatis依赖三个核心配置。
<!-- Spring bean初始化时会去加载 SqlSessionFactoryBean类实现sqlSessionFactory创建 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gupaoedu.crud.dao"/>
</bean>
<!--配置一个可以执行批量的sqlSession,全局唯一,单例 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"></constructor-arg>
<constructor-arg value="BATCH"></constructor-arg>
</bean>
- SqlSessionFactoryBean主要为了创建SqlSessionFactory。在创建过程中,最终调用mybatis的创建过程,做一系列的初始化操作,包括插件(分页插件)的初始化,实体对象和jdbc类型处理器的初始化,以及最重要的mapper初始化(将mapper与MapperProxy代理关联起来,getMapper时拿到代理对象)。
- MapperScannerConfigurer主要是为了扫描xml文件,通过扫描xml文件获得dao接口,并将dao接口注册到IOC容器中,同时替换IOC容器BeanDefinition目标类,统一指向MapperFactoryBean(实现了FactoryBean接口)。也就是说在DI操作getBean,Dao层实例时,实际调用MapperFactoryBean的getObject方法。这个方法最终返回的是MapperProxy代理对象。
- SqlSessionTemplate主要为了解决DefaultSqlSession非线程安全的问题。我们在使用DefaultSqlSession时都需要open或者close掉sqlSession。为了在Spring中方便的解决这个问题,提供了SqlSession的另外一个实现类SqlSessionTemplate(线程安全的)。线程安全的原因就是因为使用了代理模式,在创建SqlSessionTemplate时实际返回的是SqlSessionInterceptor代理类,这里的invoke方法中会open/close操作sqlSession。
SqlSessionFactory创建
我们在使用Spring集成Mybatis时,会在Spring的配置文件中使用如下配置:
// 实现了 InitializingBean Spring在完成 Bean 配置后调用 afterPropertiesSet
// 实现了 FactoryBean 在创建bean实例
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
//默认走这里 先初始化Configuration
targetConfiguration = new Configuration();
}
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
if (hasLength(this.typeAliasesPackage)) {
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
}
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
if (hasLength(this.typeHandlersPackage)) {
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
}
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
if (!isEmpty(this.scriptingLanguageDrivers)) {
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
targetConfiguration.getLanguageRegistry().register(languageDriver);
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
});
}
Optional.ofNullable(this.defaultScriptingLanguageDriver)
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
try {
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
//配置文件中 通过mapperLocation配置 xml文件位置
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
// 此处进行 扫描加载
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
// 调用mybatis的方法
xmlMapperBuilder.parse();
} catch (Exception e) {
}
}
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
//XMLMapperBuilder类,上边xmlMapperBuilder.parse()。在configutation中 初始化MapperProxyFactory
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
}
Mapper代理注入
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
// ClassPathBeanDefinitionScanner
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 只做两件事情,第一扫描全部 mapper.xml
// 替换mapper目标对象
// 注意 首先调用的是子类的doScan方法。
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
//ClassPathMapperScanner
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 再调用父类 ClassPathBeanDefinitionScanner doScan方法 扫描接口注册IOC
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
// 修改beanDefinitions 的定义
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 将实体类 使用mapperFactoryBeanClass类替换。
// Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
// 调用 sqlSessionTemplete getMapper方法
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
}
//SqlSessionDaoSupport
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
// SqlSessionTemplate
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// open Session
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
// close Session
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
}
Spring Boot
简介
- Spring的升级版。功能基于Spring。比如:注解,事件,定时器等
- 约定优于配置的体现产物。基于约定的规则简化了一些常用配置。
- 引入Web-Stater默认内置tomcat启动,创建MVC,提供配置文件。
- 自动注入规则。
- Starter启动
- 去XML配置(Spring 提供)
- @Component/@Controller/@Service。(省略<bean>)
- @Configuration/@Component-Scan/@Import。(去XML核心)
- @Enable驱动。(重点)
- @Conditional条件注解。(重点 Spring 4.X)
- EnableAutoConfiguration自动装配。
特性:Stater、自动装配、SpringBoot-Cli、Actuator
Enable驱动
自动完成相关组件的Bean配置。
核心是通过@Import或者其他方式向IOC容器注入Bean。
是Spring去XML配置,约定优于配置的集中体现,通过配置Enable,使得开发者不用关注大量的配置内容。
如@EnableScheduling
正常加载:
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
private static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME = "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect";
public AnnotationDrivenBeanDefinitionParser() {
}
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// .... 省略无关代码
if (registry.containsBeanDefinition(
"org.springframework.context.annotation.internalScheduledAnnotationProcessor"
)) {
parserContext.getReaderContext().error(
"Only one ScheduledAnnotationBeanPostProcessor may exist within the context.",
source);
} else {
builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"
);
builder.getRawBeanDefinition().setSource(source);
scheduler = element.getAttribute("scheduler");
if (StringUtils.hasText(scheduler)) {
builder.addPropertyReference("scheduler", scheduler);
}
registerPostProcessor(
parserContext, builder,
"org.springframework.context.annotation.internalScheduledAnnotationProcessor"
);
}
parserContext.popAndRegisterContainingComponent();
return null;
}
}
@Enable驱动:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class) // Improt导入类
@Documented
public @interface EnableScheduling {
}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
自动装配
在Spring Boot 项目中存在一系列的Stater组件,通过在pom.xml中引入就可以直接通过@Autowired注入使用jar包中的类。比如RedisTemplete。这种形式叫做自动装配。
实现自动装配需要考虑几个问题。
-
如果需要直接注入,那么前提是IOC容器中要有该实例对象。那么IOC容器是怎么做到实例化的?
这里可以做一个猜想,想要将类交给IOC容器那么基于正常的方式来说需要这样做
@Configuration public class AutoConfiguration{ @Bean public RedisTemplete redistemplete(){ return new RedisTemplete(); } .... }
如何告诉Spring需要加载哪些配置类?
动态装载
Spring 提供了两种方式来实现动态配置的装载。
- Selector: ImportSelector、 DeferredImportSelector
- Registory:ImportBeanDefinitionRegistrar
我们通过实现ImportSelector接口selectImports()方法来返回要加载的配置类,这样可以实现配置类或者Bean对象的动态装配。
通过动态装载我们可以将配置类交给Spring去扫描,完成依赖注入。
那么Spring如何知道我的配置类到底在哪里?—》约定优于配置
Spring定义规则:将需要扫描的配置类通过配置文件的形式(spring.factories)放在META-INF目录下。
Spring-Boot通过@EnableAutoConfiguration注解来进行扫描加载。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //导入扫描类
public @interface EnableAutoConfiguration {
...
}
------------------------------------------
public class AutoConfigurationImportSelector implements DeferredImportSelector, ... {
....
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
...
//扫描配置
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
----------------------------------------
//最终调用 SpringFactoriesLoader类的loadSpringFactories方法
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
SpringFactoriesLoader其实类似SPI(扩展点)
SPI机制
Service provider interface
需要满足以下条件
- 创建目录META-INF/services
- 在目录下创建扩展点的全路径名
- 文件中要写扩展点,类的实现
- 文件编码UTF-8
- ServiceLoader去加载
条件控制
@Condition一系列注解。
官方的Stater(Spring-boot-stater-XXX)不需要额外的spring.factories文件,在spring核心包中统一进行配置。所以官方的配置类主要是通过@Condition注解来进行条件加载的。
手写Stater组件
pom.xml引入Sping-boot jar包(需要用到注解@Configuration/@Condition等注解)同时设置<option>属性。
-
引入外部化配置。
-
创建配置对象使用@ConfigurationProperties注解,同时定义配置前缀。
@ConfigurationProperties(prefix = "fish.bone") public class propertiesConfig(){ private Sting url = "test URL"; ... }
-
外部化配置提示。
- 导入jar包(spring-boot-configuration-processor)
-
META-INF目录下创建additional-spring-configuration-metadata.json文件
{ "properties": [ { "name": "server.compression.enabled", //配置名称 spring.application.port "description": "Whether response compression is enabled.", // 配置描述 启动端口 "defaultValue": false //设置默认值 }, { "name": "server.compression.excluded-user-agents", "description": "Comma-separated list of user agents for which responses should not be compressed." } ] }
-
-
编写配置类
- 配置类加入@EnableConfigurationProperties注解加载外部化配置对象。
-
创建spring.factories文件
Condition 除了注解外 还可以在 MATA-INF目录下创建 spring-autoconfigure-metadata.properties 来定义注解条件。
MySQL
执行流程
sql->缓存(8.0停用)->解析器->预处理器->优化器->执行计划->执行器
索引机制
Innodb 使用B+Tree作为数据结构。
聚集索引
聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同,有且仅有一个,因为一个表的物理顺序只有一种。自增主键索引。
为什么推荐使用自曾主键作为索引,就是因为他的顺序与表的物理顺序一致。当我这个索引为聚集索引时,对表的新增修改删除影响会降到最小,拿新增来举例他肯定是在末尾新增。而非自增主键来说我就是一个随机的接口,当插入到中间位置时,有可能会造成整个索引结构的重新排列。
覆盖索引
覆盖索引,表示查询的数据刚好是索引字段中包含。比如A+B联合索引,在查询select a,b、a、b 时均能遇到覆盖索引。
索引下推
对于存储引擎来说,存储引擎只能通过索引来进行数据过滤。当联合索引为AB, where a = '' and b='%as' 此时b用不到索引,正常来说存储引擎会只判断a条件,然后返回server进行过滤。但是实际情况是会直接过滤ab两个条件,这种情况就是索引下推。
回表
非主键索引查询,且没有用到覆盖索引的情况都会产生回表。
数据库事务
事务的四大特性
原子性 Atomicity (undolog)
隔离性 Isolation (加锁、快照 LBCC+MVCC)
持久性 Durability (Redolog+双写缓冲)
一致性 Consistency
undolog(回滚日志,事务回滚),Redolog(执行日志+双写缓冲,崩溃恢复)。
增删改操作会自动开始事务。手动开始事务:begin; start trancation;事务开始后断开连接会自动回滚事务。
一个事务持有的锁会在事务结束后释放
事务并发的三大问题(读一致性问题)
脏读()
事务开启,但未提交的数据,叫做脏数据。
一个事务读取到了其他事务未提交的数据,造成前后两次读不一致情况。
不可重复读(undate/delete)
一个事务读取到其他事务已提交的数据,造成在一个事务里边两次查询到不一致的情况。
幻读(insert)
一个事务读取到其他事务已提交插入的数据,造成的前后两次读不一致。(只有插入已提交的情况才叫做幻读)
事务隔离级别
Read Uncommitted(RU未提交读) -- 没有解决任何问题
一个事务能够读取到其他事务没有提交的数据,会出现脏读。
Read Committed(RC已提交读) -- 解决脏读问题
一个事务能够读取到另外一个事务已提交的数据。没有解决可重复读问题。
Repeatable Read(RR可重复读) -- 解决不可重复读问题
一个事务中多次读取同样的数据结果是一样的,未解决幻读问题。
Serializable(串行化)
能够解决所有问题
如何解决事务并发的读一致性问题
- 在读取数据前加锁,防止其他事务对其修改。(性能太差,读操作都要加锁,太粗暴 LBCC Lock Based Concurrency Control )
- 生成一个时间点的快照,并为这个快照来提供一定级别的一致性读取。(MVCC Multi Version Concurrency Control)多版本并发控制。
MVCC核心思想
- 一个事务能够看到的数据版本。
- 第一次查询前已经提交的事务的修改。
- 本事务的修改。
- 一个事务不能看到的数据版本。
- 在本事务第一次查询之后创建的事务(事务ID比我的事务ID大)
- 未提交的事务的修改。
MVCC实现原理
DB_TRX_ID: 插入或更新的最后一个事务的ID,事务编号是自动递增的。
DB_ROLL_PTR:回滚指针。(删除版本)
数据库锁
表锁与行锁
锁定粒度、加锁效率、冲突概率、并发性能。
加锁方式
lock tables xxx read , lock tables xxx write ,unlock tables
共享锁(行锁)
Shared Lock
共享锁又称为读锁,简称S锁。顾名思义S锁就是多个事务对同一条数据可以共享一把锁,都能访问,但是不能修改。
加锁方式
select * from table where id = 1 lock in share mode ; commit / rollback;
排它锁(行锁)
Exclusive lock
排它锁又称谢所,简称X锁。排它锁不能与其他锁并存,如果一个事务获取了一行数据的排它锁,那么其他事务将不能再获取该行的锁,只有获取了排它锁的事务可以对这一行数据进行读取和修改。
加锁方式
自动:delete / update / insert table 都会默认加锁X锁。
手动: select * from table where id = 1 for update ; commit / rollback;
意向共享锁(Intention Shared/Exclusive Lock )
意向锁分为意向共享锁,意向排它锁。因为为表加锁的前提是表中任意一行都没有被锁住,这也就意味着加表锁之前需要先判断是否有其他行锁存在。为了提高加表锁的效率,使用了意向锁,在加共享锁和排它锁的时候需要先为表加上一个意向共享、排他锁。这样只需要判断表有没有意向锁就可以快速的锁表。
意向锁由存储引擎自己维护。
锁的粒度。
记录锁
锁的粒度,行锁锁住了一条记录(索引)。
对于要锁住的行在表中存在的情况,就是记录锁。比如数据库有id为1的一行数据。加锁通过 select * from table where id = 1 for update ;此时的锁就是记录锁。
间隙锁
[图片上传中...(Lock-2.png-79d15f-1620027539643-0)]
- 间隙锁与间隙锁不冲突。(在不新增的情况下,间隙锁都能够加锁成功。但是如果要新增则不能成功。)
- 间隙锁只阻塞插入。
- Gap Lock只在RR中存在。
邻间锁
由此可见查询大于5小于9的情况下,系统锁住了大于4小于等于10的范围,这样就解决了幻读的问题。所以InnoDB在RR状态就做到了对于幻读的解决。