1 1)配置文件注册插件 2 <plugins> 3 <plugin interceptor="com.lagou.interceptor.ExamplePlugin"> 4 <property name="name" value="dajgh"/> 5 </plugin> 6 </plugins> 7 2)插件实现 8 @Intercepts({@Signature(type = StatementHandler.class, 9 method = "prepare", args = {Connection.class,Integer.class})}) 10 public class ExamplePlugin implements Interceptor { 11 @Override 12 public Object intercept(Invocation invocation) throws Throwable {//拦截器方法, 13 System.out.println("增强行为"); 14 return invocation.proceed(); 15 } 16 @Override 17 public Object plugin(Object target) {//惯例的写法,将target封装后继续返回 18 System.out.println("进行了增强初始化..."); 19 return Plugin.wrap(target,this); 20 } 21 @Override 22 public void setProperties(Properties properties) {//扩展属性配置,xml中注册时可以增加属性配置 23 System.out.println("properties = " + properties); 24 } 25 }
三.mybatis的加载和调用
1 //xml文件配置略,注册插件见实践部分, 2 3 //加载和调用 4 String file = "sqlMapConfig.xml"; 5 InputStream inputStream = Resources.getResourceAsStream(file); 6 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); 7 sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); 8 SqlSession sqlSession = sqlSessionFactory.openSession(); 9 //得到mapper,进行调用 10 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 11 List<User> list = user1Mapper.selectAll(); 12 sqlSession.close();
四.源码剖析
1)分析加载过程
sqlSessionFactoryBuilder.build中会解析xml配置,得到configuration对象,
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse());//解析xml,得到configuration } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
2)实际调用
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration"));//解析配置文件下configuration标签 return configuration; }
3)进入parseConfiguration()
private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins"));//解析插件并进行注册 objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
4)插件解析过程
/** <plugins> <plugin interceptor="com.lagou.interceptor.ExamplePlugin"> <property name="name" value="dajgh"/> </plugin> </plugins> */ private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor");//读取插件类信息 Properties properties = child.getChildrenAsProperties();//加载插件属性, Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();//实例化拦截器, interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } } }
5)观察插件注册configuration.addInterceptor();
public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); }
6)观察InterceptorChain,
维护了一个拦截器数组,通过addInterceptor()进行添加, 提供一个pluginAll(),支持对入参obj进行链式调用,public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<>(); 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); } }7)观察Interceptor接口
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable;//在这里完成增强功能, default Object plugin(Object target) {//这个方法,一般实现类保持不变即可,逻辑基本一致, return Plugin.wrap(target, this); } default void setProperties(Properties properties) { } }8)观察Plugin.wrap(target,interceptor) 如果target实现的接口和interceptor注解的接口一致,则得到target的代理对象,否则不做处理, target的代理对象的代理逻辑是,如果调用target的方法,且方法在拦截器中配置了,则执行拦截器interceptor.intercept,
public class Plugin implements InvocationHandler { private final Object target; private final Interceptor interceptor; private final Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.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);//得到type所有实现的与signatureMap中一致的接口, if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));//一致即走代理,不一致就原样返回target,一致就new一个本对象, 实质是一次动态代理,代理targt对象, } return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//代理核心方法,如果被调用方法method,在signatureMap中已注册,则调用拦截器进行增强,否则直接执行方法, 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); } } private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {//读取拦截器注解中的信息,标注针对xx接口,yy方法,进行增强 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()); } Signature[] sigs = interceptsAnnotation.value(); Map<Class<?>, Set<Method>> signatureMap = new HashMap<>(); for (Signature sig : sigs) { Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>()); 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) {//遍历type及其父类,得到实现的与拦截器注解一致的所有接口, Set<Class<?>> interfaces = new HashSet<>(); 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()]); } }9)观察自定义插件实现intercept() 进行增强处理,然后调用入参invocation, 注意
@Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("增强行为"); return invocation.proceed(); }10)观察Invocation.proceed(),可知内部就是正常调用反射
public Object proceed() throws InvocationTargetException, IllegalAccessException { return method.invoke(target, args); }11)补充查看拦截器链被调用入口 在configuration中总工有四个类似方法,针对四种接口调用链式拦截器链进行代理封装,逻辑基本一致,这里举一例, 对StatementHandler进行链式代理, 而以下方法在对应组件被使用时会被调用,得到链式代理后的对象完成业务操作,
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
五.总结
结合8,9,10,可知,拦截器中实现增强的具体逻辑, 然后invocation.proceed()完成正常的反射调用原方法; 综上拦截器的解析过程可知, 解析拦截器的注解信息,得到拦截器拦截的接口的指定方法, 然后将拦截器添加到拦截器链中, 拦截器链支持对Object进行链式代理,通过InterceptorChain.pluginAll(tatrget)方法, 对target进行层层链式代理,拦截器提供两个方法,一个plugin()由拦截器链调用,进行链式代理,一个intercept()进行业务调用和增强逻辑, 链式代理过程中,如果target匹配拦截器注解类注解指定的接口,则进行代理封装,返回代理对象继续进行代理封装,否则直接返回target原对象---至此完成target的拦截器链式代理封装处理 target就这样被层层代理, 如果target方法被调用,若符合拦截器中定义的方法,则调用拦截器定义的增强方法,