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類。