正文前先来一波福利推荐:
福利一:
百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。
福利二:
毕业答辩以及工作上各种答辩,平时积累了不少精品PPT,现在共享给大家,大大小小加起来有几千套,总有适合你的一款,很多是网上是下载不到。
获取方式:
微信关注 精品3分钟 ,id为 jingpin3mins,关注后回复 百万年薪架构师 ,精品收藏PPT 获取云盘链接,谢谢大家支持!
-----------------------正文开始---------------------------
1、背景介绍
拦截器顾名思义为拦截某个功能的一个武器,在众多框架中均有“拦截器”。这个Plugin有什么用呢?或者说拦截器有什么用呢?可以想想拦截器是怎么实现的。Plugin用到了Java中很重要的一个特性——动态代理。所以这个Plugin可以理解为,在调用一个方法时,我“拦截”其方法做一些我想让它做的事(包括方法的前与后)。在Mybatis中可以拦截以下方法:
这正是mybatis中大名鼎鼎的四大对象;
2、源码过程跟踪,了解拦截器拦截过程以及原理
1 //ParameterHandler 处理sql的参数对象 2 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { 3 ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); 4 //包装参数插件 5 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); 6 return parameterHandler; 7 } 8 9 //ResultSetHandler 处理sql的返回结果集 10 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, 11 ResultHandler resultHandler, BoundSql boundSql) { 12 ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); 13 //包装返回结果插件 14 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); 15 return resultSetHandler; 16 } 17 18 //StatementHandler 数据库的处理对象 19 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 20 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 21 //包装数据库执行sql插件 22 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 23 return statementHandler; 24 } 25 26 public Executor newExecutor(Transaction transaction) { 27 //创建Mybatis的执行器:Executor 28 return newExecutor(transaction, defaultExecutorType); 29 } 30 31 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 32 executorType = executorType == null ? defaultExecutorType : executorType; 33 executorType = executorType == null ? ExecutorType.SIMPLE : executorType; 34 Executor executor; 35 //mybatis支持的三种执行器:batch、reuse、simple,其中默认支持的是simple 36 if (ExecutorType.BATCH == executorType) { 37 executor = new BatchExecutor(this, transaction); 38 } else if (ExecutorType.REUSE == executorType) { 39 executor = new ReuseExecutor(this, transaction); 40 } else {
//默认为SimpleExecutor 41 executor = new SimpleExecutor(this, transaction); 42 }
//如果开启二级缓存 则对executor进行缓存包装 43 if (cacheEnabled) { 44 executor = new CachingExecutor(executor); 45 } 46 //包装执行器插件 47 executor = (Executor) interceptorChain.pluginAll(executor); 48 return executor; 49 }
我们可以看到这四大对象 在创建的过程中 都调用了 (Executor) interceptorChain.pluginAll(Object target) 代码,该代码是怎么样的呢 正是我们所说的拦截器链,将四大对象传入到拦截器链进行处理 然后返回包装后的 四大对象 如果我们在拦截器链中进行拦截处理 则实现了拦截技术;
下面我们看连接器链中的内容:
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
//在pluginAll方法中 遍历拦截器集 将传入的target 也就是四大对象进行传入 在interceptor中的Plugin方法中处理 public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } }
下面我们看一下interceptor对象的源代码:
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties); }
从源代码看出 interceptor是一个接口 接口中 有三个方法 分别是 intercept plugin 和 setProperties;下面分别介绍着几个方法;
我们自己写插件或者拦截四大对象后 进行相应功能的添加就在要实现该接口,然后实现接口的三个方法;
举例实现该接口:
注意: 记住必须使用 注解的方式实现声明拦截器拦截哪个类对象 原因在后面源码中进行分析
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) public class ExamplePlugin implements Interceptor { /* 此方法用于实现拦截逻辑 * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation) */ @Override
public Object intercept(Invocation invocation) throws Throwable
{
doSomeThing();
/* 注:此处实际上使用Invocation.proceed()方法完成interceptorChain链的遍历调用(即执行所有注册的Interceptor的intercept方法),到最终被代理对象的原始方法调用 */
return invocation.proceed();
}
/* 使用当前的这个拦截器实现对目标对象的代理(内部实现时Java的动态代理) * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object) */
@Override
public Object plugin(Object target)
{
/* 当目标类是StatementHandler类型时,才包装目标类,不做无意义的代理 */
return (target instanceof StatementHandler)?Plugin.wrap(target, this):target;
}
/* 初始化Configuration时通过配置文件配置property传递参数给此方法并调用。 * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties) */ @Override public void setProperties(Properties properties) { Iterator iterator = properties.keySet().iterator(); while (iterator.hasNext()){ String keyValue = String.valueOf(iterator.next()); System.out.println(properties.getProperty(keyValue)); } } }
我们看点在plugin方法中 通过Plugin.wrap创建了代理对象 我们来看源代码:看到了Plugin 类实现了 InvocationHandler 是不是感觉很熟悉 这就是我们上篇文章中讲过的 动态代理中的 invocationHandel类
前面说了为什么自己实现拦截器类时 为什么必须使用注解的方式 因为在获得数字签名Map的方法中 存在使用反射获得注解信息的方法
//获得Interceptor注解,@Signature中的type(要拦截的类),method(拦截类的方法)和args(拦截器用于这些类中)
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
所以如果没有添加注解方式 则会抛出 throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName())
然后我们看一下wrap方法 该方法就是通过数据签名 然后看数字签名中的是否包含要拦截对象和方法 如果包含 则创建代理对象 返回代理对象
public class Plugin implements InvocationHandler { private Object target; //目标对象 private Interceptor interceptor;//拦截器对象 private Map<Class<?>, Set<Method>> signatureMap;//目标对象方法签名 private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; //此处的target就是被传入的被代理对象 this.interceptor = interceptor; this.signatureMap = signatureMap; } public static Object wrap(Object target, Interceptor interceptor) { //从拦截器的注解中获取拦截的类名和方法信息 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); //解析被拦截对象的所有接口 Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { //生成代理对象,Plugin对象为该代理对象的InvocationHandler return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } @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)) { //调用代理类所属拦截器的intercept方法 return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) { //获得Interceptor注解,@Signature中的type(要拦截的类),method(拦截类的方法)和args(拦截器用于这些类中) Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); // issue #251 if (interceptsAnnotation == null) { throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); } //获得注解type,method 和args生成一个signature数组 Signature[] sigs = interceptsAnnotation.value(); Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>(); for (Signature sig : sigs) { Set<Method> methods = signatureMap.get(sig.type()); if (methods == null) { methods = new HashSet<Method>(); signatureMap.put(sig.type(), methods); } try { //获得类的方法 Method method = sig.type().getMethod(sig.method(), sig.args()); methods.add(method); } catch (NoSuchMethodException e) { throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e); } } return signatureMap; } //获得所有接口 private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { Set<Class<?>> interfaces = new HashSet<Class<?>>(); while (type != null) { //获得接口 for (Class<?> c : type.getInterfaces()) { if (signatureMap.containsKey(c)) { interfaces.add(c); } } //获得父类 type = type.getSuperclass(); } //返回一个接口的数组 return interfaces.toArray(new Class<?>[interfaces.size()]); } }
到这里 代理对象以及中间对象invocationHandler对象已经创建完成 现在我们看一下 被代理对象 被代理对象就是 statementHandler 该接口的子类 这类的是
SimpleStatementHandler
由此可以看到该类实现了 statementHandler接口的方法。
通过看这里的源码也可以看出 其实mysql的底层其实也是使用了 底层的jdbc来实现的!
public abstract class BaseStatementHandler implements StatementHandler { protected final Configuration configuration; protected final ObjectFactory objectFactory; protected final TypeHandlerRegistry typeHandlerRegistry; protected final ResultSetHandler resultSetHandler; protected final ParameterHandler parameterHandler; protected final Executor executor; protected final MappedStatement mappedStatement; protected final RowBounds rowBounds; protected BoundSql boundSql; protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { this.configuration = mappedStatement.getConfiguration(); this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); } //获取BoundSql public BoundSql getBoundSql() { return boundSql; } //获取ParameterHandler public ParameterHandler getParameterHandler() { return parameterHandler; } //准备语句 public Statement prepare(Connection connection) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { //实例化Statement statement = instantiateStatement(connection); //设置超时 setStatementTimeout(statement); //设置读取条数 setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } } //如何实例化Statement,交给子类去做 protected abstract Statement instantiateStatement(Connection connection) throws SQLException; //设置超时,实际就是调用Statement.setQueryTimeout protected void setStatementTimeout(Statement stmt) throws SQLException { Integer timeout = mappedStatement.getTimeout(); Integer defaultTimeout = configuration.getDefaultStatementTimeout(); if (timeout != null) { stmt.setQueryTimeout(timeout); } else if (defaultTimeout != null) { stmt.setQueryTimeout(defaultTimeout); } } //设置读取条数,其实就是调用Statement.setFetchSize protected void setFetchSize(Statement stmt) throws SQLException { Integer fetchSize = mappedStatement.getFetchSize(); if (fetchSize != null) { stmt.setFetchSize(fetchSize); } } //关闭Statement protected void closeStatement(Statement statement) { try { if (statement != null) { statement.close(); } } catch (SQLException e) { //ignore } } protected void generateKeys(Object parameter) { KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); ErrorContext.instance().store(); keyGenerator.processBefore(executor, mappedStatement, null, parameter); ErrorContext.instance().recall(); } }
public class SimpleStatementHandler extends BaseStatementHandler { public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); } @Override public int update(Statement statement) throws SQLException { String sql = boundSql.getSql(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); int rows; if (keyGenerator instanceof Jdbc3KeyGenerator) { statement.execute(sql, Statement.RETURN_GENERATED_KEYS); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else if (keyGenerator instanceof SelectKeyGenerator) { statement.execute(sql); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else { statement.execute(sql); rows = statement.getUpdateCount(); } return rows; } @Override public void batch(Statement statement) throws SQLException { String sql = boundSql.getSql(); statement.addBatch(sql); } @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { String sql = boundSql.getSql(); statement.execute(sql); return resultSetHandler.<E>handleResultSets(statement); } @Override protected Statement instantiateStatement(Connection connection) throws SQLException { if (mappedStatement.getResultSetType() != null) { return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.createStatement(); } } @Override public void parameterize(Statement statement) throws SQLException { // N/A } }