001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.aop;
017
018import com.jfinal.aop.Interceptor;
019import com.jfinal.kit.HashKit;
020import com.jfinal.kit.SyncWriteMap;
021
022import java.lang.reflect.Method;
023import java.util.Map;
024import java.util.Objects;
025
026public class InterceptorCache {
027
028    private static final Map<MethodKey, Interceptor[]> cache = new SyncWriteMap<>(2048, 0.25F);
029
030    public static void put(MethodKey methodKey, Interceptor[] inters) {
031        Objects.requireNonNull(methodKey, "methodKey can not be null");
032        Objects.requireNonNull(inters, "inters can not be null");
033
034        cache.putIfAbsent(methodKey, inters);
035    }
036
037    public static Interceptor[] get(MethodKey methodKey) {
038        return cache.get(methodKey);
039    }
040
041    public static MethodKey getMethodKey(Class<?> target, Method method) {
042        long paraHash = HashKit.FNV_OFFSET_BASIS_64;
043        Class<?>[] paraTypes = method.getParameterTypes();
044        for (Class<?> pt : paraTypes) {
045            paraHash ^= pt.getName().hashCode();
046            paraHash *= HashKit.FNV_PRIME_64;
047        }
048
049        return new MethodKey(target.getName().hashCode(), method.getName().hashCode(), paraHash);
050    }
051
052    public static void clear() {
053        cache.clear();
054    }
055
056
057    public static class MethodKey {
058        final int classHash;
059        final int methodHash;
060        final long paraHash;
061
062        MethodKey(int classHash, int methodHash, long paraHash) {
063            this.classHash = classHash;
064            this.methodHash = methodHash;
065            this.paraHash = paraHash;
066        }
067
068        @Override
069        public int hashCode() {
070            return classHash ^ methodHash ^ ((int) paraHash);
071        }
072
073        /**
074         * 通过比较三部分 hash 值,避免超大规模场景下可能的 key 值碰撞
075         * <p>
076         * 不必判断 if (methodKey instanceof MethodKey),因为所有 key 类型必须要相同
077         * 不必判断 if (this == methodKey),因为每次用于取值的 methodKey 都是新建的
078         */
079        @Override
080        public boolean equals(Object methodKey) {
081            MethodKey mk = (MethodKey) methodKey;
082            return mk != null && classHash == mk.classHash && methodHash == mk.methodHash && paraHash == mk.paraHash;
083        }
084
085        @Override
086        public String toString() {
087            return "classHash = " + classHash + "\nmethodHash = " + methodHash + "\nparaHash = " + paraHash;
088        }
089    }
090}