DearBelinda

DearBelinda 查看完整档案

杭州编辑广东海洋大学  |  软件工程 编辑海康威视  |  应用软件开发工程师 编辑 www.cnblogs.com/bojuetech 编辑
编辑

念念不忘,终有回响。

个人动态

DearBelinda 发布了文章 · 2019-11-29

Spring专题之bean的循环依赖

简介

Spring在初始化容器过程中会对容器内的单例Bean对象进行初始化,而这初始化过程中存在对Bean的依赖注入。当存在A依赖B,B依赖C,C依赖A这种情况的时候,即出现了循环依赖的问题,这个时候Spring是如何来解决的呢?所以本文跟大家一起讨论Spring容器启动过程中对bean循环依赖的解决之道。
image.png

分析-入口

本文源码分析基于SpringFrameWork-5.1.5_release版本。因为spring的循环依赖问题是在容器初始化过程中对单例bean的初始化发生的,所以这里开始从spring对单例bean的初始化开始分析,进入org.springframework.context.support.AbstractApplicationContext类中找到refresh这个这个方法

org.springframework.context.support.AbstractApplicationContext#refresh

public void refresh() throws BeansException, IllegalStateException {
        ......
        try {
            .....
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var9) {
            ......
        } finally {
            ......
        }
    }
}

这段代码中的this.finishBeanFactoryInitialization(beanFactory)这行代码是spring bean初始化的入口,跟踪进入这个方法

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    ......
    beanFactory.preInstantiateSingletons();
}

可以看到preInstantiateSingletons这个方法,这个方法是spring开始正式对容器中的单例bean进行初始化并且依赖注入。

分析-过程

跟踪进入上面这个preInstantiateSingletons方法中,来到这个org.springframework.beans.factory.support.DefaultListableBeanFactory类,方法代码如下:

public void preInstantiateSingletons() throws BeansException {
    ......
    List<String> beanNames = new ArrayList(this.beanDefinitionNames);
    Iterator var2 = beanNames.iterator();
    while(true) {
        String beanName;
        Object bean;
        do {
            while(true) {
                RootBeanDefinition bd;
                do {
                    do {
                        do {
                            ......
                            beanName = (String)var2.next();
                            bd = this.getMergedLocalBeanDefinition(beanName);
                        } while(bd.isAbstract());
                    } while(!bd.isSingleton());
                } while(bd.isLazyInit());
                
                if (this.isFactoryBean(beanName)) {
                    bean = this.getBean("&" + beanName);
                    break;
                }
                this.getBean(beanName);
            }
        } while(!(bean instanceof FactoryBean));
        ......
    }
}

这里需要说明的地方是代码中存在的三个do while循环,spring想表达的逻辑是当bean定义是懒加载,不是单例或者是抽象bean的情况下,会循环下去。言外之意是当一个bean,既不是懒加载,并且是单例以及不是抽象的情况下,会跳出循环从而进入到getBean方法中进行bean的初始化。
跟踪进入getBean方法,最终会进入到org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,需要关注的代码如下:

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = this.transformedBeanName(name);
    //关注点1,先尝试获取bean
    Object sharedInstance = this.getSingleton(beanName);
    Object bean;
    //关注点2,如果sharedInstance不为空,则直接返回。
    if (sharedInstance != null && args == null) {
        if (this.logger.isTraceEnabled()) {
            if (this.isSingletonCurrentlyInCreation(beanName)) {
                this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
            } else {
                this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
    } else {
        //关注点3,这里的意思是当需要从Spring容器中获取Prototype类型的bean时,而这个bean处于正在创建的阶段时会直接抛出异常,言外之意即对于Prototype类型的bean,spring不支持循环依赖。
        if (this.isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        ......
        try {
            RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
            this.checkMergedBeanDefinition(mbd, beanName, args);
            ......

            if (mbd.isSingleton()) {
                //关注点4,单例bean的初始化入口
                sharedInstance = this.getSingleton(beanName, () -> {
                    try {
                        return this.createBean(beanName, mbd, args);
                    } catch (BeansException var5) {
                        this.destroySingleton(beanName);
                        throw var5;
                    }
                });
                bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } 
            ......
        } catch (BeansException var26) {
            this.cleanupAfterBeanCreationFailure(beanName);
            throw var26;
        }
    }
    ......
}

从上面的标注的关注点1中可以看到,获取bean的时候会先尝试从spring容器中获取bean,进入关注1中的方法,最终可以跟踪进入到org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)中,代码如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //先从singletonObjects这个map缓存中获取已经初始化完毕的对象
    Object singletonObject = this.singletonObjects.get(beanName);
    //如果获取不到,要不就说明这个bean对象还没有开始创建,要不就处于正在创建阶段,比如,A依赖B,B依赖A,spring在初始化B对象的时候,会依赖注入,获取依赖A对象,而这个A对象正好处于正在创建阶段,所以会进入到这个判断
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        Map var4 = this.singletonObjects;
        synchronized(this.singletonObjects) {
            //earlySingletonObjects这个也是一个map缓存对象,预加载完成但没有进行依赖注入的bean对象会在这个里面
            singletonObject = this.earlySingletonObjects.get(beanName);
            //如果earlySingletonObjects里获取不到,并且允许获取bean早期引用
            if (singletonObject == null && allowEarlyReference) {
                //重点关注,singletonFactories这个也是一个key为beanName,value为ObjectFactory的map缓存对象(通过这个ObjectFactory的getObject方法可以获取早期应用对象)
                ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

代码逻辑需要关注的地方这里使用了注解说明,其中需要理解的点有几个,第一个是singletonObjects,earlySingletonObjects,singletonFactories这几个缓存对象的作用,第二个是singletonFactories这个对象的key,value是在什么时候在put填充的(后面说明)。

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
private final Map<String, Object> earlySingletonObjects = new HashMap(16);

从上面的源码中可以看出singletonObjects,singletonFactories,earlySingletonObjects都是map对象,分别用于缓存已经初始化完毕(包括依赖注入)的bean,bean对象的ObjectFactory对象(用于获取早期bean对象)以及早期应用bean对象(仅仅缓存作用)。
重新回到关注点4代码中,代码如下:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

if (mbd.isSingleton()) {
    sharedInstance = this.getSingleton(beanName, () -> {
        try {
            return this.createBean(beanName, mbd, args);
        } catch (BeansException var5) {
            this.destroySingleton(beanName);
            throw var5;
        }
    });
    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

跟踪进入org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)方法中,最终会触发this.createBean(beanName, mbd, args)的调用,进行bean的初始化,代码如下:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    Map var3 = this.singletonObjects;
    synchronized(this.singletonObjects) {
        //先从singletonObjects缓存中获取bean
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            //如果当前获取的bean处于正在销毁的阶段,spring直接抛出异常,即无法获取正在处于销毁阶段的bean。
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            ......
            //这里是将beanName加入到singletonsCurrentlyInCreation缓存列表中,用于判断当前beanName是否处于初始化阶段
            this.beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            ......
            try {
                //调用方法参数singletonFactory的getObject,即触发createBean方法初始化bean对象
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch (IllegalStateException var16) {
                ......
            } catch (BeanCreationException var17) {
                ......
            } finally {
                ......
                //从singletonsCurrentlyInCreation缓存列表中移除当前创建的bean,表明该bean对象已经不处于正在初始化阶段
                this.afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                //将成功初始化的bean对象加入singletonObjects缓存map中,并且singletonFactories,earlySingletonObjects对象中移除对应beanName的早期引用获取方法,以及早期引用。
                this.addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

上面的源码逻辑从注解中可以看出,其中创建bean的操作会进入createBean方法,最终可以跟踪进入org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,代码如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    ......
    //bean实例化
    if (instanceWrapper == null) {
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }

    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    ......
    //bean是单例的,并且允许循环依赖以及当前bean处于正在创建的阶段
    boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
    if (earlySingletonExposure) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
        }
        //重点关注,将早期引用获取的ObjectFactory类put进入singletonFactories缓存Map对象中
        this.addSingletonFactory(beanName, () -> {
            return this.getEarlyBeanReference(beanName, mbd, bean);
        });
    }
    Object exposedObject = bean;
    try {
        //依赖注入核心
        this.populateBean(beanName, mbd, instanceWrapper);
        exposedObject = this.initializeBean(beanName, exposedObject, mbd);
    } catch (Throwable var18) {
        ......
    }
    ......
    try {
        ......        
        return exposedObject;
    } catch (BeanDefinitionValidationException var16) {
        ......
    }
}

至此,Spring通过对singletonFactories,earlySingletonObjects,singletonObjects几个缓存对象的使用,解决了单例bean循环依赖的问题。需要注意的是,这里的依赖指的是属性依赖,如果是构造器依赖或者是Prototype类型的bean,spring是不支持的,原因主要是spring通过构造器实例化bean对象的时候,还未调用addSingletonFactory这个方法,所以无法提前获取到依赖bena的预初始化的bean对象,而Prototype类型的bean在获取依赖的时候会判断是否处于正在创建阶段,如果是会直接抛出异常。所以总结来说Spring在5.1.5这个版本中仅支持单例bean的循环依赖,其他版本可以参考本文源码分析自行了解。

分析-总结

总结下,在Spring中,单例A依赖B,B依赖A中,首先Spring先进入A的创建,在通过构造方法实例化后,因为bean是单例的,并且允许循环依赖以及当前bean处于正在创建的阶段,所以spring会将早期引用获取的ObjectFactory类put进入singletonFactories缓存Map对象中,后面开始
populateBean进行依赖注入,会解析得到A的依赖B,从而进入B对象的初始化,当进行B对象的初始化过程中,同样也会进行B对象的依赖注入,会解析得到B的依赖A,从而进入A对象的获取即doGetBean操作,doGetBean中会先调用getSingleton(org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean))方法尝试获取初始化完毕的bena对象或者是预初始化的bean对象,从而最终B获取到了预初始化的A对象(已经实例化但还没进行依赖注入)。

所以平时代码中如果在bean创建出现BeanCurrentlyInCreationException或者BeanCreationException异常时,可以先考虑下是否bean依赖出现循环依赖问题,结合本文的内容进行排查。

查看原文

赞 0 收藏 0 评论 0

DearBelinda 评论了文章 · 2019-03-26

MyBatis原理概括

博文目标:希望大家看了这篇博文后,对Mybatis整体运行过程有一个清晰的认识和把握。

1.什么是 MyBatis ?

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。(这是官网解释)

2.MyBatis运行原理

MyBatis运行原理

框架图解释说明

当框架启动时,通过configuration解析config.xml配置文件和mapper.xml映射文件,映射文件可以使用xml方式或者注解方式,然后由configuration获得sqlsessionfactory对象,再由sqlsessionfactory获得sqlsession数据库访问会话对象,通过会话对象获得对应DAO层的mapper对象,通过调用mapper对象相应方法,框架就会自动执行SQL语句从而获得结果。
讲完了,6不6,可以,牛逼,就这么简单。此时心中是否有千万只草泥马奔涌而出,别急,对于上述,我会在下面针对重点进行一一讲解。

3.xml解析&配置解析

这里请大家自行百度解决,网上也有比较多的解析库,对于大家来说应该是没有什么问题,我们这边主要抓住框架运行的总体过程。对于细节大家可以课后慢慢研究。

mybatis启动(编程式)

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

我们再来看下这个build操作在底层做了什么

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
            ;
        }
    }
    return var5;
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

