Java 总结

数据结构

数组

Array、Array List...

特点


char[] cs = new char[5];
char[] cs2 = new char[]{'fish','bone'};
cs[0] = "fish";
...
  1. 内存地址连续,使用前需要指定数组长度。
  2. 有下标。依据下标来获取、设置值。查询效率高。
  3. 增删操作效率较低。(需要考虑下标越界问题,动态扩缩容)

总结


增删操作效率较低主要指:

  1. 新增。
    • 初始长度假如是5,不停的新增,当增加到第6个元素时,则需要从触发<font color="red">扩容</font>操作(一般会提前触发不会等数组满了才触发),扩容操作则需要重新建立一个数组,将之前数据中元素<font color="red">移动</font>过来。
    • 初始长度范围内的新增效率很高。
  2. 修改。
    • 如果要修改某个元素,比如将数组中'bone'修改为‘bones’则需要遍历查抄,然后修改。
    • 直接通过下标修改效率比较高。
  3. 删除。
    • 如果删除虽然元素不在,但是内存依然存在,且空间是连续的。比较占用内存。

综上所述,数组适用于,查询操作较多。修改、新增、删除操作较少的定长数据情况。

链表

单向链表|双向链表(Linked List)

特点


  1. 灵活的空间要求,存储空间不要求连续。
  2. 不支持下标访问,查询效率低,支持顺序遍历检索(next)。
  3. 针对增删操作效率会比较高,只需要操作节点,无需移动元素。

总结


链表适合于新增,删除操作较多的情况,由于无法通过下标快速获取到某个位置元素,所以对查询操作不够友好。

数组、链表问题

  1. 在<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树,通过自旋转实现平衡。
  • 不平衡二叉树
    • 二叉查找树一般都是非平衡的。
  • 完全平衡二叉树
  • 不完全平衡二叉树(红黑树)。

特点

  1. 某节点的左侧树节点值仅包含小于该节点的值。
  2. 某节点的右侧树节点值仅包含大于该节点的值。
  3. 左右树节点每个也必须是二叉树。
  4. 顺序排列。

红黑树

自平衡二叉树。(黑平衡二叉树)

特点

  1. 每个节点要么是红,要么是黑。
  2. 根节点必须是黑色。
  3. 每个叶子节点【NIL】必须是黑色。
  4. 每个红色节点的子节点必须是黑色。
  5. 任意节点向下到任意叶子节点【NIL】的路径包含相同数量的黑色节点(黑平衡二叉树)。

BTree

平衡多路查找树,节点不只是2个。

每个节点都会存储数据。

B+Tree

1kb = 1024b , 1字节 = 8 位。 UUID 32位

平衡多路查找树,节点不只是2个。

是对BTree的优化,数据仅存储在叶子结点,且叶子节点以链表的形式存在。

集合

TreeMap

特点

  1. 本质是红黑树的实现
  2. 有序的

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的注册。

000-springIOC.png

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中解决循环依赖其实是利用了引用传递的特性,允许对象初始化未填充属性阶段先让其他依赖对象完成引用,后续再赋值。

问题:

哪些情况可以解决?哪些不可以解决?

  1. 构造函数循环依赖。(NO)
  2. 多例Prototype Field Setter 类型循环依赖。(NO)
  3. 单例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核心

  1. 切面 Aspect,面向规则,具有相同规则的<font color="red">方法</font> (methonCache)集合。
  2. 通知 Advice,调用(回调)操作。
  3. 切点 PointCurd,需要代理的具体方法。
  4. 目标对象 Target,需要代理的对象()。
  5. 代理 Proxy,JDK ,CGLIB。
  6. 前置通知、后置通知、异常通知。

概述

简单来说,AOP就是通过一定的规则,来当做切面。在Spring 实例化DI阶段,基于规则判断是否满足,满足则使用代理来代替目标对象。在使用代理对象,调用方法时,会从methodCache中获取后续执行链(chain),如果存在,则表示改方法有AOP代理需求,执行后续链操作。如果没有直接利用反射调用目标类方法。

在chain不为null时,Spring 使用责任链模式,来执行操作(有一定的执行顺序)。

// 缓存切点 拦截器
Map<MethodCacheKey, List<Object>> methodCache;

注意

  1. AOP是基于一定的规则来匹配方法(通过拦截器实现),是方法层面的代理和织入。但是由于代理针对的是类,所以在DI操作进行规则验证时,只需要验证类的层面满足就可以。在生成代理对象后调用方法时,才会对方法层面进行验证(chain),通过反射来实现方法的调用。

  2. AOP采用策略模式,使用JDK(有实现接口时),和CGLIB两种方式来实现代理。

  3. 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

