gpt4 book ai didi

java - 如何编写切面来满足不同的对象和方法参数

转载 作者:行者123 更新时间:2023-11-30 05:28:38 25 4
gpt4 key购买 nike

我希望编写一个方面,它可用于根据方法中传递的某些值来模拟数据。该模拟将取代实际的 REST 调用。如果值不匹配,请返回实际方法并调用 REST 端点。

我已经编写了一个可以在生产中使用的方面,但非常困惑,并且必须在其中实现多个不同的方法,以满足返回不同对象和处理不同方法参数的需要。有没有一种方法可以概括这一点,以便我可以使用单一方法来执行工作,并且能够在将来的情况下使用 if ,而不必担心对象类型和方法签名。

当前实现如下。我如何修改它以作为单一方法工作,可以满足 future 的方法和响应类型,而无需更改我的方面?

为了澄清我有以下 3 个方法,我想使用 Aspect 来模拟它们。如您所见,我有一个包含名称的自定义注释。该方法的所有参数都不对齐。

@MyMock(name = "ABC")
public AResponse getAResponse(ARequest aRequest){
// make a REST call
}

@MyMock(name = "DEF")
public BResponse getBResponse(String fruit, BRequest bRequest){
// make a REST call
}

@MyMock(name = "GHJ")
public CResponse getCResponse(String vehicle, CRequest cRequest, int id){
// make a REST call
}

目前的 Aspect 类如下。我使用注释中的名称值来确定要调用哪个方法以及要返回哪个值类型。如您所见,这不太可扩展。每次实现新的模拟时,我都需要编写新的方法和逻辑。

@Around("@annotation(myMock)")
public Object getMockedData(ProceedingJoinPoint pjp, MyMock myMock) throws Throwable {
String servName = performMocking.name();
Object[] methodArguments = pjp.getArgs();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Class returnType = signature.getReturnType();

switch (myMock.name()) {
case "ABC":
getA(methodArguments[0]);
break;
case "DEF":
getB(methodArguments[0]);
break;
case "GHJ":
getC(methodArguments[2]);
break;
}
return pjp.proceed(methodArguments);
}

private AResponse getA(ARequest aRequest){
// I use methodArguments[0] (which is aRequest) to decide what value to return as a mock response here.
// There is a getName value in that object which I use to reference
}

private BResponse getB(BRequest bRequest){
// I use methodArguments[0] (which is a String) to decide what value to return as a mock response here.
}

private CResponse getC(CRequest cRequest){
// I use methodArguments[2] (which is an int) to decide what value to return as a mock response here.
}

以上所有 get 方法都会调用外部 JSON 文件来获取模拟数据。文件内容如下。如果键匹配,将给出模拟响应,否则返回进行实际的 REST 调用。

{
"ABC": {
"enabled": "true",
"responses": [
{
"a_name": "{some valid json response matching the AResponse Object structure}"
}
]
},
"DEF": {
"enabled": "true",
"responses": [
{
"d_name": "{some valid json response matching the BResponse Object structure}"
}
]
},
"GHJ": {
"enabled": "true",
"responses": [
{
"123": "{some valid json response matching the CResponse Object structure}"
}
]
}
}

编辑:添加了我的 ASPECT 类,如下所示:

package com.company.a.b.mock;

import com.domain.abc.b.logging.util.MyLogger;
import com.domain.abc.a.util.generic.ConfigHelper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;
import java.util.Map;

