代理模式在Jeesuite框架中的使用

上一篇解析Jeesuite框架中mybatis拦截器的使用讲到拦截器添加到了sqlSessionFactory的配置类sqlSessionFactory.getConfiguration().addInterceptor(interceptor)

那么拦截器是何时执行以及如何生效的呢?

这个实际上是添加到配置类Configuration的interceptorChain属性了

interceptorChain.addInterceptor(interceptor);

我们先来看这个拦截器接口

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  Object plugin(Object target);

  void setProperties(Properties properties);

}

关键的是这个intercept()方法,就是执行拦截逻辑的.它的的实现类里做了三件事情.

1.循环执行拦截器的处理类,调用拦截器onInterceptor()

2.执行原有方法逻辑

3.执行拦截器处理类的结束方法

@Override
public Object intercept(Invocation invocation) throws Throwable {
		
		Object result = null;
		boolean proceed = false;
		for (InterceptorHandler handler : interceptorHandlers) {
			result = handler.onInterceptor(invocation);
			if(result != null)break;
		}
		
		if(result == null){
			result = invocation.proceed();
			proceed = true;
		}
		
		for (InterceptorHandler handler : interceptorHandlers) {
			handler.onFinished(invocation,proceed ? result : null);
		}
		
		return result;
	}

我们在看看它在哪被调用的它在Plugin的invoke()方法中被调用

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
这个Plugin实现了 InvocationHandler.

当执行一个sql的时候,例如

thirdpayChannelacctMapper.selectByPrimaryKey(channelId)

debug的时候thirdpayChannelacctMapper就是MapperProxy代理类了

此时是由MapperProxy类来代理的,调用org.apache.ibatis.binding.MapperProxy#invoke方法来处理.我们看其构造方法的参数,sqlSession,mapper接口,以及方法.

MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache)

然后调用了org.apache.ibatis.binding.MapperMethod#execute(),在这里看到了我们熟悉的sqlSession.selectOne()方法

result = sqlSession.selectOne(command.getName(), param);

然后往下看,并不是直接使用,还是用代理sqlSessionProxy处理,

this.sqlSessionProxy.<T> selectOne(statement, parameter);

然后执行拦截器SqlSessionInterceptor方法

org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke

然后执行方法,返回结果.

Object result = method.invoke(sqlSession, args);

最后是Executor执行的结果.Executor分BaseExecutor和CachingExecutor.

像简单sql就是通过BaseExecutor的子类SimpleExecutor来执行,这里调用了prepareStatement()方法,获取了Statement用于后续执行sql.
stmt = prepareStatement(handler, ms.getStatementLog());
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
然后BaseStatementHandler的子类PreparedStatementHandler处理
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.<E> handleResultSets(ps);
}

早前,我们使用jdbc也是使用PreparedStatement来执行sql的

Connection connection = DriverManager.getConnection(url, username, password);
String sql = "update user set username='admin' where id = 1";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
int result = preparedStatement.executeUpdate();

上面提到了prepareStatement()方法,也是获取connection,初始化statement.

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

handler.parameterize(stmt)是用于设置sql参数,替换掉参数的占位符.DefaultParameterHandler是ParameterHandler接口的默认实现.

查询到结果后就是结果的处理

resultSetHandler.<E> handleResultSets(ps);

需要用到另一种handler是ResultSetHandler,只有一个默认实现DefaultResultSetHandler

 实现ResultSetHandler这个接口也可以自定义结果处理类.至此,流程结束.

总结一下:

MapperProxy#invoke

MapperMethod#execute()

sqlSession.<E>selectList()

SqlSessionTemplate#selectList(java.lang.String, java.lang.Object)

this.sqlSessionProxy.<E> selectList(statement, parameter)

DefaultSqlSession#selectList(java.lang.String, java.lang.Object)

CachingExecutor#query()开启缓存就会先调这个,如果缓存为空就调SimpleExecutor

SimpleExecutor#doQuery

RoutingStatementHandler#query 

PreparedStatementHandler#query

PreparedStatement.execute()

RoutingStatementHandler这里做了个路由

switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}

那怎么知道是什么类型呢?DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds) 这个方法里自动创建了

MappedStatement ms = configuration.getMappedStatement(statement);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞翔的咩咩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值