我们可以很明显的看到mybatis通过XMLConfigBuilder初始化并且解析了我们的配置文件,最后得到一个Configuration类型的对象传给另外一个build操作,这个build操作最后直接new了一个DefaultSqlSessionFactory对象并且返回。

4.何为Mapper对象?

通过上面的叙述我们已经知道我们与mybatis交互主要是通过配置文件或者配置对象,但是我们最终的目的是要操作数据库的,所以mybatis为我们提供了sqlSession这个对象来进行所有的操作,也就是说我们真正通过mybatis操作数据库只要对接sqlSession这个对象就可以了。那么问题来了,我们怎么样通过sqlSession来了操作数据库的呢?

  • 问题1:如何获取sqlSession?

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
    
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }
    
    return var8;
}

由上面代码我们可知我们可以通过SqlSessionFactory的openSession去获取我们的sqlSession,也就是默认得到一个DefaultSqlSession对象。

  • 问题2:Mapper对象怎么来的?

平时我们使用如下代码获得一个Mapper对象。

public <T> T getMapper(Class<T> type) {
    return this.configuration.getMapper(type, this);
}

通过调用DefaultSqlSession的getMapper方法并且传入一个类型对象获取,底层呢调用的是配置对象configuration的getMapper方法,configuration对象是我们在加载DefaultSqlSessionFactory时传入的。

然后我们再来看下这个配置对象的getMapper,传入的是类型对象(补充一点这个类型对象就是我们平时写的DAO层接口,里面是一些数据库操作的接口方法。),和自身也就是DefaultSqlSession。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

我们看到这个configuration的getMapper方法里调用的是mapperRegistry的getMapper方法,参数依然是类型对象和sqlSession。这里呢,我们要先来看下这个MapperRegistry即所谓Mapper注册器是什么。

public class MapperRegistry {
    private final Configuration config;
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }
    ....
}

从这里我们可以知道其实啊这个MapperRegistry就是保持了一个Configuration对象和一个HashMap,而这个HashMap的key是类型对象,value呢是MapperProxyFactory。我们这里先不管MapperProxyFactory是什么东西,我们现在只需要知道MapperRegistry是这么一个东西就可以了。这里有人会问MapperRegistry对象是怎么来的,这里呢是在初始化Configuration对象时初始化了这个MapperRegistry对象的,代码大家可以去看,为了避免混乱,保持贴出来的代码是一条线走下来的,这里就不贴出来了。接下来我们继续看下这个MapperRegistry的getMapper方法。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

这里我们可以看到从knownMappers中获取key为类型对象的MapperProxyFactory对象。然后调用MapperProxyFactory对象的newInstance方法返回,newInstance方法传入sqlSession对象。到这里我们可能看不出什么端倪,那我们就继续往下看这个newInstance方法做的什么事情吧。

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

这里我们可以看到MapperProxyFactory直接new了一个MapperProxy对象,然后调用另外一重载的newInstance方法传入MapperProxy对象。这里我们可以看出一些东西了,通过调用Proxy.newProxyInstance动态代理了我们的mapperProxy对象!这里的mapperInterface即我们的dao层(持久层)接口的类型对象。
所以总结下就是我们通过sqlSesssion.getMapper(clazz)得到的Mapper对象是一个mapperProxy的代理类!
所以也就引出下面的问题。

  • 问题3:为什么我调用mapper对象方法就能发出sql操作数据库?

通过上面的讲解,我们知道了这个mapper对象其实是一个一个mapperProxy的代理类!所以呢这个mapperProxy必然实现了InvocationHandler接口。

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    ....
}

所以当我们调用我们的持久层接口的方法时必然就会调用到这个MapperProxy对象的invoke方法,所以接下来我们进入这个方法看看具体mybatis为我们做了什么。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
        try {
            return method.invoke(this, args);
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    } else {
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }
}

private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
    if (mapperMethod == null) {
        mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
        this.methodCache.put(method, mapperMethod);
    }

    return mapperMethod;
}

从代码中我们可以看到前面做了一个判断,这个判断主要是防止我们调用像toString方法或者equals方法时也能正常调用。然后我们可以看到它调用cachedMapperMethod返回MapperMethod对象,接着就执行这个MapperMethod对象的execute方法。这个cachedMapperMethod方法主要是能缓存我们使用过的一些mapperMethod对象,方便下次使用。这个MapperMethod对象主要是获取方法对应的sql命令和执行相应SQL操作等的处理,具体细节同学们可以抽空研究。

public class MapperMethod {
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }
    ....
}

说到这个mapperMethod对象的execute方法,我们看下代码具体做了什么事情吧。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (this.method.returnsMany()) {
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

我们可以清晰的看到这里针对数据库的增删改查做了对应的操作,这里我们可以看下查询操作。我们可以看到这里针对方法的不同返回值作了不同的处理,我们看下其中一种情况。

param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);

这里我们可以看到它将方法参数类型转换成数据库层面上的参数类型,最后调用sqlSession对象的selectOne方法执行。所以我们看到最后还是回到sqlSession对象上来,也就是前面所说的sqlSession是mybatis提供的与数据库交互的唯一对象。

接下来我们看下这个selectOne方法做了什么事,这里我们看的是defaultSqlSession的selectOne方法。

public <T> T selectOne(String statement, Object parameter) {
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}

我们看到它调用selectList方法,通过去返回值的第一个值作为结果返回。那么我们来看下这个selectList方法。

public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}

我们可以看到这里调用了executor的query方法,我们再进入到query里看看。这里我们看的是BaseExecutor的query方法。

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator i$ = this.deferredLoads.iterator();

            while(i$.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }

        return list;
    }
}

这里我们抓住这样的一句话

list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

进入这个方法

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

    List list;
    try {
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        this.localCache.removeObject(key);
    }

    this.localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        this.localOutputParameterCache.putObject(key, parameter);
    }

    return list;
}

我们看到有个一个方法doQuery,进入方法看看做了什么。点进去后我们发现是抽象方法,我们选择simpleExecutor子类查看实现。

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Connection connection = this.getConnection(statementLog);
    Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

我们可以看到通过configuration对象的newStatementHandler方法构建了一个StatementHandler,然后在调用prepareStatement方法中获取连接对象,通过StatementHandler得到Statement对象。另外我们注意到在获取了Statement对象后调用了parameterize方法。继续跟踪下去(自行跟踪哈)我们可以发现会调用到ParameterHandler对象的setParameters去处理我们的参数。所以这里的prepareStatement方法主要使用了StatementHandler和ParameterHandler对象帮助我们处理语句集和参数的处理。最后还调用了StatementHandler的query方法,我们继续跟踪下去。

这里我们进入到PreparedStatementHandler这个handler查看代码。

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    c ps = (PreparedStatement)statement;
    ps.execute();
    return this.resultSetHandler.handleResultSets(ps);
}

看到这里,我们终于找到了操作数据库的地方了,就是ps.execute()这句代码。底层我们可以发现就是我们平时写的JDBC!然后将这个执行后的PreparedStatement交给resultSetHandler处理结果集,最后返回我们需要的结果集。

以上,我们将mybatis的总体运行思路跟大家讲解了一遍,很多地方没有讲到细节上,因为本篇主要目的就是带大家熟悉mybatis总体流程的,细节大家可以私底下结合mybatis的执行流程去梳理和理解。

好啦,大家有什么问题都可以在评论区评论,我看到会尽快回复哒。哎呀,终于写完了。拜拜。

噢,对了,这里预告下,下下篇我将带大家手写一遍mybatis!没错,纯手写还能跑起来的那种!那下篇呢,下篇当然还是讲mybatis啦,不过是spring-mybatis!

查看原文

DearBelinda 发布了文章 · 2018-09-07

Spring专题之Bean初始化源码分析(2)

前言

这篇是Spring专题Bean初始化的第二篇,主要对bean初始化具体过程的源码分析。上篇博客Spring专题之Bean初始化源码分析(1)中我们对Spring如何开始初始化bean以及bena初始化的总体过程有了大致的了解,接下来就继续上篇博客的结尾处开始来分析初始化bean的具体过程。

Bean初始化

上篇博客中我们知道了Spring在通过判断bean定义是否是单例bean,是否是原型bena之后,最后都是调用了createBean方法去创建bean的,所以我们进入该方法分析具体初始化过程。进入方法后发现是该方法是AbstractBeanFactory的一个抽象方法,真正地调用是在子类AbstractAutowireCapableBeanFactory中重写了。源码如下:

    //创建Bean实例对象
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        if (logger.isDebugEnabled()) {
            logger.debug("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;

        //判断需要创建的Bean是否可以实例化,即是否可以通过当前的类加载器加载
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        //校验和准备Bean中的方法覆盖
        try {
            mbdToUse.prepareMethodOverrides();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            //如果Bean配置了初始化前和初始化后的处理器,则试图返回一个需要创建Bean的代理对象
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        try {
            //创建Bean的入口
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
                logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (ImplicitlyAppearedSingletonException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }

这里我们有两个地方需要了解的,第一个是resolveBeforeInstantiation方法,第二个是doCreateBean方法。之后我们重点分析doCreateBean方法,这里的resolveBeforeInstantiation方法主要是针对Bean如果配置了初始化前和初始化后的处理器,会试图返回一个需要创建Bean的代理对象。那么现在我们开始进入doCreateBean方法,源码如下:

    //真正创建Bean的方法
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        //封装被创建的Bean对象
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        //获取实例化对象的类型
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        //调用PostProcessor后置处理器
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        //向容器中缓存单例模式的Bean对象,以防循环引用
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        //Bean对象的初始化,依赖注入在此触发
        //这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
        Object exposedObject = bean;
        try {
            //将Bean实例对象封装,并且Bean定义中配置的属性值赋值给实例对象
            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)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    //获取当前Bean所依赖的其他Bean
                    for (String dependentBean : dependentBeans) {
                        //对依赖Bean进行类型检查
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been " +
                                "wrapped. This means that said other beans do not use the final version of the " +
                                "bean. This is often the result of over-eager type matching - consider using " +
                                "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        // 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;
    }

这里有三个方法我们需要注意,第一个createBeanInstance方法,这是bean实例化的第一步,第二个是populateBean方法,这是bean依赖注入的地方,第三个是initializeBean,这是初始化bean的地方。

首先我们先看createBeanInstance方法,源码如下:

    //创建Bean的实例对象
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // Make sure bean class is actually resolved at this point.
        //检查确认Bean是可实例化的
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        //使用工厂方法对Bean进行实例化
        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }

        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }

        if (mbd.getFactoryMethodName() != null)  {
            //调用工厂方法实例化
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        // Shortcut when re-creating the same bean...
        //使用容器的自动装配方法进行实例化
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                //配置了自动装配属性,使用容器的自动装配实例化
                //容器的自动装配是根据参数类型匹配Bean的构造方法
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                //使用默认的无参构造方法实例化
                return instantiateBean(beanName, mbd);
            }
        }

        // Need to determine the constructor...
        //使用Bean的构造方法进行实例化
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
            //使用容器的自动装配特性,调用匹配的构造方法实例化
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // No special handling: simply use no-arg constructor.
        //使用默认的无参构造方法实例化
        return instantiateBean(beanName, mbd);
    }

