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.test; 017 018import com.jfinal.kit.LogKit; 019import io.jboot.aop.InterceptorCache; 020import io.jboot.aop.cglib.JbootCglibCallback; 021import io.jboot.aop.javassist.JbootJavassistHandler; 022import io.jboot.service.JbootServiceBase; 023import io.jboot.utils.ClassUtil; 024import javassist.util.proxy.MethodHandler; 025import net.sf.cglib.proxy.MethodProxy; 026 027import java.lang.reflect.Method; 028import java.lang.reflect.Modifier; 029import java.util.ArrayList; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033 034class MockMethodInterceptor extends JbootCglibCallback implements MethodHandler { 035 036 private static final Map<InterceptorCache.MethodKey, MockMethodInfo> METHOD_INFO_CACHE = new HashMap<>(); 037 038 public static void addMethodInfo(MockMethodInfo value) { 039 InterceptorCache.MethodKey methodKey = InterceptorCache.getMethodKey(value.getTargetClass(), value.getTargetMethod()); 040 METHOD_INFO_CACHE.put(methodKey, value); 041 } 042 043 public static void addMockClass(Class<?> mockClass) { 044 Class<?>[] interfaces = mockClass.getInterfaces(); 045 List<MockMethodInfo> mockMethodInfos = new ArrayList<>(); 046 for (Class<?> inter : interfaces) { 047 for (MockMethodInfo mockMethodInfo : METHOD_INFO_CACHE.values()) { 048 //相同的代理对象 049 if (mockMethodInfo.getTargetClass() == inter) { 050 mockMethodInfos.add(new MockMethodInfo(mockMethodInfo, mockClass)); 051 } 052 } 053 } 054 mockMethodInfos.forEach(MockMethodInterceptor::addMethodInfo); 055 } 056 057 058 private boolean autoMockInterface; 059 060 public MockMethodInterceptor(boolean autoMockInterface) { 061 this.autoMockInterface = autoMockInterface; 062 } 063 064 @Override 065 public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 066 067 Class<?> targetClass = ClassUtil.getUsefulClass(target.getClass()); 068 069 //对于接口而且没有实现类的情况,target 是一个 Object 类 070 if (targetClass == Object.class && method.getDeclaringClass() != Object.class) { 071 targetClass = method.getDeclaringClass(); 072 } 073 074 InterceptorCache.MethodKey methodKey = InterceptorCache.getMethodKey(targetClass, method); 075 076 if (METHOD_INFO_CACHE.containsKey(methodKey)) { 077 MockMethodInfo methodInfo = METHOD_INFO_CACHE.get(methodKey); 078 return methodInfo.invokeMock(target, args); 079 } 080 081 if (autoMockInterface && Modifier.isInterface(targetClass.getModifiers())) { 082 if (!("toString".equals(method.getName()) && args.length == 0)) { 083 LogKit.warn("Return null for Mock Method: \"" + ClassUtil.buildMethodString(method) + "\", " + 084 "Because the class \"" + targetClass.getName() + "\" is an interface and has no any implementation classes."); 085 } 086 return null; 087 } 088 089 try { 090 return super.intercept(target, method, args, methodProxy); 091 } catch (Exception ex) { 092 if ("initDao".equals(method.getName()) && JbootServiceBase.class == method.getDeclaringClass()) { 093 return null; 094 } else { 095 throw ex; 096 } 097 } 098 099 } 100 101 102 private static final JbootJavassistHandler orginalHandler = new JbootJavassistHandler(); 103 104 @Override 105 public Object invoke(Object target, Method thisMethod, Method method, Object[] args) throws Throwable { 106 Class<?> targetClass = ClassUtil.getUsefulClass(target.getClass()); 107 108 //对于接口而且没有实现类的情况,target 是一个 Object 类 109 if (targetClass == Object.class && method.getDeclaringClass() != Object.class) { 110 targetClass = method.getDeclaringClass(); 111 } 112 113 InterceptorCache.MethodKey methodKey = InterceptorCache.getMethodKey(targetClass, method); 114 115 if (METHOD_INFO_CACHE.containsKey(methodKey)) { 116 MockMethodInfo methodInfo = METHOD_INFO_CACHE.get(methodKey); 117 return methodInfo.invokeMock(target, args); 118 } 119 120 if (autoMockInterface && Modifier.isInterface(targetClass.getModifiers())) { 121 if (!("toString".equals(method.getName()) && args.length == 0)) { 122 LogKit.warn("Return null for Mock Method: \"" + ClassUtil.buildMethodString(method) + "\", " + 123 "Because the class \"" + targetClass.getName() + "\" is an interface and has no any implementation classes."); 124 } 125 return null; 126 } 127 128 try { 129 return orginalHandler.invoke(target, thisMethod, method, args); 130 } catch (Exception ex) { 131 if ("initDao".equals(method.getName()) && JbootServiceBase.class == method.getDeclaringClass()) { 132 return null; 133 } else { 134 throw ex; 135 } 136 } 137 } 138}