前言
在開始動態(tài)代理的原理講解以前,我們先看一下集成mybatis以后dao層不使用動態(tài)代理以及使用動態(tài)代理的兩種實現(xiàn)方式,通過對比我們自己實現(xiàn)dao層接口以及mybatis動態(tài)代理可以更加直觀的展現(xiàn)出mybatis動態(tài)代理替我們所做的工作,有利于我們理解動態(tài)代理的過程,講解完以后我們再進行動態(tài)代理的原理解析,此講解基于mybatis的環(huán)境已經(jīng)搭建完成,并且已經(jīng)實現(xiàn)了基本的用戶類編寫以及用戶類的Dao接口的聲明,下面是Dao層的接口代碼
public interface UserDao {
/*
查詢所有用戶信息
*/
ListUser> findAll();
/**
* 保存用戶
* @param user
*/
void save(User user);
/**
* 更新用戶
* @return
*/
void update(User user);
/**
* 刪除用戶
*/
void delete(Integer userId);
/**
* 查找一個用戶
* @param userId
* @return
*/
User findOne(Integer userId);
/**
* 根據(jù)名字模糊查詢
* @param name
* @return
*/
ListUser> findByName(String name);
/**
* 根據(jù)組合對象進行模糊查詢
* @param vo
* @return
*/
ListUser> findByQueryVo(QueryVo vo);
}
一、Mybatis dao層兩種實現(xiàn)方式的對比
1.dao層不使用動態(tài)代理
dao層不使用動態(tài)代理的話,就需要我們自己實現(xiàn)dao層的接口,為了簡便起見,我只是實現(xiàn)了Dao接口中的findAll方法,以此方法為例子來展現(xiàn)我們自己實現(xiàn)Dao的方式的情況,讓我們來看代碼:
public class UserDaoImpl implements UserDao{
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory){
this.factory = factory;
}
public ListUser> findAll() {
//1.獲取sqlSession對象
SqlSession sqlSession = factory.openSession();
//2.調(diào)用selectList方法
ListUser> list = sqlSession.selectList("com.example.dao.UserDao.findAll");
//3.關(guān)閉流
sqlSession.close();
return list;
}
public void save(User user) {
}
public void update(User user) {
}
public void delete(Integer userId) {
}
public User findOne(Integer userId) {
return null;
}
public ListUser> findByName(String name) {
return null;
}
public ListUser> findByQueryVo(QueryVo vo) {
return null;
}
這里的關(guān)鍵代碼 ListUser> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),需要我們自己手動調(diào)用SqlSession里面的方法,基于動態(tài)代理的方式最后的目標也是成功的調(diào)用到這里。
注意:如果是添加,更新或者刪除操作的話需要在方法中增加事務(wù)的提交。
2.dao層使用Mybatis的動態(tài)代理
使用動態(tài)代理的話Dao層的接口聲明完成以后只需要在使用的時候通過SqlSession對象的getMapper方法獲取對應(yīng)Dao接口的代理對象,關(guān)鍵代碼如下:
//3.獲取SqlSession對象
SqlSession session = factory.openSession();
//4.獲取dao的代理對象
UserDao mapper = session.getMapper(UserDao.class);
//5.執(zhí)行查詢所有的方法
ListUser> list = mapper.findAll();
獲取到dao層的代理對象以后通過代理對象調(diào)用查詢方法就可以實現(xiàn)查詢所有用戶列表的功能。
二、Mybatis動態(tài)代理實現(xiàn)方式的原理解析
動態(tài)代理中最重要的類:SqlSession、MapperProxy、MapperMethod,下面開始從入口方法到調(diào)用結(jié)束的過程分析。
1.調(diào)用方法的開始:
//4.獲取dao的代理對象
UserDao mapper = session.getMapper(UserDao.class); 因為SqlSesseion為接口,所以我們通過Debug方式發(fā)現(xiàn)這里使用的實現(xiàn)類為DefaultSqlSession。
2.找到DeaultSqlSession中的getMapper方法,發(fā)現(xiàn)這里沒有做其他的動作,只是將工作繼續(xù)拋到了Configuration類中,Configuration為類不是接口,可以直接進入該類的getMapper方法中
@Override
public T> T getMapper(ClassT> type) {
return configuration.T>getMapper(type, this);
}
3. 找到Configuration類的getMapper方法,這里也是將工作繼續(xù)交到MapperRegistry的getMapper的方法中,所以我們繼續(xù)向下進行。
public T> T getMapper(ClassT> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
4. 找到MapperRegistry的getMapper的方法,看到這里發(fā)現(xiàn)和以前不一樣了,通過MapperProxyFactory的命名方式我們知道這里將通過這個工廠生成我們所關(guān)注的MapperProxy的代理類,然后我們通過mapperProxyFactory.newInstance(sqlSession);進入MapperProxyFactory的newInstance方法中
public T> T getMapper(ClassT> type, SqlSession sqlSession) {
final MapperProxyFactoryT> mapperProxyFactory = (MapperProxyFactoryT>) 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的newIntance方法,通過參數(shù)類型SqlSession可以得知,上面的調(diào)用先進入第二個newInstance方法中并創(chuàng)建我們所需要重點關(guān)注的MapperProxy對象,第二個方法中再調(diào)用第一個newInstance方法并將MapperProxy對象傳入進去,根據(jù)該對象創(chuàng)建代理類并返回。這里已經(jīng)得到需要的代理類了,但是我們的代理類所做的工作還得繼續(xù)向下看MapperProxy類。
protected T newInstance(MapperProxyT> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxyT> mapperProxy = new MapperProxyT>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
6. 找到MapperProxy類,發(fā)現(xiàn)其確實實現(xiàn)了JDK動態(tài)代理必須實現(xiàn)的接口InvocationHandler,所以我們重點關(guān)注invoke()方法,這里看到在invoke方法里先獲取MapperMethod類,然后調(diào)用mapperMethod.execute(),所以我們繼續(xù)查看MapperMethod類的execute方法。
public class MapperProxyT> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final ClassT> mapperInterface;
private final MapMethod, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, ClassT> mapperInterface, MapMethod, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final ConstructorMethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return ((method.getModifiers()
(Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
method.getDeclaringClass().isInterface();
}
}
7. 找到類MapperMethod類的execute方法,發(fā)現(xiàn)execute中通過調(diào)用本類中的其他方法獲取并封裝返回結(jié)果,我們來看一下MapperMethod整個類。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null method.getReturnType().isPrimitive() !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
8. MapperMethod類是整個代理機制的核心類,對SqlSession中的操作進行了封裝使用。
該類里有兩個內(nèi)部類SqlCommand和MethodSignature。 SqlCommand用來封裝CRUD操作,也就是我們在xml中配置的操作的節(jié)點。每個節(jié)點都會生成一個MappedStatement類。
MethodSignature用來封裝方法的參數(shù)以及返回類型,在execute的方法中我們發(fā)現(xiàn)在這里又回到了SqlSession中的接口調(diào)用,和我們自己實現(xiàn)UerDao接口的方式中直接用SqlSession對象調(diào)用DefaultSqlSession的實現(xiàn)類的方法是一樣的,經(jīng)過一大圈的代理又回到了原地,這就是整個動態(tài)代理的實現(xiàn)過程了。
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null method.getReturnType().isPrimitive() !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long)rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = rowCount > 0;
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
if (void.class.equals(ms.getResultMaps().get(0).getType())) {
throw new BindingException("method " + command.getName()
+ " needs either a @ResultMap annotation, a @ResultType annotation,"
+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
}
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
private E> Object executeForMany(SqlSession sqlSession, Object[] args) {
ListE> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.E>selectList(command.getName(), param);
}
// issue #510 Collections arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
private T> CursorT> executeForCursor(SqlSession sqlSession, Object[] args) {
CursorT> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.T>selectCursor(command.getName(), param, rowBounds);
} else {
result = sqlSession.T>selectCursor(command.getName(), param);
}
return result;
}
private E> Object convertToDeclaredCollection(Configuration config, ListE> list) {
Object collection = config.getObjectFactory().create(method.getReturnType());
MetaObject metaObject = config.newMetaObject(collection);
metaObject.addAll(list);
return collection;
}
@SuppressWarnings("unchecked")
private E> Object convertToArray(ListE> list) {
Class?> arrayComponentType = method.getReturnType().getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
} else {
return list.toArray((E[])array);
}
}
private K, V> MapK, V> executeForMap(SqlSession sqlSession, Object[] args) {
MapK, V> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
result = sqlSession.K, V>selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
public static class ParamMapV> extends HashMapString, V> {
private static final long serialVersionUID = -2212268410512043556L;
@Override
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
}
return super.get(key);
}
}
public static class SqlCommand {
private final String name;
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
private MappedStatement resolveMappedStatement(Class?> mapperInterface, String methodName,
Class?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
public static class MethodSignature {
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final Class?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;
public MethodSignature(Configuration configuration, Class?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class?>) {
this.returnType = (Class?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.returnsCursor = Cursor.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
public boolean hasRowBounds() {
return rowBoundsIndex != null;
}
public RowBounds extractRowBounds(Object[] args) {
return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
}
public boolean hasResultHandler() {
return resultHandlerIndex != null;
}
public ResultHandler extractResultHandler(Object[] args) {
return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
}
public String getMapKey() {
return mapKey;
}
public Class?> getReturnType() {
return returnType;
}
public boolean returnsMany() {
return returnsMany;
}
public boolean returnsMap() {
return returnsMap;
}
public boolean returnsVoid() {
return returnsVoid;
}
public boolean returnsCursor() {
return returnsCursor;
}
private Integer getUniqueParamIndex(Method method, Class?> paramType) {
Integer index = null;
final Class?>[] argTypes = method.getParameterTypes();
for (int i = 0; i argTypes.length; i++) {
if (paramType.isAssignableFrom(argTypes[i])) {
if (index == null) {
index = i;
} else {
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
}
}
}
return index;
}
private String getMapKey(Method method) {
String mapKey = null;
if (Map.class.isAssignableFrom(method.getReturnType())) {
final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
if (mapKeyAnnotation != null) {
mapKey = mapKeyAnnotation.value();
}
}
return mapKey;
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:- Idea如何去除Mapper警告方法解析
- Mybatis Mapper接口工作原理實例解析
- MyBatis Mapper接受參數(shù)的四種方式代碼解析
- mapper接口注入兩種方式詳解
- Springboot整合通用mapper過程解析
- 詳解MyBatis Mapper 代理實現(xiàn)數(shù)據(jù)庫調(diào)用原理
- 如何自動生成Mybatis的Mapper文件詳解
- 如何使用IDEA創(chuàng)建MAPPER模板過程圖解