通过上述的源码分析,我们知道Spring这里对bean实例化有几种方式,分别是工厂方法实例化,容器自动装配以及使用默认的无参构造方法实例化。

然后我们在来看populateBean方法是如何进行依赖注入的,源码如下:

    //将Bean属性设置到生成的实例对象上
    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        //如果属性存在但是需要注入的对象为空,则抛出异常。
        if (bw == null) {
            if (mbd.hasPropertyValues()) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // Skip property population phase for null instance.
                return;
            }
        }

        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        //使得InstantiationAwareBeanPostProcessors有机会在属性注入之前修改bean的状态,例如支持一些属性类型的注入。
        boolean continueWithPropertyPopulation = true;

        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }

        if (!continueWithPropertyPopulation) {
            return;
        }
        //获取容器在解析Bean定义资源时为BeanDefiniton中设置的属性值
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

        //对依赖注入处理,首先处理autowiring自动装配的依赖注入
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // Add property values based on autowire by name if applicable.
            //根据Bean名称进行autowiring自动装配处理
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            //根据Bean类型进行autowiring自动装配处理
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }

        //对非autowiring的属性进行依赖注入处理
        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

        if (hasInstAwareBpps || needsDepCheck) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }

        if (pvs != null) {
            //对属性进行注入
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

这里有也有几个方法需要注意,分别是autowireByName、autowireByType以及applyPropertyValues。
我们先来看下autowireByName方法,源码如下:

    //根据名称对属性进行自动依赖注入
    protected void autowireByName(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

        //对Bean对象中非简单属性(不是简单继承的对象,如8中原始类型,字符串,URL等都是简单属性)进行处理
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            //如果Spring IOC容器中包含指定名称的Bean
            if (containsBean(propertyName)) {
                //调用getBean方法向IOC容器索取指定名称的Bean实例,迭代触发属性的初始化和依赖注入
                Object bean = getBean(propertyName);
                //为指定名称的属性赋予属性值
                pvs.add(propertyName, bean);
                //指定名称属性注册依赖Bean名称,进行属性依赖注入
                registerDependentBean(propertyName, beanName);
                if (logger.isDebugEnabled()) {
                    logger.debug("Added autowiring by name from bean name '" + beanName +
                            "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
                            "' by name: no matching bean found");
                }
            }
        }
    }

可以看到这里通过找到Bean中非简单属性的属性,比如CharSequence类型、Number类型、Date类型、URL类型、URI类型、Locale类型、Class类型就会忽略,具体可见BeanUtils的isSimpleProperty方法,遍历所有被找到的属性,如果bean定义中包含了属性名,那么先实例化该属性名对应的bean,注册一下当前bean的依赖bean。

然后我们看下根据类型自动装配autowireByType方法的实现,源码如下:

    //根据类型对属性进行自动依赖注入
    protected void autowireByType(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

        //获取用户定义的类型转换器
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }

        //存放解析的要注入的属性
        Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
        //对Bean对象中非简单属性(不是简单继承的对象,如8中原始类型,字符
        //URL等都是简单属性)进行处理
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            try {
                //获取指定属性名称的属性描述器
                PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
                // Don't try autowiring by type for type Object: never makes sense,
                // even if it technically is a unsatisfied, non-simple property.
                //不对Object类型的属性进行autowiring自动依赖注入
                if (Object.class != pd.getPropertyType()) {
                    //获取属性的setter方法
                    MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                    // Do not allow eager init for type matching in case of a prioritized post-processor.
                    //检查指定类型是否可以被转换为目标对象的类型
                    boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
                    //创建一个要被注入的依赖描述
                    DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                    //根据容器的Bean定义解析依赖关系,返回所有要被注入的Bean对象
                    Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                    if (autowiredArgument != null) {
                        //为属性赋值所引用的对象
                        pvs.add(propertyName, autowiredArgument);
                    }
                    for (String autowiredBeanName : autowiredBeanNames) {
                        //指定名称属性注册依赖Bean名称,进行属性依赖注入
                        registerDependentBean(autowiredBeanName, beanName);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
                                    propertyName + "' to bean named '" + autowiredBeanName + "'");
                        }
                    }
                    //释放已自动注入的属性
                    autowiredBeanNames.clear();
                }
            }
            catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
            }
        }
    }

具体的解析过程主要看resolveDependency方法,这里感兴趣的同学可以研究下具体过程。
最后我们来看下applyPropertyValues方法,源码如下:

    //解析并注入依赖属性的过程
    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        if (pvs.isEmpty()) {
            return;
        }

        //封装属性值
        MutablePropertyValues mpvs = null;
        List<PropertyValue> original;

        if (System.getSecurityManager() != null) {
            if (bw instanceof BeanWrapperImpl) {
                //设置安全上下文,JDK安全机制
                ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
            }
        }

        if (pvs instanceof MutablePropertyValues) {
            mpvs = (MutablePropertyValues) pvs;
            //属性值已经转换
            if (mpvs.isConverted()) {
                // Shortcut: use the pre-converted values as-is.
                try {
                    //为实例化对象设置属性值
                    bw.setPropertyValues(mpvs);
                    return;
                }
                catch (BeansException ex) {
                    throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
                }
            }
            //获取属性值对象的原始类型值
            original = mpvs.getPropertyValueList();
        }
        else {
            original = Arrays.asList(pvs.getPropertyValues());
        }

        //获取用户自定义的类型转换
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        //创建一个Bean定义属性值解析器,将Bean定义中的属性值解析为Bean实例对象的实际值
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

        // Create a deep copy, resolving any references for values.

        //为属性的解析值创建一个拷贝,将拷贝的数据注入到实例对象中
        List<PropertyValue> deepCopy = new ArrayList<>(original.size());
        boolean resolveNecessary = false;
        for (PropertyValue pv : original) {
            //属性值不需要转换
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            //属性值需要转换
            else {
                String propertyName = pv.getName();
                //原始的属性值,即转换之前的属性值
                Object originalValue = pv.getValue();
                //转换属性值,例如将引用转换为IOC容器中实例化对象引用
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                //转换之后的属性值
                Object convertedValue = resolvedValue;
                //属性值是否可以转换
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                if (convertible) {
                    //使用用户自定义的类型转换器转换属性值
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                // Possibly store converted value in merged bean definition,
                // in order to avoid re-conversion for every created bean instance.
                //存储转换后的属性值,避免每次属性注入时的转换工作
                if (resolvedValue == originalValue) {
                    if (convertible) {
                        //设置属性转换之后的值
                        pv.setConvertedValue(convertedValue);
                    }
                    deepCopy.add(pv);
                }
                //属性是可转换的,且属性原始值是字符串类型,且属性的原始类型值不是
                //动态生成的字符串,且属性的原始值不是集合或者数组类型
                else if (convertible && originalValue instanceof TypedStringValue &&
                        !((TypedStringValue) originalValue).isDynamic() &&
                        !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                    pv.setConvertedValue(convertedValue);
                    //重新封装属性的值
                    deepCopy.add(pv);
                }
                else {
                    resolveNecessary = true;
                    deepCopy.add(new PropertyValue(pv, convertedValue));
                }
            }
        }
        if (mpvs != null && !resolveNecessary) {
            //标记属性值已经转换过
            mpvs.setConverted();
        }

        // Set our (possibly massaged) deep copy.
        //进行属性依赖注入
        try {
            bw.setPropertyValues(new MutablePropertyValues(deepCopy));
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Error setting property values", ex);
        }
    }

这里主要是对需要转换的属性进行转换然后进行属性值的依赖注入。

至此属性的依赖注入分析结束,最后我们看下initializeBean方法是如何对bean进行初始化操作的,其源码如下:

    //初始容器创建的Bean实例对象,为其添加BeanPostProcessor后置处理器
    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        //JDK的安全机制验证权限
        if (System.getSecurityManager() != null) {
            //实现PrivilegedAction接口的匿名内部类
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            //为Bean实例对象包装相关属性,如名称,类加载器,所属容器等信息
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        //对BeanPostProcessor后置处理器的postProcessBeforeInitialization
        //回调方法的调用,为Bean实例初始化前做一些处理
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        //调用Bean实例对象初始化的方法,这个初始化方法是在Spring Bean定义配置
        //文件中通过init-method属性指定的
        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        //对BeanPostProcessor后置处理器的postProcessAfterInitialization
        //回调方法的调用,为Bean实例初始化之后做一些处理
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

至此,bean初始化操作的总体过程分析结束,当然中间存在一些细节方面的点没有分析清楚,还需要各位小伙伴自行研究,然后有什么问题可以留言,大家一起讨论一起进步。

查看原文

赞 0 收藏 0 评论 0

DearBelinda 发布了文章 · 2018-09-07

Spring专题之Bean初始化源码分析(1)

前言

Spring IOC容器在初始化之后会对容器中非懒加载的,单例的以及非抽象的bean定义进行bean的初始化操作,同时会也涉及到Bean的后置处理器以及DI(依赖注入)等行为。对于Bean的初始化,Spring是通过第一次调用getBean方法向容器获取bean实例时进行的。下面的源码分析也是基于getBean()作为入口一步步去了解Spring是如何初始化单例Bean的。

Bean初始化

我们知道Spring IOC容器初始化后会对容器中非懒加载的,单例的以及非抽象的bean定义进行bean的初始化操作,所以我们分析源码的入口也就是在容器初始化的入口,分析容器初始化后Spring在什么地方开始第一次的Bean初始化。

在之前的一篇博文Spring专题之IOC源码分析中有分析到Spring IOC容器初始化的过程,过程源码如下:

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
            //子类的refreshBeanFactory()方法启动
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //为BeanFactory配置容器特性,例如类加载器、事件处理器等
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //为容器的某些子类指定特殊的BeanPost事件处理器
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //调用所有注册的BeanFactoryPostProcessor的Bean
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //为BeanFactory注册BeanPost事件处理器.
                //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //初始化信息源,和国际化相关.
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化容器事件传播器.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //调用子类的某些特殊Bean初始化方法
                onRefresh();

                // Check for listener beans and register them.
                //为事件传播器注册事件监听器.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //初始化所有剩余的单例Bean
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                //销毁已创建的Bean
                destroyBeans();

                // Reset 'active' flag.
                //取消refresh操作,重置容器的同步标识.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

上述源码可以观察看在IOC容器被初始化后进行了很多其他的操作,但这些现在我们暂时不关心,我们需要关注的只有finishBeanFactoryInitialization这个方法,这个方法的作用就是初始化所有剩余的单例Bean,所以这也是我们以下分析源码的入口。

finishBeanFactoryInitialization方法源码如下:

    //对配置了lazy-init属性的Bean进行预实例化处init属性的Bean进行预实例化处理理
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        ....

        // Instantiate all remaining (non-lazy-init) singletons.
        //对配置了lazy-init属性的单态模式Bean进行预实例化处理
        beanFactory.preInstantiateSingletons();
    }