执行流程

  1. 解析配置,通过SqlSessionFactoryBuilder创建SqlSessionFactory。
  2. 通过SqlSessionFactory获取SqlSession,执行操作前需要open。
  3. 通过SqlSession获取Mapper代理对象。
  4. 执行操作调用excute执行器,执行xml的sql。
  5. 接收后需要关闭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>
  1. SqlSessionFactoryBean主要为了创建SqlSessionFactory。在创建过程中,最终调用mybatis的创建过程,做一系列的初始化操作,包括插件(分页插件)的初始化,实体对象和jdbc类型处理器的初始化,以及最重要的mapper初始化(将mapper与MapperProxy代理关联起来,getMapper时拿到代理对象)。
  2. MapperScannerConfigurer主要是为了扫描xml文件,通过扫描xml文件获得dao接口,并将dao接口注册到IOC容器中,同时替换IOC容器BeanDefinition目标类,统一指向MapperFactoryBean(实现了FactoryBean接口)。也就是说在DI操作getBean,Dao层实例时,实际调用MapperFactoryBean的getObject方法。这个方法最终返回的是MapperProxy代理对象。
  3. 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

简介

  1. Spring的升级版。功能基于Spring。比如:注解,事件,定时器等
  2. 约定优于配置的体现产物。基于约定的规则简化了一些常用配置。
    1. 引入Web-Stater默认内置tomcat启动,创建MVC,提供配置文件。
    2. 自动注入规则。
  3. Starter启动
  4. 去XML配置(Spring 提供)
    1. @Component/@Controller/@Service。(省略<bean>)
    2. @Configuration/@Component-Scan/@Import。(去XML核心)
    3. @Enable驱动。(重点)
  5. @Conditional条件注解。(重点 Spring 4.X)
  6. 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。这种形式叫做自动装配。

实现自动装配需要考虑几个问题。

  1. 如果需要直接注入,那么前提是IOC容器中要有该实例对象。那么IOC容器是怎么做到实例化的?

    这里可以做一个猜想,想要将类交给IOC容器那么基于正常的方式来说需要这样做

    @Configuration
    public class AutoConfiguration{
        
        @Bean
        public RedisTemplete redistemplete(){
            return new RedisTemplete();
        }
        
        ....
    }
    
  2. 如何告诉Spring需要加载哪些配置类?

动态装载

Spring 提供了两种方式来实现动态配置的装载。

  1. Selector: ImportSelector、 DeferredImportSelector
  2. 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组件

  1. pom.xml引入Sping-boot jar包(需要用到注解@Configuration/@Condition等注解)同时设置<option>属性。

  2. 引入外部化配置。

    • 创建配置对象使用@ConfigurationProperties注解,同时定义配置前缀。

      @ConfigurationProperties(prefix = "fish.bone")
      public class propertiesConfig(){
          
          private Sting url = "test URL";
          
          ...
      }
      
    • 外部化配置提示。

      1. 导入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."
          }
         ]
      }
      
  1. 编写配置类

    • 配置类加入@EnableConfigurationProperties注解加载外部化配置对象。
  2. 创建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(串行化)

能够解决所有问题

如何解决事务并发的读一致性问题

  1. 在读取数据前加锁,防止其他事务对其修改。(性能太差,读操作都要加锁,太粗暴 LBCC Lock Based Concurrency Control )
  2. 生成一个时间点的快照,并为这个快照来提供一定级别的一致性读取。(MVCC Multi Version Concurrency Control)多版本并发控制。

MVCC核心思想

  • 一个事务能够看到的数据版本。
    1. 第一次查询前已经提交的事务的修改。
    2. 本事务的修改。
  • 一个事务不能看到的数据版本。
    1. 在本事务第一次查询之后创建的事务(事务ID比我的事务ID大)
    2. 未提交的事务的修改。

MVCC实现原理

DB_TRX_ID: 插入或更新的最后一个事务的ID,事务编号是自动递增的。

DB_ROLL_PTR:回滚指针。(删除版本)


mvcc-1.png
mvcc-2.png
mvcc-3.png

数据库锁

表锁与行锁

锁定粒度、加锁效率、冲突概率、并发性能。

加锁方式

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 )

意向锁分为意向共享锁,意向排它锁。因为为表加锁的前提是表中任意一行都没有被锁住,这也就意味着加表锁之前需要先判断是否有其他行锁存在。为了提高加表锁的效率,使用了意向锁,在加共享锁和排它锁的时候需要先为表加上一个意向共享、排他锁。这样只需要判断表有没有意向锁就可以快速的锁表。

意向锁由存储引擎自己维护。


锁的粒度。

lock-1.png

记录锁

锁的粒度,行锁锁住了一条记录(索引)。

对于要锁住的行在表中存在的情况,就是记录锁。比如数据库有id为1的一行数据。加锁通过 select * from table where id = 1 for update ;此时的锁就是记录锁。

间隙锁

[图片上传中...(Lock-2.png-79d15f-1620027539643-0)]

  1. 间隙锁与间隙锁不冲突。(在不新增的情况下,间隙锁都能够加锁成功。但是如果要新增则不能成功。)
  2. 间隙锁只阻塞插入。
  3. Gap Lock只在RR中存在。

邻间锁

Lock-4.png

由此可见查询大于5小于9的情况下,系统锁住了大于4小于等于10的范围,这样就解决了幻读的问题。所以InnoDB在RR状态就做到了对于幻读的解决。

Spring Cloud NetFlix

LoadBlance

Ribbon

OpenFeign

Eureka

Config

Gateway

Spring Cloud Alibaba

Doubbo

Nacos

Zookeeper

Sentine

Seata

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容