diff --git a/fox-mock-agent/pom.xml b/fox-mock-agent/pom.xml index 4fde946..a33da51 100644 --- a/fox-mock-agent/pom.xml +++ b/fox-mock-agent/pom.xml @@ -7,7 +7,7 @@ com.cxytiandi 1.0-SNAPSHOT - 6.0 + 7.0 4.0.0 fox-mock-agent @@ -69,7 +69,14 @@ dubbo provided true - 2.7.3 + + + + + io.github.openfeign + feign-core + provided + true diff --git a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/FoxMockAgent.java b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/FoxMockAgent.java index 08ea746..77e1103 100644 --- a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/FoxMockAgent.java +++ b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/FoxMockAgent.java @@ -129,6 +129,5 @@ private static void preLoadClass() { } catch (ClassNotFoundException e) { LOG.warn("{} ClassNotFoundException", FoxMockConstant.IBATIS_CACHING_EXECUTOR); } - } } diff --git a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/constant/FoxMockConstant.java b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/constant/FoxMockConstant.java index d32b0ad..90a8533 100644 --- a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/constant/FoxMockConstant.java +++ b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/constant/FoxMockConstant.java @@ -13,6 +13,7 @@ public class FoxMockConstant { public static final String IBATIS_BASE_EXECUTOR = "org.apache.ibatis.executor.BaseExecutor"; public static final String IBATIS_CACHING_EXECUTOR = "org.apache.ibatis.executor.CachingExecutor"; public static final String DUBBO_CONSUMER_FILTER = "org.apache.dubbo.rpc.filter.ConsumerContextFilter"; + public static final String FEIGN_METHOD_HANDLER = "feign.SynchronousMethodHandler"; public static final String IBATIS_MOCK_QUERY_METHOD = "query"; public static final String IBATIS_MOCK_UPDATE_METHOD = "update"; diff --git a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/storage/StorageHelper.java b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/storage/StorageHelper.java index 6b9d16d..9f85f36 100644 --- a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/storage/StorageHelper.java +++ b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/storage/StorageHelper.java @@ -71,6 +71,7 @@ public static Set getMockClassNames() { allMockClassNames.add(FoxMockConstant.IBATIS_BASE_EXECUTOR); allMockClassNames.add(FoxMockConstant.IBATIS_CACHING_EXECUTOR); allMockClassNames.add(FoxMockConstant.DUBBO_CONSUMER_FILTER); + allMockClassNames.add(FoxMockConstant.FEIGN_METHOD_HANDLER); return allMockClassNames; } } diff --git a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MethodInvokeFilter.java b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MethodInvokeFilter.java index 56f4ca0..c99eef6 100644 --- a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MethodInvokeFilter.java +++ b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MethodInvokeFilter.java @@ -37,7 +37,7 @@ public class MethodInvokeFilter { private static final Logger LOG = LoggerFactory.getLogger(MethodInvokeFilter.class); public static boolean filter(Object[] args, String express) { - if (args.length == 0 || StringUtils.isBlank(express)) { + if (args == null || args.length == 0 || StringUtils.isBlank(express)) { return true; } diff --git a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MockClassFileTransformer.java b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MockClassFileTransformer.java index bf480e0..24226ca 100644 --- a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MockClassFileTransformer.java +++ b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/transformer/MockClassFileTransformer.java @@ -107,6 +107,17 @@ public byte[] transform(ClassLoader loader, String className, Class classBein } } + if (FoxMockConstant.FEIGN_METHOD_HANDLER.equals(classInfo.getClassName())) { + for (CtMethod method : declaredMethods) { + String methodName = method.getName(); + if ("invoke".equals(methodName)) { + LOG.info("mock feign FeignInvocationHandler invoke method"); + method.insertBefore("Object data = feign.FeignInvokeFilter.invoke($args,this);if(java.util.Objects.nonNull(data)){return ($r)data;}"); + } + } + } + + // 缓存没有增强之前的类的信息 if (match && !CLASS_INFO.containsKey(className)) { CLASS_INFO.put(className, new ClassInfo(className, classfileBuffer, loader)); diff --git a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/utils/ReflectionUtils.java b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/utils/ReflectionUtils.java index d641eff..26056e1 100644 --- a/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/utils/ReflectionUtils.java +++ b/fox-mock-agent/src/main/java/com/cxytiandi/foxmock/agent/utils/ReflectionUtils.java @@ -1,5 +1,6 @@ package com.cxytiandi.foxmock.agent.utils; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Objects; @@ -32,4 +33,10 @@ public static Type getGenericReturnType(String className, String methodName) { return genericReturnType; } + + public static Object getFieldValue(String fieldName, Class clz, Object target) throws Throwable { + Field field = clz.getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(target); + } } diff --git a/fox-mock-agent/src/main/java/feign/FeignInvokeFilter.java b/fox-mock-agent/src/main/java/feign/FeignInvokeFilter.java new file mode 100644 index 0000000..f8f392a --- /dev/null +++ b/fox-mock-agent/src/main/java/feign/FeignInvokeFilter.java @@ -0,0 +1,54 @@ +package feign; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.cxytiandi.foxmock.agent.factory.MockInfoFactory; +import com.cxytiandi.foxmock.agent.model.MockInfo; +import com.cxytiandi.foxmock.agent.storage.StorageHelper; +import com.cxytiandi.foxmock.agent.transformer.MethodInvokeFilter; +import com.cxytiandi.foxmock.agent.utils.JsonUtils; +import com.cxytiandi.foxmock.agent.utils.ReflectionUtils; +import java.lang.reflect.Type; +import java.util.Objects; + +/** + * @作者 尹吉欢 + * @个人微信 jihuan900 + * @微信公众号 猿天地 + * @GitHub https://github.com/yinjihuan + * @作者介绍 http://cxytiandi.com/about + * @时间 2022-05-24 23:07 + */ +public class FeignInvokeFilter { + + private static final Logger LOG = LoggerFactory.getLogger(FeignInvokeFilter.class); + + public static Object invoke(Object[] argsArray, Object methodHandler) throws Throwable { + Object[] args = (Object[]) argsArray[0]; + SynchronousMethodHandler handler = (SynchronousMethodHandler) methodHandler; + Class handlerClass = handler.getClass(); + + MethodMetadata methodMetadata = (MethodMetadata) ReflectionUtils.getFieldValue("metadata", handlerClass, handler); + Target target = (Target) ReflectionUtils.getFieldValue("target", handlerClass, handler);; + + String className = target.type().getName(); + String ms = methodMetadata.configKey().split("#")[1]; + String methodName = ms.substring(0, ms.indexOf("(")); + + String key = String.format("%s#%s", className, methodName); + String data = StorageHelper.get(key); + + if (Objects.nonNull(data)) { + MockInfo mockInfo = MockInfoFactory.create(data); + boolean filter = MethodInvokeFilter.filter(args, mockInfo.getOgnlExpress()); + if (filter) { + LOG.info(String.format("mock methods %s, mock data is %s", key, data)); + Type genericReturnType = methodMetadata.returnType(); + Object value = JsonUtils.parseByType(mockInfo.getMockData(), genericReturnType); + return value; + } + } + + return null; + } +} diff --git a/fox-mock-boot/src/main/java/com/cxytiandi/foxmock/boot/Bootstrap.java b/fox-mock-boot/src/main/java/com/cxytiandi/foxmock/boot/Bootstrap.java index 020bb6c..fd0b85b 100644 --- a/fox-mock-boot/src/main/java/com/cxytiandi/foxmock/boot/Bootstrap.java +++ b/fox-mock-boot/src/main/java/com/cxytiandi/foxmock/boot/Bootstrap.java @@ -31,7 +31,7 @@ public static void main(String[] args) { String mockDataHttpUrl = config.getProperty("mockDataHttpUrl", ""); if (Objects.isNull(foxMockAgentJarPath)) { - foxMockAgentJarPath = PathUtils.getAgentPath() + File.separator + "fox-mock-agent-6.0.jar"; + foxMockAgentJarPath = PathUtils.getAgentPath() + File.separator + "fox-mock-agent-7.0.jar"; } VirtualMachine attach = VirtualMachine.attach(String.valueOf(getPid())); diff --git a/fox-mock-example/pom.xml b/fox-mock-example/pom.xml index c9493d1..64e2c50 100644 --- a/fox-mock-example/pom.xml +++ b/fox-mock-example/pom.xml @@ -20,6 +20,13 @@ pom import + + org.springframework.cloud + spring-cloud-dependencies + Greenwich.SR2 + pom + import + @@ -68,5 +75,16 @@ nacos-client 1.1.4 + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + 2.1.0.RELEASE + \ No newline at end of file diff --git a/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/FoxMockApp.java b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/FoxMockApp.java index a9a6173..2a61fce 100644 --- a/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/FoxMockApp.java +++ b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/FoxMockApp.java @@ -1,11 +1,13 @@ package com.cxytiandi.foxmock.example; import com.cxytiandi.foxmock.example.dubbo.DubboApiTestService; +import com.cxytiandi.foxmock.example.feign.FeignApiTestService; import com.cxytiandi.foxmock.example.mybatis.UserMapper; import com.cxytiandi.foxmock.example.mybatis.UserQuery; import com.google.gson.Gson; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Map; @@ -19,6 +21,7 @@ * @作者介绍 http://cxytiandi.com/about * @时间 2022-04-18 22:11 */ +@EnableFeignClients(basePackages = "com.cxytiandi.foxmock.example.feign") @EnableTransactionManagement @SpringBootApplication public class FoxMockApp { @@ -70,6 +73,18 @@ public static void main(String[] args) throws Exception { }); } + FeignApiTestService feignApiTestService = ApplicationContextHelper.getBean(FeignApiTestService.class); + System.out.println("feign getUserInfo:" + new Gson().toJson(feignApiTestService.getUserInfo(1L))); + userDetailResult1 = feignApiTestService.getUserDetail(); + System.out.println("feign getUserDetail:" + new Gson().toJson(userDetailResult1)); + userDetail1 = userDetailResult1.getData(); + if (Objects.nonNull(userDetail1)) { + Map addressMap = userDetail1.getAddressMap(); + addressMap.forEach((k,v) -> { + System.out.println(k + "\t" + v.getAddress()); + }); + } + System.out.println("----------------------------------------"); Thread.sleep(5000); } diff --git a/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApi.java b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApi.java new file mode 100644 index 0000000..d3673bc --- /dev/null +++ b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApi.java @@ -0,0 +1,30 @@ +package com.cxytiandi.foxmock.example.feign; + +import com.cxytiandi.foxmock.example.Result; +import com.cxytiandi.foxmock.example.UserDetail; +import com.cxytiandi.foxmock.example.UserInfo; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.HttpMethod; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @作者 尹吉欢 + * @个人微信 jihuan900 + * @微信公众号 猿天地 + * @GitHub https://github.com/yinjihuan + * @作者介绍 http://cxytiandi.com/about + * @时间 2022-05-24 22:59 + */ +@FeignClient(name = "fox-mock-example", url = "http://localhost:8080") +public interface FeignApi { + + @RequestMapping(value = "/getUserInfo", method = RequestMethod.GET) + UserInfo getUserInfo(@RequestParam("id")Long id); + + @RequestMapping(value = "/getUserDetail", method = RequestMethod.GET) + Result getUserDetail(); + +} diff --git a/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApiImpl.java b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApiImpl.java new file mode 100644 index 0000000..9ca46b4 --- /dev/null +++ b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApiImpl.java @@ -0,0 +1,28 @@ +package com.cxytiandi.foxmock.example.feign; + +import com.cxytiandi.foxmock.example.Result; +import com.cxytiandi.foxmock.example.UserDetail; +import com.cxytiandi.foxmock.example.UserInfo; +import org.springframework.web.bind.annotation.*; + +/** + * @作者 尹吉欢 + * @个人微信 jihuan900 + * @微信公众号 猿天地 + * @GitHub https://github.com/yinjihuan + * @作者介绍 http://cxytiandi.com/about + * @时间 2022-05-24 22:59 + */ +@RestController +public class FeignApiImpl { + + @RequestMapping(value = "/getUserInfo", method = RequestMethod.GET) + public UserInfo getUserInfo(@RequestParam("id")Long id) { + return new UserInfo(); + } + + @RequestMapping(value = "/getUserDetail", method = RequestMethod.GET) + public Result getUserDetail() { + return new Result<>(); + } +} diff --git a/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApiTestService.java b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApiTestService.java new file mode 100644 index 0000000..16856c6 --- /dev/null +++ b/fox-mock-example/src/main/java/com/cxytiandi/foxmock/example/feign/FeignApiTestService.java @@ -0,0 +1,33 @@ +package com.cxytiandi.foxmock.example.feign; + +import com.cxytiandi.foxmock.example.Result; +import com.cxytiandi.foxmock.example.UserDetail; +import com.cxytiandi.foxmock.example.UserInfo; +import com.cxytiandi.foxmock.example.dubbo.DubboApi; +import org.apache.dubbo.config.annotation.Reference; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @作者 尹吉欢 + * @个人微信 jihuan900 + * @微信公众号 猿天地 + * @GitHub https://github.com/yinjihuan + * @作者介绍 http://cxytiandi.com/about + * @时间 2022-05-18 23:00 + */ +@Service +public class FeignApiTestService { + + @Autowired + private FeignApi feignApi; + + public UserInfo getUserInfo(Long id) { + return feignApi.getUserInfo(id); + } + + public Result getUserDetail() { + return feignApi.getUserDetail(); + } +} diff --git a/fox-mock-example/src/main/resources/application.properties b/fox-mock-example/src/main/resources/application.properties index 1fee8f1..805e12e 100644 --- a/fox-mock-example/src/main/resources/application.properties +++ b/fox-mock-example/src/main/resources/application.properties @@ -11,4 +11,6 @@ dubbo.application.name=foxmock-example dubbo.registry.address=47.105.66.210:8848 dubbo.registry.protocol=nacos dubbo.protocol.name=dubbo -dubbo.protocol.port=20880 \ No newline at end of file +dubbo.protocol.port=20880 + +feign.sentinel.enabled=true \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5b616b3..23662ee 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,7 @@ 3.1.19 3.5.6 2.7.3 + 10.2.3 @@ -72,12 +73,20 @@ mybatis ${mybatis.version} + org.apache.dubbo dubbo ${dubbo.version} + + + + io.github.openfeign + feign-core + ${feign.version} + \ No newline at end of file