这个方法前面一些处理暂时不看,可以知道最后调用了ConfigurableListableBeanFactory的preInstantiateSingletons方法,也就是对配置了lazy-init属性的单态模式Bean进行预实例化处理。
下面进入preInstantiateSingletons方法分析,源码如下:

    //对配置lazy-init属性单态Bean的预实例化
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }

        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        for (String beanName : beanNames) {
            //获取指定名称的Bean定义
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            //Bean不是抽象的,是单态模式的,且lazy-init属性配置为false
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                //如果指定名称的bean是创建容器的Bean
                if (isFactoryBean(beanName)) {
                    //FACTORY_BEAN_PREFIX=”&”,当Bean名称前面加”&”符号
                    //时,获取的是产生容器对象本身,而不是容器产生的Bean.
                    //调用getBean方法,触发容器对Bean实例化和依赖注入过程
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    //标识是否需要预实例化
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        //一个匿名内部类
                        isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
                                ((SmartFactoryBean<?>) factory).isEagerInit(),
                                getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        //调用getBean方法,触发容器对Bean实例化和依赖注入过程
                        getBean(beanName);
                    }
                }
                else {
                    //调用getBean方法,触发容器对Bean实例化和依赖注入过程
                    getBean(beanName);
                }
            }
        }

        //触发bean初始化后的回调
        ...
    }

通过源码解析,我们可以看到这里对于不是抽象的,是单态模式的,且lazy-init属性配置为false的Bean定义进行初始化,而初始化过程正是调用了getBean方法。下面我们进入getBean方法观察Spring对bean的初始化过程。getBean方法及相关调用源码如下:

    //获取IOC容器中指定名称的Bean
    public Object getBean(String name) throws BeansException {
        //doGetBean才是真正向IoC容器获取被管理Bean的过程
        return doGetBean(name, null, null, false);
    }
    //真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        //根据指定的名称获取被管理Bean的名称,剥离指定名称中对容器的相关依赖,如果指定的是别名,将别名转换为规范的Bean名称
        final String beanName = transformedBeanName(name);
        Object bean;

        //先从缓存中取是否已经有被创建过的单态类型的Bean,对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
        Object sharedInstance = getSingleton(beanName);
        //IOC容器创建单例模式Bean实例对象
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                //如果指定名称的Bean在容器中已有单例模式的Bean被创建
                //直接返回已经创建的Bean
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            //获取给定Bean的实例对象,主要是完成FactoryBean的相关处理。注意:BeanFactory是管理容器中Bean的工厂,而FactoryBean是创建创建对象的工厂Bean,两者之间有区别
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            //缓存没有正在创建的单例模式Bean,缓存中已经有已经创建的原型模式Bean,但是由于循环引用的问题导致实例化对象失败
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            //对IOC容器中是否存在指定名称的BeanDefinition进行检查,首先检查是否能在当前的BeanFactory中获取的所需要的Bean,如果不能则委托当前容器的父级容器去查找,如果还是找不到则沿着容器的继承体系向父级容器查找
            BeanFactory parentBeanFactory = getParentBeanFactory();
            //当前容器的父级容器存在,且当前容器中不存在指定名称的Bean
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //解析指定Bean名称的原始名称
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    //委派父级容器根据指定名称和显式的参数查找
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    //委派父级容器根据指定名称和类型查找
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }

            //创建的Bean是否需要进行类型验证,一般不需要
            if (!typeCheckOnly) {
                //向容器标记指定的Bean已经被创建
                markBeanAsCreated(beanName);
            }

            try {
                //根据指定Bean名称获取其父级的Bean定义
                //主要解决Bean继承时子类合并父类公共属性问题
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);
                //获取当前Bean所有依赖Bean的名称
                String[] dependsOn = mbd.getDependsOn();
                //如果当前Bean有依赖Bean
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        //递归调用getBean方法,获取当前Bean的依赖Bean
                        registerDependentBean(dep, beanName);
                        //把被依赖Bean注册给当前依赖的Bean
                        getBean(dep);
                    }
                }
                //创建单例模式Bean的实例对象
                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);
                }

                //IOC容器创建原型模式Bean实例对象
                else if (mbd.isPrototype()) {
                    //原型模式(Prototype)是每次都会创建一个新的对象
                    Object prototypeInstance = null;
                    try {
                        //回调beforePrototypeCreation方法,默认的功能是注册当前创建的原型对象
                        beforePrototypeCreation(beanName);
                        //创建指定Bean对象实例
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        //回调afterPrototypeCreation方法,默认的功能告诉IOC容器指定Bean的原型对象不再创建
                        afterPrototypeCreation(beanName);
                    }
                    //获取给定Bean的实例对象
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                //要创建的Bean既不是单例模式,也不是原型模式,则根据Bean定义资源中配置的生命周期范围,选择实例化Bean的合适方法,这种在Web应用程序中,比较常用,如:request、session、application等生命周期
                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    //Bean定义资源中没有配置生命周期范围,则Bean定义不合法
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        //这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例
                        Object scopedInstance = scope.get(beanName, () -> {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        });
                        //获取给定Bean的实例对象
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        ...
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        //对创建的Bean实例对象进行类型检查
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return convertedBean;
            }
            catch (TypeMismatchException ex) {
                ...
            }
        }
        return (T) bean;
    }

这里通过上述源码的分析,总结以下Spring初始化bean的过程,首先Spring会去缓存中搜索是否已存在bean实例,如果存在则直接取出返回,不存在就判断是否存在父容器,存在则调用父容器的getBean方法进行初始化,否则通过判断bean定义是否是单例bean,是否是原型bena进行相关的初始化操作,可以知道最后都是调用了createBean方法去创建bean的。

至此,通过上述的源码分析,我们对Spring在IOC初始化后对bean的初始化过程有了大致的了解,下篇博客会继续这篇通过源码分析Spring初始化的具体过程。

查看原文

赞 0 收藏 0 评论 0

DearBelinda 发布了文章 · 2018-09-04

Spring专题之IOC源码分析

前言

以下源码基于Spring 5.0.2版本解析。

什么是IOC容器?

容器,顾名思义可以用来容纳一切事物。我们平常所说的Spring IOC容器就是一个可以容纳对象的东西。IOC全名Inversion of Control,即控制反转,什么是控制反转?平时我们代码里需要创建一个对象是需要通过new操作或者反射等方式创建,也就是说现在是我们人为地创建对象,控制对象,那么控制反转的意思就显而易见了,就是将原来属于我们的控制权交由Spring框架进行管理,由Spring替我们创建对象,管理对象以及对象之间的依赖关系。当我们需要使用对象的时候直接问Spring取就可以了。

IOC实现源码分析

对于Spring IOC的实现,总结来说就是定位加载注册定位就是Spring需要定位配置文件的位置,加载就是将配置文件加载进内存,注册就是通过解析配置文件注册BeanDefinition。
下面我们从其中的一种使用Spring的方式一步一步的分析IOC的实现源码。我们平时编程式地使用Spring框架如下代码所示。

public class TestSpring {
    public static void main(String[] args) { 
        //传入配置文件路径信息,初始化Spring IOC容器     
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        //从容器中获取名称为world的bean实例
        context.getBean("world");   
    }
}

所以我们分析Spring IOC实现的入口也就是ClassPathXmlApplicationContext的构造方法。ClassPathXmlApplicationContext的构造方法源码如下:

//这里是我们上述初始化IOC容器的地方
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    this(new String[]{configLocation}, true, (ApplicationContext)null);
}
//实际调用的是这个核心方法初始化IOC容器的
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
    //设置父容器信息
    super(parent);
    //设置配置文件路径,方便接下来的查找
    this.setConfigLocations(configLocations);
    //判断是否需要重新刷新IOC容器
    if (refresh) {
        //调用父类AbstractApplicationContext的refresh方法
        this.refresh();
    }
}

所以这里我们需要进入refresh方法分析Spring IOC是如何刷新的,源码如下:

public void refresh() throws BeansException, IllegalStateException {
    Object var1 = this.startupShutdownMonitor;
    //这里需要进行同步操作,防止多个线程同时刷新容器
    synchronized(this.startupShutdownMonitor) {
        //刷新前的准备工作,获取容器的当时时间,同时给容器设置同步标识。
        this.prepareRefresh();
        //这里是IOC容器初始化的核心方法,告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        //对容器进行一些相关设置,为BeanFactory配置容器特性,例如类加载器、事件处理器等
        this.prepareBeanFactory(beanFactory);

        try {
            //为容器的某些子类指定特殊的BeanPost事件处理器
            this.postProcessBeanFactory(beanFactory);
            //调用bean的后置处理器
            this.invokeBeanFactoryPostProcessors(beanFactory);
            //为BeanFactory注册BeanPost事件处理器.BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
            this.registerBeanPostProcessors(beanFactory);
            //初始化信息源,和国际化相关.
            this.initMessageSource();
            //初始化容器事件传播器.
            this.initApplicationEventMulticaster();
            //调用子类的某些特殊Bean初始化方法
            this.onRefresh();
            //为事件传播器注册事件监听器.
            this.registerListeners();
            //初始化所有剩余的单例Bean
            this.finishBeanFactoryInitialization(beanFactory);
            //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }
            //销毁已创建的Bean
            this.destroyBeans();
            //取消refresh操作,重置容器的同步标识.
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            //缓存清理工作
            this.resetCommonCaches();
        }

    }
}

这里我们主要分析AbstractApplicationContext的obtainFreshBeanFactory方法,源码如下:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

