Plugin.java

  1. /*
  2.  *    Copyright 2009-2023 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.plugin;

  17. import java.lang.reflect.InvocationHandler;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Proxy;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Map;
  23. import java.util.Set;

  24. import org.apache.ibatis.reflection.ExceptionUtil;
  25. import org.apache.ibatis.util.MapUtil;

  26. /**
  27.  * @author Clinton Begin
  28.  */
  29. public class Plugin implements InvocationHandler {

  30.   private final Object target;
  31.   private final Interceptor interceptor;
  32.   private final Map<Class<?>, Set<Method>> signatureMap;

  33.   private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
  34.     this.target = target;
  35.     this.interceptor = interceptor;
  36.     this.signatureMap = signatureMap;
  37.   }

  38.   public static Object wrap(Object target, Interceptor interceptor) {
  39.     Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  40.     Class<?> type = target.getClass();
  41.     Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  42.     if (interfaces.length > 0) {
  43.       return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
  44.     }
  45.     return target;
  46.   }

  47.   @Override
  48.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  49.     try {
  50.       Set<Method> methods = signatureMap.get(method.getDeclaringClass());
  51.       if (methods != null && methods.contains(method)) {
  52.         return interceptor.intercept(new Invocation(target, method, args));
  53.       }
  54.       return method.invoke(target, args);
  55.     } catch (Exception e) {
  56.       throw ExceptionUtil.unwrapThrowable(e);
  57.     }
  58.   }

  59.   private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
  60.     Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
  61.     // issue #251
  62.     if (interceptsAnnotation == null) {
  63.       throw new PluginException(
  64.           "No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
  65.     }
  66.     Signature[] sigs = interceptsAnnotation.value();
  67.     Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
  68.     for (Signature sig : sigs) {
  69.       Set<Method> methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());
  70.       try {
  71.         Method method = sig.type().getMethod(sig.method(), sig.args());
  72.         methods.add(method);
  73.       } catch (NoSuchMethodException e) {
  74.         throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e,
  75.             e);
  76.       }
  77.     }
  78.     return signatureMap;
  79.   }

  80.   private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
  81.     Set<Class<?>> interfaces = new HashSet<>();
  82.     while (type != null) {
  83.       for (Class<?> c : type.getInterfaces()) {
  84.         if (signatureMap.containsKey(c)) {
  85.           interfaces.add(c);
  86.         }
  87.       }
  88.       type = type.getSuperclass();
  89.     }
  90.     return interfaces.toArray(new Class<?>[0]);
  91.   }

  92. }