前言

在开始动态代理的原理讲解以前,我们先看一下集成mybatis以后dao层不使用动态代理以及使用动态代理的两种实现方式,通过对比我们自己实现dao层接口以及mybatis动态代理可以更加直观的展现出mybatis动态代理替我们所做的工作,有利于我们理解动态代理的过程,讲解完以后我们再进行动态代理的原理解析,此讲解基于mybatis的环境已经搭建完成,并且已经实现了基本的用户类编写以及用户类的Dao接口的声明,下面是Dao层的接口代码

public interface UserDao { /* 查询所有用户信息  */ List<User> findAll(); /**  * 保存用户  * @param user  */ void save(User user); /**  * 更新用户  * @return  */ void update(User user); /**  * 删除用户  */ void delete(Integer userId); /**  * 查找一个用户  * @param userId  * @return  */ User findOne(Integer userId); /**  * 根据名字模糊查询  * @param name  * @return  */ List<User> findByName(String name); /**  * 根据组合对象进行模糊查询  * @param vo  * @return  */ List<User> findByQueryVo(QueryVo vo);}

1.dao层不使用动态代理

dao层不使用动态代理的话,就需要我们自己实现dao层的接口,为了简便起见,我只是实现了Dao接口中的findAll方法,以此方法为例子来展现我们自己实现Dao的方式的情况,让我们来看代码:

public class UserDaoImpl implements UserDao{ private SqlSessionFactory factory; public UserDaoImpl(SqlSessionFactory factory){  this.factory = factory; } public List<User> findAll() {  //1.获取sqlSession对象  SqlSession sqlSession = factory.openSession();  //2.调用selectList方法  List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll");  //3.关闭流  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 List<User> findByName(String name) {  return null; } public List<User> findByQueryVo(QueryVo vo) {  return null; }

注意:如果是添加,更新或者删除操作的话需要在方法中增加事务的提交。

2.dao层使用Mybatis的动态代理

使用动态代理的话Dao层的接口声明完成以后只需要在使用的时候通过SqlSession对象的getMapper方法获取对应Dao接口的代理对象,关键代码如下:

//3.获取SqlSession对象SqlSession session = factory.openSession();//4.获取dao的代理对象UserDao mapper = session.getMapper(UserDao.class);//5.执行查询所有的方法List<User> list = mapper.findAll();

二、Mybatis动态代理实现方式的原理解析

动态代理中最重要的类:SqlSession、MapperProxy、MapperMethod,下面开始从入口方法到调用结束的过程分析。

1.调用方法的开始:

//4.获取dao的代理对象

UserDao mapper = session.getMapper(UserDao.class); 因为SqlSesseion为接口,所以我们通过Debug方式发现这里使用的实现类为DefaultSqlSession。

2.找到DeaultSqlSession中的getMapper方法,发现这里没有做其他的动作,只是将工作继续抛到了Configuration类中,Configuration为类不是接口,可以直接进入该类的getMapper方法中

@Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) 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); } }
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
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; } @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 Constructor<MethodHandles.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(); }}
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; }

该类里有两个内部类SqlCommand和MethodSignature。 SqlCommand用来封装CRUD操作,也就是我们在xml中配置的操作的节点。每个节点都会生成一个MappedStatement类。

MethodSignature用来封装方法的参数以及返回类型,在execute的方法中我们发现在这里又回到了SqlSession中的接口调用,和我们自己实现UerDao接口的方式中直接用SqlSession对象调用DefaultSqlSession的实现类的方法是一样的,经过一大圈的代理又回到了原地,这就是整个动态代理的实现过程了。

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) { List<E> 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> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) { Cursor<T> 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, List<E> list) { Object collection = config.getObjectFactory().create(method.getReturnType()); MetaObject metaObject = config.newMetaObject(collection); metaObject.addAll(list); return collection; } @SuppressWarnings("unchecked") private <E> Object convertToArray(List<E> 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> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) { Map<K, 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 ParamMap<V> extends HashMap<String, 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; } }

更多相关文章

  1. IM-A820L限制GSM,WCDMA上网的原理(其他泛泰机型可参考)7.13
  2. Android--SoLoader,android动态加载so库
  3. Android(安卓)TabHost使用、动态加载内容
  4. Android(安卓)实现View中添加子元素的动态效果
  5. exp: 修改Android中strings.xml文件, 动态改变数据
  6. Android动态获取当前手机IP地址
  7. Android动态添加删除recycleview并动态保存recycleview中的的数
  8. Android(安卓)GridView,Gallery动态更新数据
  9. 动态创建ImageView视图

随机推荐

  1. android ddms查看线程
  2. Android ApiDemos示例解析(167):Views->L
  3. traceview进行Android性能测试
  4. Android 屏幕实现上下翻转
  5. Android的系统服务
  6. Android - Activity - 启动模式
  7. Android读写文件二
  8. android学习——处理 EditText 右下角为D
  9. Android使用videoview播放res/raw下的视
  10. 拥抱Android:编译nginx搭建移动平台