分析AbstractApplicationContext子类AbstractRefreshableApplicationContext的refreshBeanFactory方法,源码如下:

    protected final void refreshBeanFactory() throws BeansException {
        //如果已经有容器,销毁容器中的bean,关闭容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建IOC容器,这里是直接实例化DefaultListableBeanFactory对象
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
            customizeBeanFactory(beanFactory);
            //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

这里主要分析Spring是如何载入Bean定义的,进入AbstractXmlApplicationContext的loadBeanDefinitions方法,源码如下:

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
        //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //为Bean读取器设置SAX xml解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
        initBeanDefinitionReader(beanDefinitionReader);
        //Bean读取器真正实现加载的方法
        loadBeanDefinitions(beanDefinitionReader);
    }

可以看到这里通过创建Bean读取器,最后调用loadBeanDefinitions实现定位、加载和注册。loadBeanDefinitions方法的源码如下:

    //Xml Bean读取器加载Bean定义资源
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //获取Bean定义资源的定位
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
            //的Bean定义资源
            reader.loadBeanDefinitions(configResources);
        }
        //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
            //的Bean定义资源
            reader.loadBeanDefinitions(configLocations);
        }
    }

接下来分析其父类AbstractBeanDefinitionReader是如何读取定位Bean定义资源的,查看AbstractBeanDefinitionReader的loadBeanDefinitions方法,源码如下:

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        //获取在IoC容器初始化过程中设置的资源加载器
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
                //加载多个指定位置的Bean定义资源文件
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
            //加载单个指定位置的Bean定义资源文件
            Resource resource = resourceLoader.getResource(location);
            //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