@Aspect
@Component
public class AspectMocking {

private final ConfigHelper configHelper;
private final MyLogger myLogger;

public AspectMocking(ConfigHelper configHelper, MyLogger myLogger) {
this.configHelper = configHelper;
this.myLogger = myLogger;
}

@Around("@annotation(myMock)")
public Object getMockedData(ProceedingJoinPoint pjp, MyMock myMock) throws Throwable {

final String env = System.getProperty("spring.profiles.active");
String response = null;
Object returnObject = null;
String logMessage = null;
String servName = myMock.name();
Object[] methodArguments = pjp.getArgs();
try {
if ("test_env1".equals(env) || "test_env2".equals(env)) {

MethodSignature signature = (MethodSignature) pjp.getSignature();
Class returnType = signature.getReturnType();

Map allDataFromMockedFile = getMockedDataFromFile();
Map getResultForKey = (Map) allDataFromMockedFile.get(myMock.name());

List result = null;
if(getResultForKey != null){
result = (List) getResultForKey.get("responses");
}

switch (myMock.name()) {
case "ABC":
response = fetchABCMockedData(result, (String) methodArguments[0]);
logMessage = "Fetching ABC mock data for ABC Response: " + response;
break;
case "V2_ABC":
response = fetchABCMockedData(getIntlResult(myMock.name(), (String)methodArguments[2]), (String) methodArguments[0]);
logMessage = "Fetching V2 ABC mock data for V2 ABC Response: " + response;
break;
case "DEF":
response = fetchDEFMockedData(result, (String) methodArguments[0]);
logMessage = "Fetching DEF mock data: " + response;
break;
case "GHJ":
response = fetchGHJMockedOfferData(result, (String) methodArguments[0], (String) methodArguments[1], (String) methodArguments[2]);
logMessage = "Fetching GHJ mock data: " + response;
break;
}

ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
returnObject = mapper.readValue(response, returnType);
}
} catch (Exception exp) {
myLogger.addMessageAsWarning(String
.format("Exception occured for service %s while loading the mocked json, so hitting the actual service:"
+ exp.getMessage(), servName));
}

if (returnObject == null) {
returnObject = pjp.proceed(methodArguments);
}

myLogger.addMessage(logMessage);
return returnObject;
}


private List getIntlResult(String name, String locale){
Map localBasedMockFile = getMockedDataFromFile(locale.toLowerCase());
Map localeSpecificMockedData = (Map) localBasedMockFile.get(name);
return (List) localeSpecificMockedData.get("responses");
}

// had to add this recently as we needed locale values now to go to a diff file.
private Map getMockedDataFromFile(String countryCode){
final String DATA_URL = String.format("/a/b/c/%s/data.json", countryCode);
return configHelper.getMAVDataAsObject(DATA_URL, Map.class);
}

private Map getMockedDataFromFile() {
final String DATA_URL = "/a/b/c/zh/mock.json";
return configHelper.getMAVDataAsObject(DATA_URL, Map.class);
}

private String fetchABCMockedData(List<Map> allResponses, String vinNumber) throws IOException {
String response = null;
for (Map m : allResponses) {
String mockedVinNumber = m.keySet().toString().replaceAll("[\\[\\]]", "");
if (vinNumber.equals(mockedVinNumber)) {
response = (String) m.get(mockedVinNumber);
}
}
return response;
}

private String fetchDEFMockedData(List<String> allResponses, String vinNumber) {
return allResponses.contains(vinNumber) ? "true" : "false";
}

private String fetchGHJMockedOfferData(List<Map> allResponses, String journey, String name, String pin) {
String response = null;
String key = journey+"_"+name + "_" + pin;

for (Map m : allResponses) {
String mockedKey = m.keySet().toString().replaceAll("[\\[\\]]", "");
if (mockedKey.equals(key)) {
response = (String) m.get(mockedKey);
}
}

return response;
}
}

最佳答案

让我们先编译您的示例代码,然后再继续讨论,好吗?这是我的MCVE :

帮助类:

package de.scrum_master.app;

public class ARequest {}
package de.scrum_master.app;

public class BRequest {}
package de.scrum_master.app;

public class CRequest {}
package de.scrum_master.app;

public class AResponse {
private String content;

public AResponse(String content) {
this.content = content;
}

@Override
public String toString() {
return "AResponse[content=" + content + "]";
}
}
package de.scrum_master.app;

