Mybatis mapper动态代理的原理解析

前言

在开始动态代理的原理讲解以前,我们先看一下集成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);
}

一、Mybatis dao层两种实现方式的对比

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

这里的关键代码 List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),需要我们自己手动调用SqlSession里面的方法,基于动态代理的方式最后的目标也是成功的调用到这里。

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

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();

获取到dao层的代理对象以后通过代理对象调用查询方法就可以实现查询所有用户列表的功能。

二、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);
 }

3. 找到Configuration类的getMapper方法,这里也是将工作继续交到MapperRegistry的getMapper的方法中,所以我们继续向下进行。

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

4. 找到MapperRegistry的getMapper的方法,看到这里发现和以前不一样了,通过MapperProxyFactory的命名方式我们知道这里将通过这个工厂生成我们所关注的MapperProxy的代理类,然后我们通过mapperProxyFactory.newInstance(sqlSession);进入MapperProxyFactory的newInstance方法中

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

5. 找到MapperProxyFactory的newIntance方法,通过参数类型SqlSession可以得知,上面的调用先进入第二个newInstance方法中并创建我们所需要重点关注的MapperProxy对象,第二个方法中再调用第一个newInstance方法并将MapperProxy对象传入进去,根据该对象创建代理类并返回。这里已经得到需要的代理类了,但是我们的代理类所做的工作还得继续向下看MapperProxy类。

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

6. 找到MapperProxy类,发现其确实实现了JDK动态代理必须实现的接口InvocationHandler,所以我们重点关注invoke()方法,这里看到在invoke方法里先获取MapperMethod类,然后调用mapperMethod.execute(),所以我们继续查看MapperMethod类的execute方法。

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

7. 找到类MapperMethod类的execute方法,发现execute中通过调用本类中的其他方法获取并封装返回结果,我们来看一下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中的操作进行了封装使用。