可以看到这里最后委派调用其子类XmlBeanDefinitionReader的方法实现加载功能,XmlBeanDefinitionReader的loadBeanDefinitions方法源码如下:

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //将读入的XML资源进行特殊编码处理
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    /**
     * Load bean definitions from the specified XML file.
     * @param encodedResource the resource descriptor for the XML file,
     * allowing to specify an encoding to use for parsing the file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    //这里是载入XML形式Bean定义资源文件方法
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            //将资源文件转为InputStream的IO流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //从InputStream中得到XML的解析源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //这里是具体的读取过程
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                //关闭从Resource中得到的IO流
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

查看doLoadBeanDefinitions方法,源码如下:

    //从特定XML文件中实际载入Bean定义资源的方法
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //将XML文件转换为DOM对象,解析过程由documentLoader实现
            Document doc = doLoadDocument(inputSource, resource);
            //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

这里的registerBeanDefinitions方法即对Bean定义解析的详细过程。

    //按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //获得容器中注册的Bean数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        //解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
        //具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //统计解析的Bean数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

这里最后调用了DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法对xml文件的解析和注册。源码如下:

    //根据Spring DTD对Bean的定义规则解析Bean定义Document对象
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        //获得XML描述符
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        //获得Document的根元素
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.

        //具体的解析过程由BeanDefinitionParserDelegate实现,
        //BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        //在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
        preProcessXml(root);
        //从Document的根元素开始进行Bean定义的Document对象
        parseBeanDefinitions(root, this.delegate);
        //在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
        postProcessXml(root);

        this.delegate = parent;
    }

这里主要分析观察parseBeanDefinitions方法是如何解析配置文件的。源码如下:

    //使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //Bean定义的Document对象使用了Spring默认的XML命名空间
        if (delegate.isDefaultNamespace(root)) {
            //获取Bean定义的Document对象根元素的所有子节点
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                //获得Document节点是XML元素节点
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    //Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
                    if (delegate.isDefaultNamespace(ele)) {
                        //使用Spring的Bean规则解析元素节点
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            //Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的
            //解析规则解析Document根节点
            delegate.parseCustomElement(root);
        }
    }

可以看到这里有针对默认命名空间的解析过程,也有针对自定义命名空间的解析过程,这里要注意的是针对非默认命名空间是通过NamespaceHandler的handler方法解析的(这里在讲解aop的时候会重点讲),所以现在我们来分析针对默认命名空间的解析过程parseDefaultElement方法,源码如下:

    //使用Spring的Bean规则解析Document元素节点
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //如果元素节点是<Import>导入元素,进行导入解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        //如果元素节点是<Alias>别名元素,进行别名解析
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        //元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
        //按照Spring的Bean规则解析元素
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

可以看到的是这里包括对<Import>导入元素、<Alias>别名元素、<Bean>元素的解析。下面对各个元素的解析进行分析,首先对导入元素的解析,方法importBeanDefinitionResource的源码如下:

    //解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中
    protected void importBeanDefinitionResource(Element ele) {
        //获取给定的导入元素的location属性
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        //如果导入元素的location属性值为空,则没有导入任何资源,直接返回
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }

        // Resolve system properties: e.g. "${user.dir}"
        //使用系统变量值解析location属性值
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

        Set<Resource> actualResources = new LinkedHashSet<>(4);

        // Discover whether the location is an absolute or relative URI
        //标识给定的导入元素的location是否是绝对路径
        boolean absoluteLocation = false;
        try {
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        }
        catch (URISyntaxException ex) {
            // cannot convert to an URI, considering the location relative
            // unless it is the well-known Spring prefix "classpath*:"
            //给定的导入元素的location不是绝对路径
        }

        // Absolute or relative?
        //给定的导入元素的location是绝对路径
        if (absoluteLocation) {
            try {
                //使用资源读入器加载给定路径的Bean定义资源
                int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
                }
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error(
                        "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
            }
        }
        else {
            // No URL -> considering resource location as relative to the current file.
            //给定的导入元素的location是相对路径
            try {
                int importCount;
                //将给定导入元素的location封装为相对路径资源
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                //封装的相对路径资源存在
                if (relativeResource.exists()) {
                    //使用资源读入器加载Bean定义资源
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                }
                //封装的相对路径资源不存在
                else {
                    //获取Spring IOC容器资源读入器的基本路径
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    //根据Spring IOC容器资源读入器的基本路径加载给定导入路径的资源
                    importCount = getReaderContext().getReader().loadBeanDefinitions(
                            StringUtils.applyRelativePath(baseLocation, location), actualResources);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
                }
            }
            catch (IOException ex) {
                getReaderContext().error("Failed to resolve current resource location", ele, ex);
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
                        ele, ex);
            }
        }
        Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
        //在解析完<Import>元素之后,发送容器导入其他资源处理完成事件
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    }

可以看到这里主要对路径的解析,针对绝对路径或者相对路径调用XmlBeanDefinitionReader的loadBeanDefinitions方法进行BeanDefinition的解析定位,并且在解析完成之后发送容器导入其他资源处理完成事件。
下面分析<Alias>别名元素的解析过程,processAliasRegistration方法的源码如下:

    //解析<Alias>别名元素,为Bean向Spring IoC容器注册别名
    protected void processAliasRegistration(Element ele) {
        //获取<Alias>别名元素中name的属性值
        String name = ele.getAttribute(NAME_ATTRIBUTE);
        //获取<Alias>别名元素中alias的属性值
        String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
        boolean valid = true;
        //<alias>别名元素的name属性值为空
        if (!StringUtils.hasText(name)) {
            getReaderContext().error("Name must not be empty", ele);
            valid = false;
        }
        //<alias>别名元素的alias属性值为空
        if (!StringUtils.hasText(alias)) {
            getReaderContext().error("Alias must not be empty", ele);
            valid = false;
        }
        if (valid) {
            try {
                //向容器的资源读入器注册别名
                getReaderContext().getRegistry().registerAlias(name, alias);
            }
            catch (Exception ex) {
                getReaderContext().error("Failed to register alias '" + alias +
                        "' for bean with name '" + name + "'", ele, ex);
            }
            //在解析完<Alias>元素之后,发送容器别名处理完成事件
            getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
        }
    }

可以看到这里的解析过程比较简单,分别去除bean名称以及别名,然后向容器注册别名,最后发送容器注册别名处理完成事件。下面继续分析对<Bean>元素的解析过程,processBeanDefinition源码如下:

    //解析Bean定义资源Document对象的普通元素
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
        //对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
        // BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                //向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            //在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

可以看到这里主要包括两个过程,第一个是对节点的解析封装,第二个是bean定义的注册。我们先看对节点的解析封装,这里的解析是委托给了BeanDefinitionParserDelegate的parseBeanDefinitionElement方法,源码如下:

    //解析Bean定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的id,name和别名属性
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        //获取<Bean>元素中的id属性值
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //获取<Bean>元素中的name属性值
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        //获取<Bean>元素中的alias属性值
        List<String> aliases = new ArrayList<>();

        //将<Bean>元素中的所有name属性值存放到别名中
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        //如果<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        //检查<Bean>元素所配置的id或者name的唯一性,containingBean标识<Bean>
        //元素中是否包含子<Bean>元素
        if (containingBean == null) {
            //检查<Bean>元素所配置的id、name或者别名是否重复
            checkNameUniqueness(beanName, aliases, ele);
        }

        //详细对<Bean>元素中配置的Bean定义进行解析的地方
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        //如果<Bean>元素中没有配置id、别名或者name,且没有包含子元素
                        //<Bean>元素,为解析的Bean生成一个唯一beanName并注册
                        beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        //如果<Bean>元素中没有配置id、别名或者name,且包含了子元素
                        //<Bean>元素,为解析的Bean使用别名向IOC容器注册
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        //为解析的Bean使用别名注册时,为了向后兼容
                        //Spring1.2/2.0,给别名添加类名后缀
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }
        //当解析出错时,返回null
        return null;
    }

可以看到这里的具体的解析过程交给了parseBeanDefinitionElement方法。

    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {
        //记录解析的<Bean>
        this.parseState.push(new BeanEntry(beanName));

        //这里只读取<Bean>元素中配置的class名字,然后载入到BeanDefinition中去
        //只是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完成
        String className = null;

        //如果<Bean>元素中配置了parent属性,则获取parent属性的值
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            //根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
            //为载入Bean定义信息做准备
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            //对当前的<Bean>元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //为<Bean>元素解析的Bean设置description信息
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            //对<Bean>元素的meta(元信息)属性解析
            parseMetaElements(ele, bd);
            //对<Bean>元素的lookup-method属性解析
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            //对<Bean>元素的replaced-method属性解析
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            //解析<Bean>元素的构造方法设置
            parseConstructorArgElements(ele, bd);
            //解析<Bean>元素的<property>设置
            parsePropertyElements(ele, bd);
            //解析<Bean>元素的qualifier属性
            parseQualifierElements(ele, bd);

            //为当前解析的Bean设置所需的资源和依赖对象
            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));

            return bd;
        }
        catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
        }
        catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
            this.parseState.pop();
        }

        //解析<Bean>元素出错时,返回null
        return null;
    }

然后我们分析注册部分,源码如下:

    //将解析的BeanDefinitionHold注册到容器中
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        //获取解析的BeanDefinition的名称
        String beanName = definitionHolder.getBeanName();
        //向IOC容器注册BeanDefinition
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        //如果解析的BeanDefinition有别名,向容器为其注册别名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

接下来分析BeanDefinitionRegistry的registerBeanDefinition方法,这里的BeanDefinitionRegistry是一个接口,最终由容器DefaultListableBeanFactory进行注册,源码如下:

    //向IOC容器注册解析的BeanDefiniton
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

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

        //校验解析的BeanDefiniton
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);

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

        //检查是否有同名的BeanDefinition已经在IOC容器中注册
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            //重置所有已经注册过的BeanDefinition的缓存
            resetBeanDefinition(beanName);
        }
    }

可以看到最终将bean定义注册进了一个Map结构的beanDefinitionMap。

至此,IOC容器的初始化工作进行完毕,下篇会分析bean的后置处理器是如何工作的。

查看原文

赞 4 收藏 3 评论 0

DearBelinda 关注了用户 · 2018-06-21

已注销 @600801a0db2e0

关注 0

DearBelinda 发布了文章 · 2018-06-21

教你手写Mybatis框架

前言

(。・∀・)ノ゙嗨,小伙伴们,这篇博文将带大家手写mybatis,让大家对mybaits的核心原理以及工作流程有更加深刻的理解。在上篇Spring-Mybatis运行机制概括以及上上篇MyBatis原理概括发布后,由于本人事情较多导致这篇博文却迟迟没出来,这里需要跟大家深感抱歉。但是,这篇如果你看了,我想你应该会觉得这一切都是值得的,因为以后在面试过程中或者在同事面前就可以好好装装啦,哈哈。
嗯哼,废话不多说,咱们直接进入主题。

成果

这里先给大家看看如果你们看了这篇博文之后,能达到什么样的成果。

public class Entry {
    public static void main(String[] args) {
        MtConfiguration configuration = new MtConfiguration("mybatis-config.properties");
        MtSqlSessionFactoryBuilder sqlSessionFactoryBuilder = new MtSqlSessionFactoryBuilder(configuration);
        MtSqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build();
        MtSqlSession sqlSession = sqlSessionFactory.openSession();
        PersonDao personDao = sqlSession.getMapper(PersonDao.class);
        Person person = personDao.queryPersonById(1l);
        System.out.println(person);
    }
}

了解Mybatis使用的同学们,能看出这跟你们常用的Mybatis有什么区别吗?区别还是有的,只是很像有木有。
然后再让大家看看这个仿真版的Mybatis框架由哪些部分组成,下面是框架的代码目录。

clipboard.png

从上面的图中可以看到涵盖了executor、plugin、typehandler等基础组件。然后再看下是如何使用的。

clipboard.png

有没有发现使用过程中跟原生Mybatis使用方式基本一致。最后就是大家应该比较关心的部分了,那就是源代码能提供吗???答案是肯定!
handwritten_mybatis_framework 希望喜欢的小伙伴能star哦。

解析

有些小伙伴可能会有疑问,怎么才能写出这样的框架呢?这个怎么回答呢,其实写出来只需要你对Mybatis的工作原理有大致的了解,知道那些模块是做什么的又是怎么实现的,然后画个流程图,然后定义需要哪些类和接口去实现,最后只需要将这些定义好的类和接口填空就可以了。
那么现在我们来看看这个仿真Mybatis是怎么工作的,当然需要申明的是这个框架仅仅只是作为了解Mybatis运行原理的一个代码练习,有些地方的代码可能只是为了演示这样的效果,并不一定完全跟原生一样。比如Mybatis需要解析XML,而这里我们投了个懒,直接上properties。这样我们就可以重点关注Myabtis的业务流程啦。

接下来对框架的每个部分进行一个简单的说明,具体还请小伙伴去看代码好好了解然后有什么问题直接在评论区或者私信我就可以了,我保证看到就回复,哈哈。

1.configuration模块

顾名思义,就是框架配置类,用于解析配置文件加载相关环境。这里代码里对配置文件的初始化主要有以下这么一个流程。

    public MtConfiguration(String configLocation){
        this.configLocation = configLocation;
        init();
    }

    private void init(){
        try {
            //记载配置文件,这里使用properties代替xml解析
            loadConfigProperties();
            //初始化数据源信息
            initDataSource();
            //解析并加载mapper文件
            loadMapperRegistory();
            //解析加载plugin
            initPluginChain();
            //解析加载typeHandler
            initTypeHandler();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

可以看到实例类的时候调用init方法进行初始化,分别加载配置信息、初始化数据源信息、解析并加载mapper文件、解析加载plugin、解析加载typeHandler这几个步骤。具体如何进行小伙伴们可以看下代码实现,代码写得简单,相信你们一看就懂。

2.session模块

这个模块用于用户与框架交互的入口。包括常用的sqlSession、sqlSessionFactory以及sqlSessionFactoryBuilder。这几个类完全是仿照Mybatis的,只是类实现会相对简单很多。至于每个类用来干嘛,这个我在之前的文章中有讲述,这里就不多做赘述啦,只是简单引导讲解框架信息。

clipboard.png

3.Mapper映射模块

这个模块主要用来注册我们的mapper映射文件的。

clipboard.png

这里需要说明下MapperData这个类,这个类主要记录sql语句和sql语句返回类型信息的。然后MapperRegistory用来保存方法与MapperData的映射关系,这样我们执行某个方法是就能找到对应的Sql语句和对应的返回类型了。这个过程可以结合Mybatis解析mapper.xml的过程去理解。

public class MapperData {
    private String sql;
    private Class type;

    public MapperData(String sql, Class type) {
        this.sql = sql;
        this.type = type;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "MapperData{" +
                "sql='" + sql + '\'' +
                ", resultType=" + type +
                '}';
    }
}

4.Executor执行模块

这个模块主要用于执行SQL语句并且结合各种handler处理,然后这里只是写了一简单的执行器类。

clipboard.png

5.handler模块

这里模仿Mybatis执行SQL时的过程,stamentHandler用于处理语句集、parameterHandler用于处理参数、resultHandler用于处理结果映射。

clipboard.png

6.plugin模块

这里模仿Myabtis插件的工作原理实现的,也就是说这里和原生Mybatis的plugin基本一样,不过这里我又偷了个懒,目前只能实现对executor的拦截。哈哈,别怪我。

clipboard.png

7.typeHandler模块

这里模仿Mybatis类型处理的机制,用于javaType到jdbcType的映射处理以及jdbcType到javaType的映射处理,当然这只是演示这样的一种效果,具体实现其实没有像Mybatis那样复杂。你们看代码应该也能看出来。

clipboard.png

8.配置模块

这里的对框架的配置使用了简单的properties,主要原因还是简单易懂然后节省时间。

clipboard.png

使用

本地使用的话其实就跟项目入口文件那样,拉下代码,修改数据源信息,导入数据库,pom依赖下载然后运行就跟远程Mybatis那样使用就可以了,最后剩下的你们就可以自由发挥修改代码,相信你们能做得更好。

查看原文

赞 5 收藏 6 评论 9

DearBelinda 关注了用户 · 2018-06-07

justjavac @justjavac

会写点 js 代码

关注 14506

DearBelinda 发布了文章 · 2018-06-04

Spring-Mybatis运行机制概括

前言

本篇是继上篇MyBatis原理概括延伸的,所以如果有小伙伴还没看上篇博文的话,可以先去看下,也不会浪费大家太多的时间,因为本篇会结合到上篇叙述的相关内容。

好,切入正题,这篇主要讲一个点,就是我们在结合spring去使用mybatis的时候,spring为我们做了什么事。还是老套路,我们只讲过程思路,具体细节还望各位小伙伴找时间去研究,如果我全讲了,你们也都看懂了,那你们最多也就是感到一种获得感,而不是成就感,获得感是会随着时间的推移而慢慢减少的,所以我这里主要提供给大家一个思路,然后大家可以顺着这条思路慢慢摸索下去,从而获得成就感!

使用spring-mybatis

1.spring-mybatis是什么

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。 使用这个类库中的类, Spring 将会加载必要的 MyBatis 工厂类和 session 类。 这个类库也提供一个简单的方式来注入 MyBatis 数据映射器和 SqlSession 到业务层的 bean 中。 而且它也会处理事务, 翻译 MyBatis 的异常到 Spring 的 DataAccessException 异常(数据访问异常,译者注)中。最终,它并 不会依赖于 MyBatis,Spring 或 MyBatis-Spring 来构建应用程序代码。(这是官网解释)

2.基于XML配置和注解形式使用

a.基于XML配置

一般情况下,我们使用xml的形式引入mybatis,一般的配置如下:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
    <!-- 初始化连接大小 --> 
    <property name="initialSize" value="${initialSize}"></property> 
    <!-- 连接池最大数量 --> 
    <property name="maxActive" value="${maxActive}"></property> 
    <!-- 连接池最大空闲 --> 
    <property name="maxIdle" value="${maxIdle}"></property> 
    <!-- 连接池最小空闲 --> 
    <property name="minIdle" value="${minIdle}"></property> 
    <!-- 获取连接最大等待时间 --> 
    <property name="maxWait" value="${maxWait}"></property> 
</bean>

<!-- spring和MyBatis的完美结合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- 自动扫描mapping.xml文件 --> 
    <property name="mapperLocations" value="classpath:com/javen/mapping/*.xml"></property> 
</bean>

<!-- DAO接口所在包名,Spring会自动查找其下的类 --> 
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 
    <property name="basePackage" value="com.javen.dao" /> 
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> 
</bean>

如上配置所示,我们一般需要申明dataSource、sqlSessionFactory以及MapperScannerConfigurer。如何我们还有其他mybatis的配置,比如plugin、typehandler等,我们可以另外申明一个mybaits-config.xml文件,在sqlSessionFactory配置中引入即可。下面对各部分作用总结下。
dataSource:申明一个数据源;
sqlSessionFactory:申明一个sqlSession的工厂;
MapperScannerConfigurer:让spring自动扫描我们持久层的接口从而自动构建代理类。

b.基于注解形式

注解形式的话相当于将上述的xml配置一一对应成注解的形式

@Configuration  
@MapperScan(value="org.fhp.springmybatis.dao")  
public class DaoConfig {
  
    @Value("${jdbc.driverClass}")  
    private String driverClass;  
  
    @Value("${jdbc.user}")  
    private String user;  
  
    @Value("${jdbc.password}")  
    private String password;  
  
    @Value("${jdbc.jdbcUrl}")  
    private String jdbcUrl;  
  
    @Bean  
    public DataSource dataSource() {  
        DriverManagerDataSource dataSource = new DriverManagerDataSource();  
        dataSource.setDriverClassName(driverClass);  
        dataSource.setUsername(user);  
        dataSource.setPassword(password);  
        dataSource.setUrl(jdbcUrl);  
        return dataSource;  
    }  
  
    @Bean  
    public DataSourceTransactionManager transactionManager() {  
        return new DataSourceTransactionManager(dataSource());  
    }  
  
    @Bean  
    public SqlSessionFactory sqlSessionFactory() throws Exception {  
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();  
        sessionFactory.setDataSource(dataSource());  
        return sessionFactory.getObject();  
    }  
}  

很明显,一样需要一个dataSource,SqlSessionFactory以及一个@MapperScan的注解。这个注解的作用跟上述的
MapperScannerConfigurer的作用是一样的。

3.spring和mybatis无缝整合的机制

a.BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar的认识

在讲mybatis如何无缝整合进spring之前,我们先认识下BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar这两个接口的作用。

我们先看下这两个接口是什么样的。

//BeanDefinitionRegistryPostProcessor接口
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

//ImportBeanDefinitionRegistrar接口
public interface ImportBeanDefinitionRegistrar {
    void registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2);
}

对于这两个接口我们先看官方文档给我们的解释。

以下是BeanDefinitionRegistryPostProcessor的解释:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor

Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.

意思大概就是我们可以扩展spring对于bean definitions的定义。也就是说可以让我们实现自定义的注册bean定义的逻辑。
再来看下ImportBeanDefinitionRegistrar的解释:

public interface ImportBeanDefinitionRegistrar Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.

Along with @Configuration and ImportSelector, classes of this type may be provided to the @Import annotation (or may also be returned from an ImportSelector).

通俗解释来讲就是在@Configuration上使用@Import时可以自定义beanDefinition,或者作为ImportSelector接口的返回值(有兴趣的小伙伴可以自行研究)。

所以总结下就是如果我想扩展beanDefinition那么我可以继承这两个接口实现。下面我们就从mybatis配置方式入手讲讲spring和mybatis是如何无缝整合的。

b.基于XML配置mybatis是如何整合进spring的

首先,容器启动的时候,我们在xml配置中的SqlSessionFactoryBean会被初始化,所以我们先看下SqlSessionFactoryBean是在初始化的时候作了哪些工作。

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
    private Resource configLocation;
    private Configuration configuration;
    private Resource[] mapperLocations;
    private DataSource dataSource;
    private TransactionFactory transactionFactory;
    private Properties configurationProperties;
    private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    private SqlSessionFactory sqlSessionFactory;
    private String environment = SqlSessionFactoryBean.class.getSimpleName();
    private boolean failFast;
    private Interceptor[] plugins;
    private TypeHandler<?>[] typeHandlers;
    private String typeHandlersPackage;
    private Class<?>[] typeAliases;
    private String typeAliasesPackage;
    private Class<?> typeAliasesSuperType;
    private DatabaseIdProvider databaseIdProvider;
    private Class<? extends VFS> vfs;
    private Cache cache;
    private ObjectFactory objectFactory;
    private ObjectWrapperFactory objectWrapperFactory;

    public SqlSessionFactoryBean() {
    }
    ...
}

我们可以看到这个类实现了FactoryBean、InitializingBean和ApplicationListener接口,对应的接口在bean初始化的时候为执行些特定的方法(如果不清楚的小伙伴请自行百度,这里不作过多叙述)。现在我们来看看都有哪些方法会被执行,这些方法又作了哪些工作。

//FactoryBean
public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        this.afterPropertiesSet();
    }

    return this.sqlSessionFactory;
}

//InitializingBean
public void afterPropertiesSet() throws Exception {
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");
    Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = this.buildSqlSessionFactory();
}

//ApplicationListener
public void onApplicationEvent(ApplicationEvent event) {
    if (this.failFast && event instanceof ContextRefreshedEvent) {
        this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }

}

通过观察代码我们可以知道前面两个都是在做同一件事情,那就是在构建sqlSessionFactory,在构建sqlSessionFactory时mybatis会去解析配置文件,构建configuation。后面的onApplicationEvent主要是监听应用事件时做的一些事情(不详讲,有兴趣的同学可以自己去了解下)。

其次,我们回忆下我们在xml配置中还配置了MapperScannerConfigurer,或者也可以配置多个的MapperFactoryBean,道理都是一样的,只是MapperScannerConfigurer帮我们封装了这一个过程,可以实现自动扫描指定包下的mapper接口构建MapperFactoryBean。

问题1:为什么我们从spring容器中能直接获取对应mapper接口的实现类?而不用使用sqlSession去getMapper呢?
答案其实在上面就已经为大家解答了,就是MapperFactoryBean。我们先看看这个类。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    private Class<T> mapperInterface;
    private boolean addToConfig = true;

    public MapperFactoryBean() {
    }

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }
    ...
}

这个类继承了SqlSessionDaoSupport,实现了FactoryBean。
我们先讲讲SqlSessionDaoSupport这个类

public abstract class SqlSessionDaoSupport extends DaoSupport {
    private SqlSession sqlSession;
    private boolean externalSqlSession;

    public SqlSessionDaoSupport() {
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
            this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }

    }

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
    }

    public SqlSession getSqlSession() {
        return this.sqlSession;
    }

    protected void checkDaoConfig() {
        Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
    }
}

可以看到这个类继承了DaoSupport,我们再来看下这个类。

public abstract class DaoSupport implements InitializingBean {
    protected final Log logger = LogFactory.getLog(this.getClass());

    public DaoSupport() {
    }

    public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
        this.checkDaoConfig();

        try {
            this.initDao();
        } catch (Exception var2) {
            throw new BeanInitializationException("Initialization of DAO failed", var2);
        }
    }

    protected abstract void checkDaoConfig() throws IllegalArgumentException;

    protected void initDao() throws Exception {
    }
}

可以看到实现了InitializingBean接口,所以在类初始化时为执行afterPropertiesSet方法,我们看到afterPropertiesSet方法里面有checkDaoConfig方法和initDao方法,其中initDao是模板方法,提供子类自行实现相关dao初始化的操作,我们看下checkDaoConfig方法作了什么事。

//MapperFactoryBean
protected void checkDaoConfig() {
    super.checkDaoConfig();
    Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    Configuration configuration = this.getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
        try {
            configuration.addMapper(this.mapperInterface);
        } catch (Exception var6) {
            this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
            throw new IllegalArgumentException(var6);
        } finally {
            ErrorContext.instance().reset();
        }
    }

}

这个方法具体的实现是在MapperFactoryBean类里面的,主要作用就是对验证mapperInterface是否存在configuration对象里面。

然后我们再来看下MapperFactoryBean实现了FactoryBean的目的是什么。我们都知道FactoryBean有一个方法是getObject,这个方法的作用就是在spring容器初始化bean时,如果判断这个类是否继承自FactoryBean,那么在获取真正的bean实例时会调用getObject,将getObject方法返回的值注册到spring容器中。在明白了这些知识点之后,我们看下MapperFactoryBean的getObject方法是如何实现的。

//MapperFactoryBean
public T getObject() throws Exception {
    return this.getSqlSession().getMapper(this.mapperInterface);
}

看到这里是否就已经明白为什么在结合spring时我们不需要使用sqlSession对象去获取我们的mapper实现类了吧。因为spring帮我们作了封装!
之后的操作可以结合上面博文去看mybatis如何获取到对应的Mapper对象的了。附上上篇博文地址:MyBatis原理概括

接下来我们看下mybatis是如何结合spring构建MapperFactoryBean的beanDefinition的。这里我们需要看看MapperScannerConfigurer这个类,这个类的目的就是扫描我们指定的dao层(持久层)对应的包(package),构建相应的beanDefinition提供给spring容器去实例化我们的mapper接口对象。

//MapperScannerConfigurer
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    private String basePackage;
    private boolean addToConfig = true;
    private SqlSessionFactory sqlSessionFactory;
    private SqlSessionTemplate sqlSessionTemplate;
    private String sqlSessionFactoryBeanName;
    private String sqlSessionTemplateBeanName;
    private Class<? extends Annotation> annotationClass;
    private Class<?> markerInterface;
    private ApplicationContext applicationContext;
    private String beanName;
    private boolean processPropertyPlaceHolders;
    private BeanNameGenerator nameGenerator;

    public MapperScannerConfigurer() {
    }
    ...
}

通过代码,我们可以看到这个类实现了BeanDefinitionRegistryPostProcessor这个接口,通过前面对BeanDefinitionRegistryPostProcessor的讲解,我们去看看MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法的实现。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
        this.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.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}

可以看这里就是在构建ClassPathMapperScanner对象,然后调用scan方法扫描。接下来我们继续看这个扫描的操作,因为这个类继承了ClassPathBeanDefinitionScanner,调用的scan方法是在ClassPathBeanDefinitionScanner里申明的。

//ClassPathBeanDefinitionScanner
public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    this.doScan(basePackages);
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}

这里我们需要注意doScan这个方法,这个方法在ClassPathMapperScanner中重写了。

//ClassPathMapperScanner
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
        this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
        this.processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

这里调用了父类的doScan得到beanDefinitions的集合。这里的父类的doScan方法是spring提供的包扫描操作,这里不过多叙述,感兴趣的小伙伴可以自行研究。我们还注意到在得到beanDefinitions集合后,这里还调用了processBeanDefinitions方法,这里是对beanDefinition做了一些特殊的处理以满足mybaits的需求。我们先来看下这个方法。

//ClassPathMapperScanner#doScan
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    Iterator var3 = beanDefinitions.iterator();

    while(var3.hasNext()) {
        BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
        GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
        }

        definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
        definition.setBeanClass(this.mapperFactoryBean.getClass());
        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) {
                this.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) {
                this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
            }

            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
        }

        if (!explicitFactoryUsed) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            }

            definition.setAutowireMode(2);
        }
    }

}

这里我们注意到有这么一行代码:definition.setBeanClass(this.mapperFactoryBean.getClass()),看到这里我们就可以知道为什么spring在加载初始化我们的mapper接口对象会初始化成MapperFactoryBean对象了

好了,到这里我们也就明白了spring是如何帮我们加载注册我们的mapper接口对应的实现类了。对于代码里涉及到的其他细节,这里暂时不作过多讲解,还是老套路,只讲解总体思路。

c.基于注解配置mybatis是如何整合进spring的

基于注解形式的配置其实就是将xml配置对应到注解中来,本质上的流程还是一样的。所以这里我就简单讲讲。我们先看看MapperScannerRegistrar这个类,因为这个类是spring构建MapperFactoryBean的核心类。

//MapperScannerRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    public MapperScannerRegistrar() {
    }
    ...
}

这里我们注意到MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,在前面的叙述中我们已经知道了实现ImportBeanDefinitionRegistrar接口的作用是什么了,所以我们直接看看这里具体做了什么操作。

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    if (this.resourceLoader != null) {
        scanner.setResourceLoader(this.resourceLoader);
    }

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
        scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
        scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
        scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
        scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    List<String> basePackages = new ArrayList();
    String[] var10 = annoAttrs.getStringArray("value");
    int var11 = var10.length;

    int var12;
    String pkg;
    for(var12 = 0; var12 < var11; ++var12) {
        pkg = var10[var12];
        if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
        }
    }

    var10 = annoAttrs.getStringArray("basePackages");
    var11 = var10.length;

    for(var12 = 0; var12 < var11; ++var12) {
        pkg = var10[var12];
        if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
        }
    }

    Class[] var14 = annoAttrs.getClassArray("basePackageClasses");
    var11 = var14.length;

    for(var12 = 0; var12 < var11; ++var12) {
        Class<?> clazz = var14[var12];
        basePackages.add(ClassUtils.getPackageName(clazz));
    }

    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
}

通过观察我们看到最后还是调用了ClassPathMapperScanner的doScan去扫描指定包下的mapper接口(持久层),然后构建对应的beanDefinition类。前面我们知道是通过MapperScan这个注解去指定包的,然后我们也可以看到,在这个方法一开始就取出这个注解的值,然后进行接下来的操作的。

 AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));

之后的过程其实跟xml形式配置的一样了。

后序

好啦,这篇没想啰理八嗦说了那么多,可能有好多小伙伴看到最后也是懵逼状态,这里有个建议,打开IDE,边看边对着代码跟踪,如果哪里觉得不对,可以直接debug。

这里给大家提个看源码的建议,就是猜想+验证。先猜想自己的想法,然后通过查找相关问题或者debug代码去验证自己的思路。

好啦,到这里为止,mybatis和spring-mybatis的基本原理都跟大家说了一遍,不知道小伙伴们有没有收获呢,下一篇,我会带大家手写一遍mybatis,是纯手写而且还能跑起来的那种哦!

:本人不才,以上如有错误的地方或者不规范的叙述还望各位小伙伴批评指点。

查看原文

赞 2 收藏 2 评论 0

DearBelinda 发布了文章 · 2018-05-31

MyBatis原理概括

博文目标:希望大家看了这篇博文后,对Mybatis整体运行过程有一个清晰的认识和把握。

1.什么是 MyBatis ?

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。(这是官网解释)

2.MyBatis运行原理

MyBatis运行原理

框架图解释说明

当框架启动时,通过configuration解析config.xml配置文件和mapper.xml映射文件,映射文件可以使用xml方式或者注解方式,然后由configuration获得sqlsessionfactory对象,再由sqlsessionfactory获得sqlsession数据库访问会话对象,通过会话对象获得对应DAO层的mapper对象,通过调用mapper对象相应方法,框架就会自动执行SQL语句从而获得结果。
讲完了,6不6,可以,牛逼,就这么简单。此时心中是否有千万只草泥马奔涌而出,别急,对于上述,我会在下面针对重点进行一一讲解。

3.xml解析&配置解析

这里请大家自行百度解决,网上也有比较多的解析库,对于大家来说应该是没有什么问题,我们这边主要抓住框架运行的总体过程。对于细节大家可以课后慢慢研究。

mybatis启动(编程式)

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

我们再来看下这个build操作在底层做了什么

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
            ;
        }
    }
    return var5;
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

我们可以很明显的看到mybatis通过XMLConfigBuilder初始化并且解析了我们的配置文件,最后得到一个Configuration类型的对象传给另外一个build操作,这个build操作最后直接new了一个DefaultSqlSessionFactory对象并且返回。

4.何为Mapper对象?

通过上面的叙述我们已经知道我们与mybatis交互主要是通过配置文件或者配置对象,但是我们最终的目的是要操作数据库的,所以mybatis为我们提供了sqlSession这个对象来进行所有的操作,也就是说我们真正通过mybatis操作数据库只要对接sqlSession这个对象就可以了。那么问题来了,我们怎么样通过sqlSession来了操作数据库的呢?

  • 问题1:如何获取sqlSession?

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
    
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }
    
    return var8;
}

由上面代码我们可知我们可以通过SqlSessionFactory的openSession去获取我们的sqlSession,也就是默认得到一个DefaultSqlSession对象。

  • 问题2:Mapper对象怎么来的?

平时我们使用如下代码获得一个Mapper对象。

public <T> T getMapper(Class<T> type) {
    return this.configuration.getMapper(type, this);
}

通过调用DefaultSqlSession的getMapper方法并且传入一个类型对象获取,底层呢调用的是配置对象configuration的getMapper方法,configuration对象是我们在加载DefaultSqlSessionFactory时传入的。

然后我们再来看下这个配置对象的getMapper,传入的是类型对象(补充一点这个类型对象就是我们平时写的DAO层接口,里面是一些数据库操作的接口方法。),和自身也就是DefaultSqlSession。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

我们看到这个configuration的getMapper方法里调用的是mapperRegistry的getMapper方法,参数依然是类型对象和sqlSession。这里呢,我们要先来看下这个MapperRegistry即所谓Mapper注册器是什么。

public class MapperRegistry {
    private final Configuration config;
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }
    ....
}

从这里我们可以知道其实啊这个MapperRegistry就是保持了一个Configuration对象和一个HashMap,而这个HashMap的key是类型对象,value呢是MapperProxyFactory。我们这里先不管MapperProxyFactory是什么东西,我们现在只需要知道MapperRegistry是这么一个东西就可以了。这里有人会问MapperRegistry对象是怎么来的,这里呢是在初始化Configuration对象时初始化了这个MapperRegistry对象的,代码大家可以去看,为了避免混乱,保持贴出来的代码是一条线走下来的,这里就不贴出来了。接下来我们继续看下这个MapperRegistry的getMapper方法。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

这里我们可以看到从knownMappers中获取key为类型对象的MapperProxyFactory对象。然后调用MapperProxyFactory对象的newInstance方法返回,newInstance方法传入sqlSession对象。到这里我们可能看不出什么端倪,那我们就继续往下看这个newInstance方法做的什么事情吧。

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

这里我们可以看到MapperProxyFactory直接new了一个MapperProxy对象,然后调用另外一重载的newInstance方法传入MapperProxy对象。这里我们可以看出一些东西了,通过调用Proxy.newProxyInstance动态代理了我们的mapperProxy对象!这里的mapperInterface即我们的dao层(持久层)接口的类型对象。
所以总结下就是我们通过sqlSesssion.getMapper(clazz)得到的Mapper对象是一个mapperProxy的代理类!
所以也就引出下面的问题。

  • 问题3:为什么我调用mapper对象方法就能发出sql操作数据库?

通过上面的讲解,我们知道了这个mapper对象其实是一个一个mapperProxy的代理类!所以呢这个mapperProxy必然实现了InvocationHandler接口。

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    ....
}

所以当我们调用我们的持久层接口的方法时必然就会调用到这个MapperProxy对象的invoke方法,所以接下来我们进入这个方法看看具体mybatis为我们做了什么。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
        try {
            return method.invoke(this, args);
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    } else {
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }
}

private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
    if (mapperMethod == null) {
        mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
        this.methodCache.put(method, mapperMethod);
    }

    return mapperMethod;
}

从代码中我们可以看到前面做了一个判断,这个判断主要是防止我们调用像toString方法或者equals方法时也能正常调用。然后我们可以看到它调用cachedMapperMethod返回MapperMethod对象,接着就执行这个MapperMethod对象的execute方法。这个cachedMapperMethod方法主要是能缓存我们使用过的一些mapperMethod对象,方便下次使用。这个MapperMethod对象主要是获取方法对应的sql命令和执行相应SQL操作等的处理,具体细节同学们可以抽空研究。

public class MapperMethod {
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }
    ....
}

说到这个mapperMethod对象的execute方法,我们看下代码具体做了什么事情吧。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (this.method.returnsMany()) {
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

我们可以清晰的看到这里针对数据库的增删改查做了对应的操作,这里我们可以看下查询操作。我们可以看到这里针对方法的不同返回值作了不同的处理,我们看下其中一种情况。

param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);

这里我们可以看到它将方法参数类型转换成数据库层面上的参数类型,最后调用sqlSession对象的selectOne方法执行。所以我们看到最后还是回到sqlSession对象上来,也就是前面所说的sqlSession是mybatis提供的与数据库交互的唯一对象。

接下来我们看下这个selectOne方法做了什么事,这里我们看的是defaultSqlSession的selectOne方法。

public <T> T selectOne(String statement, Object parameter) {
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}

我们看到它调用selectList方法,通过去返回值的第一个值作为结果返回。那么我们来看下这个selectList方法。

public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}

我们可以看到这里调用了executor的query方法,我们再进入到query里看看。这里我们看的是BaseExecutor的query方法。

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator i$ = this.deferredLoads.iterator();

            while(i$.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }

        return list;
    }
}

这里我们抓住这样的一句话

list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

进入这个方法

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

    List list;
    try {
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        this.localCache.removeObject(key);
    }

    this.localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        this.localOutputParameterCache.putObject(key, parameter);
    }

    return list;
}

我们看到有个一个方法doQuery,进入方法看看做了什么。点进去后我们发现是抽象方法,我们选择simpleExecutor子类查看实现。

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Connection connection = this.getConnection(statementLog);
    Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

我们可以看到通过configuration对象的newStatementHandler方法构建了一个StatementHandler,然后在调用prepareStatement方法中获取连接对象,通过StatementHandler得到Statement对象。另外我们注意到在获取了Statement对象后调用了parameterize方法。继续跟踪下去(自行跟踪哈)我们可以发现会调用到ParameterHandler对象的setParameters去处理我们的参数。所以这里的prepareStatement方法主要使用了StatementHandler和ParameterHandler对象帮助我们处理语句集和参数的处理。最后还调用了StatementHandler的query方法,我们继续跟踪下去。

这里我们进入到PreparedStatementHandler这个handler查看代码。

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    c ps = (PreparedStatement)statement;
    ps.execute();
    return this.resultSetHandler.handleResultSets(ps);
}

看到这里,我们终于找到了操作数据库的地方了,就是ps.execute()这句代码。底层我们可以发现就是我们平时写的JDBC!然后将这个执行后的PreparedStatement交给resultSetHandler处理结果集,最后返回我们需要的结果集。

以上,我们将mybatis的总体运行思路跟大家讲解了一遍,很多地方没有讲到细节上,因为本篇主要目的就是带大家熟悉mybatis总体流程的,细节大家可以私底下结合mybatis的执行流程去梳理和理解。

好啦,大家有什么问题都可以在评论区评论,我看到会尽快回复哒。哎呀,终于写完了。拜拜。

噢,对了,这里预告下,下下篇我将带大家手写一遍mybatis!没错,纯手写还能跑起来的那种!那下篇呢,下篇当然还是讲mybatis啦,不过是spring-mybatis!

查看原文

赞 12 收藏 12 评论 4

认证与成就

  • 获得 23 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-09-23
个人主页被 1.1k 人浏览