1. 程式人生 > >Spring中的FactoryBean及Mybatis中Mapper生成原理解析

Spring中的FactoryBean及Mybatis中Mapper生成原理解析

一、Spring中的FactoryBean

首先,讓我們先來看一看FactoryBean介面的定義,由此可見,FactoryBean也是用來建立bean的,它所建立的bean即為它後面所跟泛型對應的型別的例項。

package org.springframework.beans.factory;

public abstract interface FactoryBean<T> {
    public abstract T getObject() throws Exception;

    public abstract Class<?> getObjectType
(); public abstract boolean isSingleton(); }

其中,getObject()方法返回建立的物件,getObjectType()方法返回建立的物件的型別,isSingleton()方法表示此物件是否為單例。

舉個例子來看一下:
(1)User.java
先定義一個物件,此物件即為我們即將要實現的FactoryBean要建立的物件。

package com.alan.spring.extensionpoints;

public class User {
    private String name;
    private Integer age;

    public
String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return
"name: "+name+", age: "+age; } }

(2)UserFactoryBean.java
建立上述物件的FactoryBean,實現了FactoryBean、InitializingBean、DisposableBean介面,其中InitializingBean介面主要是為了在UserFactoryBean的初始化方法afterPropertiesSet()中建立User的例項,DisposableBean介面主要與InitializingBean介面配對,並列印銷燬日誌。

package com.alan.spring.extensionpoints;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

public class UserFactoryBean implements FactoryBean<User>, InitializingBean, DisposableBean{

    private User user;

    @Override
    public void destroy() throws Exception {
        System.err.println("user factory bean destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        user = new User();
        user.setName("tangtong");
        user.setAge(25);
    }

    @Override
    public User getObject() throws Exception {
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return user==null?User.class:user.getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public String toString() {
        return "this is a user factory bean!";
    }
}

(3)dispatcher.xml
在spring配置檔案中配置這個bean,注意,我們配置的是UserFactoryBean。

<bean id="user" class="com.alan.spring.extensionpoints.UserFactoryBean"></bean>

(4)UnitTest.java
從ApplicationContext中取出user,看它到底是什麼。

package com.alan.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath*:META-INF/dispatcher.xml")
public class UnitTest {
    @Resource
    private ApplicationContext ctx;

    @Test
    public void test() {
        System.err.println("the user factory bean create the user : " + ctx.getBean("user"));  //取出的是User的例項
        System.err.println("the user factory bean is : " + ctx.getBean("&user")); //取出的是UserFactoryBean的例項

    }
}

(5)檢視輸出
如果不出意外,輸出結果應該類似下面這樣:

the user factory bean create the user : name: tangtong, age: 25
the user factory bean is : this is a user factory bean!
user factory bean destroy

從上面的結果可以看到,從ApplicationContext中取出的”user”是User的例項,取出的”&user”卻是UserFactoryBean的例項。

二、Mybatis中的Mapper生成原理解析

在Mybatis中如果配置單個Mapper,我們一般使用如下這種形式:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper">
  <property name="sqlSessionFactory" ref="sqlSessionFactory">
</property></property></bean>

可見,這裡的userMapper配置的是MapperFactoryBean類的例項,但是我們實際使用的過程中明明就是這麼宣告的啊:

@Autowired
private UserMapper userMapper;

按照配置,它是怎麼變成UserMapper的例項的呢?學習了上面關於Spring中的FactoryBean,你可能已經明白了其中一二,下面讓我們來看看具體是怎麼實現的。

(1)MapperFactoryBean.java
進入MapperFactoryBean原始碼。

package org.mybatis.spring.mapper;

import org.apache.commons.logging.Log;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;

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

    protected void checkDaoConfig() {
        super.checkDaoConfig();

        Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");

        Configuration configuration = getSqlSession().getConfiguration();
        if ((this.addToConfig) && (!configuration.hasMapper(this.mapperInterface))) {
            try {
                configuration.addMapper(this.mapperInterface);
            } catch (Exception e) {
                this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
                throw new IllegalArgumentException(e);
            } finally {
                ErrorContext.instance().reset();
            }
        }
    }

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

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

    public boolean isSingleton() {
        return true;
    }

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

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

    public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
    }

    public boolean isAddToConfig() {
        return this.addToConfig;
    }
}

可見,MapperFactoryBean有一個屬性叫mapperInterface,即上面的配置中傳入的UserMapper介面。然後,找到它的getObject()方法,此方法返回的即是配置中userMapper對應的真實的例項,根據getObject()方法一層層進入。

(2)DefaultSqlSession.xml

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

(3)Configuration.java

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

(4)MapperRegistry.java

    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.");
        }
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

(5)MapperProxyFactory.java

    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 newInstance(mapperProxy);
    }

到此為止,可見在newInstance(mapperProxy)方法中,最後呼叫Java原生的代理類Proxy建立了一個mapperInterface介面(即我們所傳的UserMapper介面)的例項,所以,我們在程式碼中可以直接注入UserMapper的例項。

最後,我們可以在單元測試中打印出userMapper的路徑:

System.err.println(userInfoMapper.getClass().getName());

得到的結果類似下面這樣:

com.sun.proxy.$Proxy28

其中,$Proxy28表示這是一個動態生成的代理類,它繼承自java.lang.reflect.Proxy類。