该类里有两个内部类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;
  }
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • MyBatis直接执行SQL的工具SqlMapper

    可能有些人也有过类似需求,一般都会选择使用其他的方式如Spring-JDBC等方式解决. 能否通过MyBatis实现这样的功能呢? 为了让通用Mapper更彻底的支持多表操作以及更灵活的操作,在2.2.0版本增加了一个可以直接执行SQL的新类SqlMapper. 我们来了解一下SqlMapper. SqlMapper提供的方法 SqlMapper提供了以下这些公共方法: Map<String,Object> selectOne(String sql) Map<String,Object&

  • Mybatis MapperScannerConfigurer自动扫描Mapper接口生成代理注入到Spring的方法

    前言 Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring Mybatis在与Spring集成的时候可以配置 MapperFactoryBean来生成Mapper接口的代理. 例如: <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mappe

  • SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper实例详解

    一.添加所需依赖,当前完整的pom文件如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&q

  • Spring Boot整合mybatis并自动生成mapper和实体实例解析

    最近一直都在学习Java,发现目前Java招聘中,mybatis出现的频率挺高的,可能是目前Java开发中使用比较多的数据库ORM框架.于是我准备研究下Spring Boot和mybatis的整合. 1.在pom.xml文件中添加下面的配置 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-

  • Java的MyBatis框架中Mapper映射配置的使用及原理解析

    Mapper的内置方法 model层就是实体类,对应数据库的表.controller层是Servlet,主要是负责业务模块流程的控制,调用service接口的方法,在struts2就是Action.Service层主要做逻辑判断,Dao层是数据访问层,与数据库进行对接.至于Mapper是mybtis框架的映射用到,mapper映射文件在dao层用. 下面是介绍一下Mapper的内置方法: 1.countByExample ===>根据条件查询数量 int countByExample(UserE

  • 详解Mybatis通用Mapper介绍与使用

    使用Mybatis的开发者,大多数都会遇到一个问题,就是要写大量的SQL在xml文件中,除了特殊的业务逻辑SQL之外,还有大量结构类似的增删改查SQL.而且,当数据库表结构改动时,对应的所有SQL以及实体类都需要更改.这工作量和效率的影响或许就是区别增删改查程序员和真正程序员的屏障.这时,通用Mapper便应运而生-- 什么是通用Mapper 通用Mapper就是为了解决单表增删改查,基于Mybatis的插件.开发人员不需要编写SQL,不需要在DAO中增加方法,只要写好实体类,就能支持相应的增删

  • Spring Boot集成MyBatis实现通用Mapper的配置及使用

    什么是通用Mapper 通用Mapper就是为了解决单表增删改查,基于Mybatis的插件.开发人员不需要编写SQL,不需要在DAO中增加方法,只要写好实体类,就能支持相应的增删改查方法. 关于MyBatis,大部分人都很熟悉.MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Pla

  • Mybatis mapper动态代理的原理解析

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

  • Mybatis之Mapper动态代理实例解析

    一.什么是Mapper的动态代理 采用Mapper动态代理方法只需要编写相应的Mapper接口(相当于Dao接口),那么Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法. Mapper接口开发需要遵循以下规范: 1.Mapper.xml文件中的namespace与mapper接口的全类名相同. 2.Mapper接口方法名和Mapper.xml中定义的每个statement的id相同. 3.Mapper接口方法的输入参数类型和mapper.xml中定义的

  • 详解MyBatis开发Dao层的两种方式(Mapper动态代理方式)

    MyBatis开发原始Dao层请阅读我的上一篇博客:MyBatis开发Dao层的两种方式(原始Dao层开发) 接上一篇博客继续介绍MyBatis开发Dao层的第二种方式:Mapper动态代理方式 Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上一篇博客中Dao接口实现类方法. Mapper接口开发需要遵循以下规范: (1)Mapper.xml文件中的namespace与mapper接口的类路

  • Java JDK动态代理实现原理实例解析

    JDK动态代理实现原理 动态代理机制 通过实现 InvocationHandler 接口创建自己的调用处理器 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入 Interface InvocationHandler 该接口中仅定义了一个方法Object:invoke(Object obj,Method m

  • 解析Mybatis Porxy动态代理和sql解析替换问题

    JDK常用核心原理 概述 在 Mybatis 中,常用的作用就是讲数据库中的表的字段映射为对象的属性,在进入Mybatis之前,原生的 JDBC 有几个步骤:导入 JDBC 驱动包,通过 DriverManager 注册驱动,创建连接,创建 Statement,增删改查,操作结果集,关闭连接 过程详解 首先进行类的加载,通过 DriverManager 注册驱动 Class.forName("com.mysql.jdbc.Driver"); Connection connection

  • Java CGLib动态代理机制(全面解析)

    一.首先说一下JDK中的动态代理: JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的 但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高. 二.使用CGLib实现: 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高.唯一需要注意的

  • Spring中AOP概念与两种动态代理模式原理详解

    目录 1.概念 1.AOP技术简介 2.AOP的优势 3.Spring AOP术语 4.AOP 开发明确的事项 2.AOP底层实现 1.AOP 的动态代理技术: 3.基于cglib的动态代理代码 总结 1.概念 1.AOP技术简介 AOP 为Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一

  • Java实现JDK动态代理的原理详解

    目录 概念 案例 静态代理 JDK动态代理模式 原理分析 真相大白 概念 代理:为控制A对象,而创建出新B对象,由B对象代替执行A对象所有操作,称之为代理.一个代理体系建立涉及到3个参与角色:真实对象(A),代理对象(B),客户端. 其中的代理对象(B)起到中介作用,连通真实对象(A)与客户端,如果进一步拓展,代理对象可以实现更加复杂逻辑,比如对真实对象进行访问控制. 案例 需求:员工业务层接口调用save需要admin权限,调用list不需要权限,没权限调用时抛出异常提示. 静态代理 /**

  • 一文了解Java动态代理的原理及实现

    代理是指:某些场景下对象会找一个代理对象,来辅助自己完成一些工作,如明星的经纪人.买房的人找房产中介等. 代理主要是对对象的行为额外做一些辅助操作. 如何创建代理对象: Java中代理的代表类是:Java.lang.reflect.Proxy Proxy提供了一个静态方法,用于为对象产生一个代理对象返回 主类: public class Test { public static void main(String[] args) { //1.创建一个类,对象类必须实现接口 star s=new s

  • JDK动态代理过程原理及手写实现详解

    目录 JDK动态代理的过程 手写实现JDK动态代理 创建MyInvocationHandler接口 创建MyClassLoader类加载器 创建代理类 使用自定义动态代理类 创建接口 创建被代理接口 创建代理接口 客户端调用 生成源代码 JDK动态代理的过程 JDK动态代理采用字节重组,重新生成对象来替代原始对象,以达到动态代理的目的. JDK中有一个规范,在ClassPath下只要是$开头的.class文件,一般都是自动生成的. 要实现JDK动态代理生成对象,首先得弄清楚JDK动态代理的过程.

随机推荐