xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelInvocationHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,169 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill åº„骞 (smallchill@163.com)
 */
package org.springblade.core.cloud.sentinel;
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.MethodMetadata;
import feign.Target;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import static feign.Util.checkNotNull;
/**
 * é‡å†™ {@link com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler} é€‚配最新API
 *
 * @author Chill
 */
public class BladeSentinelInvocationHandler implements InvocationHandler {
   private final Target<?> target;
   private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
   private FallbackFactory fallbackFactory;
   private Map<Method, Method> fallbackMethodMap;
   public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
                           FallbackFactory fallbackFactory) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch");
      this.fallbackFactory = fallbackFactory;
      this.fallbackMethodMap = toFallbackMethod(dispatch);
   }
   public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch");
   }
   @Override
   public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
      if ("equals".equals(method.getName())) {
         try {
            Object otherHandler = args.length > 0 && args[0] != null
               ? Proxy.getInvocationHandler(args[0]) : null;
            return equals(otherHandler);
         } catch (IllegalArgumentException e) {
            return false;
         }
      } else if ("hashCode".equals(method.getName())) {
         return hashCode();
      } else if ("toString".equals(method.getName())) {
         return toString();
      }
      Object result;
      InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);
      // only handle by HardCodedTarget
      if (target instanceof Target.HardCodedTarget) {
         Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
         MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
            .get(hardCodedTarget.type().getName()
               + Feign.configKey(hardCodedTarget.type(), method));
         // resource default is HttpMethod:protocol://url
         if (methodMetadata == null) {
            result = methodHandler.invoke(args);
         } else {
            String resourceName = methodMetadata.template().method().toUpperCase()
               + ":" + hardCodedTarget.url() + methodMetadata.template().path();
            Entry entry = null;
            try {
               ContextUtil.enter(resourceName);
               entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
               result = methodHandler.invoke(args);
            } catch (Throwable ex) {
               // fallback handle
               if (!BlockException.isBlockException(ex)) {
                  Tracer.trace(ex);
               }
               if (fallbackFactory != null) {
                  try {
                     Object fallbackResult = fallbackMethodMap.get(method)
                        .invoke(fallbackFactory.create(ex), args);
                     return fallbackResult;
                  } catch (IllegalAccessException e) {
                     // shouldn't happen as method is public due to being an
                     // interface
                     throw new AssertionError(e);
                  } catch (InvocationTargetException e) {
                     throw new AssertionError(e.getCause());
                  }
               } else {
                  // throw exception if fallbackFactory is null
                  throw ex;
               }
            } finally {
               if (entry != null) {
                  entry.exit(1, args);
               }
               ContextUtil.exit();
            }
         }
      } else {
         // other target type using default strategy
         result = methodHandler.invoke(args);
      }
      return result;
   }
   @Override
   public boolean equals(Object obj) {
      if (obj instanceof BladeSentinelInvocationHandler) {
         BladeSentinelInvocationHandler other = (BladeSentinelInvocationHandler) obj;
         return target.equals(other.target);
      }
      return false;
   }
   @Override
   public int hashCode() {
      return target.hashCode();
   }
   @Override
   public String toString() {
      return target.toString();
   }
   static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
      Map<Method, Method> result = new LinkedHashMap<>();
      for (Method method : dispatch.keySet()) {
         method.setAccessible(true);
         result.put(method, method);
      }
      return result;
   }
}