阿里面試題:Mybatis中的Dao接口和XML文件裏的SQL是如何創建關係的?

1、解析XML

首先,Mybatis在初始化SqlSessionFactoryBean的時候,找到mapperLocations路徑去解析裏面全部的XML文件,這裏咱們重點關注兩部分。javascript

一、建立SqlSource

Mybatis會把每一個SQL標籤封裝成SqlSource對象。而後根據SQL語句的不一樣,又分爲動態SQL和靜態SQL。其中,靜態SQL包含一段String類型的sql語句;而動態SQL則是由一個個SqlNode組成。java


假如咱們有這樣一個SQL:面試

<select id="getUserById" resultType="user">
    select * from user 
    <where>
        <if test="uid!=null">
            and uid=#{uid}
        </if>
    </where>
</select>   
複製代碼

它對應的SqlSource對象看起來應該是這樣的:spring


二、建立MappedStatement

XML文件中的每個SQL標籤就對應一個MappedStatement對象,這裏面有兩個屬性很重要。sql

  • id

全限定類名+方法名組成的ID。緩存

  • sqlSource

當前SQL標籤對應的SqlSource對象。bash

建立完MappedStatement對象,將它緩存到Configuration#mappedStatements中。mybatis

Configuration對象,咱們知道它就是Mybatis中的大管家,基本全部的配置信息都維護在這裏。把全部的XML都解析完成以後,Configuration就包含了全部的SQL信息。架構


到目前爲止,XML就解析完成了。看到上面的圖示,聰明如你,也許就大概知道了。當咱們執行Mybatis方法的時候,就經過全限定類名+方法名找到MappedStatement對象,而後解析裏面的SQL內容,執行便可。app

2、Dao接口代理

咱們的Dao接口並無實現類,那麼,咱們在調用它的時候,它是怎樣最終執行到咱們的SQL語句的呢?

首先,咱們在Spring配置文件中,通常會這樣配置:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.viewscenes.netsupervisor.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean>
複製代碼

或者你的項目是基於SpringBoot的,那麼確定也見過這種:
@MapperScan("com.xxx.dao")

它們的做用是同樣的。將包路徑下的全部類註冊到Spring Bean中,而且將它們的beanClass設置爲MapperFactoryBean。有意思的是,MapperFactoryBean實現了FactoryBean接口,俗稱工廠Bean。那麼,當咱們經過@Autowired注入這個Dao接口的時候,返回的對象就是MapperFactoryBean這個工廠Bean中的getObject()方法對象。

那麼,這個方法幹了些什麼呢?

簡單來講,它就是經過JDK動態代理,返回了一個Dao接口的代理對象,這個代理對象的處理器是MapperProxy對象。全部,咱們經過@Autowired注入Dao接口的時候,注入的就是這個代理對象,咱們調用到Dao接口的方法時,則會調用到MapperProxy對象的invoke方法。

對這塊內容不太能理解的朋友,能夠先看看Spring中的FactoryBean 和 JDK動態代理相關知識

曾經有個朋友問過這樣一個問題:

對於有實現的dao接口,mapper還會用代理麼?

答案是確定,只要你配置了MapperScan,它就會去掃描,而後生成代理。可是,若是你的dao接口有實現類,而且這個實現類也是一個Spring Bean,那就要看你在Autowired的時候,去注入哪個了。

具體什麼意思呢?咱們來到一個例子。

若是咱們給userDao搞一個實現類,而且把它註冊到Spring。

@Component
public class UserDaoImpl implements UserDao{
    public List<User> getUserList(Map<String,Object> map){
        return new ArrayList<User>();
    }
}
複製代碼

而後咱們在Service方法中,注入這個userDao。猜猜會發生什麼?

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    UserMapper userDao1;

    public List<User> getUserList(Map<String,Object> map) {
        return userDao1.getUserList(map);
    }
}
複製代碼

也許你已經猜到了,是的,它會啓動報錯。由於在注入的時候,找到了兩個UserMapper的實例對象。日誌是這樣的:
No qualifying bean of type [com.viewscenes.netsupervisor.dao.UserDao] is defined: expected single matching bean but found 2: userDaoImpl,userDao

固然了,也許咱們的命名不太規範。其實咱們經過名字注入就能夠了,像這樣:
@Autowired UserMapper userDao; 或者 @Autowired UserMapper userDaoImpl; 再或者給你其中一個Bean加上@Primary註解。

說着說着可能扯遠了,咱們繼續回到Mybatis。那麼,目前爲止,咱們經過Dao接口也有了代理實現,因此就能夠執行到它裏面的方法了。

3、執行

如上所述,當咱們調用Dao接口方法的時候,實際調用到代理對象的invoke方法。
在這裏,實際上調用的就是SqlSession裏面的東西了。

public class DefaultSqlSession implements SqlSession {

    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            MappedStatement ms = configuration.getMappedStatement(statement);
            return executor.query(ms, 
                wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        }
    }
}
複製代碼

看到以上代碼,說明咱們想的不錯。它就是經過statement全限定類型+方法名拿到MappedStatement 對象,而後經過執行器Executor去執行具體SQL並返回。

4、總結

到這裏,再回到開頭咱們提到的問題,也許你能更好的回答。同時筆者以爲,這道題目,若是你覆蓋到如下幾個關鍵詞,面試官可能會以爲很滿意。

  • SqlSource以及動態標籤SqlNode
  • MappedStatement對象
  • Spring 工廠Bean 以及動態代理
  • SqlSession以及執行器

那麼,針對第二個問題:若是有兩個XML文件和這個Dao創建關係,豈不是衝突了?

答案也是顯而易見,無論有幾個XML和Dao創建關係,只要保證namespace+id惟一便可。

以爲不錯請點贊支持,歡迎留言或進個人我的羣855801563領取【架構資料專題目合集90期】、【BATJTMD大廠JAVA面試真題1000+】,本羣專用於學習交流技術、分享面試機會,拒絕廣告,我也會在羣內不按期答題、探討。

相關文章
相關標籤/搜索