public class BResponse {
private String content;

public BResponse(String content) {
this.content = content;
}

@Override
public String toString() {
return "BResponse[content=" + content + "]";
}
}
package de.scrum_master.app;

public class CResponse {
private String content;

public CResponse(String content) {
this.content = content;
}

@Override
public String toString() {
return "CResponse[content=" + content + "]";
}
}

标记注释:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface MyMock {
String name();
}

驱动程序应用程序:

package de.scrum_master.app;

public class Application {
@MyMock(name = "ABC")
public AResponse getAResponse(ARequest aRequest) {
// make a REST call
return new AResponse("real A response");
}

@MyMock(name = "DEF")
public BResponse getBResponse(String fruit, BRequest bRequest) {
// make a REST call
return new BResponse("real B response");
}

@MyMock(name = "GHJ")
public CResponse getCResponse(String vehicle, CRequest cRequest, int id) {
// make a REST call
return new CResponse("real C response");
}

public static void main(String[] args) {
Application application = new Application();
System.out.println(application.getAResponse(new ARequest()));
System.out.println(application.getBResponse("apple", new BRequest()));
System.out.println(application.getCResponse("bicycle", new CRequest(), 11));
}
}

不带方面的控制台日志:

AResponse[content=real A response]
BResponse[content=real B response]
CResponse[content=real C response]

方面,变体 A,使用标记注释:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import de.scrum_master.app.ARequest;
import de.scrum_master.app.AResponse;
import de.scrum_master.app.BResponse;
import de.scrum_master.app.CResponse;
import de.scrum_master.app.MyMock;

@Aspect
public class MockResponseAspect {
@Around("@annotation(myMock)")
public Object getMockedData(ProceedingJoinPoint pjp, MyMock myMock) throws Throwable {
// Whatever this does...
//String servName = performMocking.name();
Object[] methodArguments = pjp.getArgs();

switch (myMock.name()) {
case "ABC": return getA((ARequest) methodArguments[0]);
case "DEF": return getB((String) methodArguments[0]);
case "GHJ": return getC((int) methodArguments[2]);
default: return pjp.proceed(methodArguments);
}
}

private AResponse getA(ARequest aRequest) {
return new AResponse("mock A response");
}

private BResponse getB(String fruit) {
return new BResponse("mock B response");
}

private CResponse getC(int id) {
return new CResponse("mock C response");
}
}

使用方面的控制台日志:

AResponse[content=mock A response]
BResponse[content=mock B response]
CResponse[content=mock C response]

方面,变体 B,使用返回类型:

这是根本不使用模拟注释的方面的简化版本。相反,它可以使用我的示例中的方法名称,或者可能是另一个现有的方法注释,例如 @Response 或所有响应方法共有的其他任何内容。

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import de.scrum_master.app.ARequest;
import de.scrum_master.app.AResponse;
import de.scrum_master.app.BResponse;
import de.scrum_master.app.CResponse;

@Aspect
public class MockResponseAspect {
@Around("execution(* get*Response(..))")
public Object getMockedData(ProceedingJoinPoint pjp) throws Throwable {
// Whatever this does...
//String servName = performMocking.name();
Object[] methodArguments = pjp.getArgs();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Class<?> returnType = signature.getReturnType();

switch (returnType.getSimpleName()) {
case "AResponse": return getA((ARequest) methodArguments[0]);
case "BResponse": return getB((String) methodArguments[0]);
case "CResponse": return getC((int) methodArguments[2]);
default: return pjp.proceed(methodArguments);
}
}

private AResponse getA(ARequest aRequest) {
return new AResponse("mock A response");
}

private BResponse getB(String fruit) {
return new BResponse("mock B response");
}

private CResponse getC(int id) {
return new CResponse("mock C response");
}
}

控制台日志与第一个方面相同。

现在我们有事情要讨论。您对此有何不喜欢?你想实现什么目标?

关于java - 如何编写切面来满足不同的对象和方法参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58019622/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com