Java 如何编写方面以适应不同的对象和方法参数
我希望编写一个方面,它可以根据方法中传递的某些值来模拟数据。此模拟将替换实际的REST调用。如果值不匹配,则返回实际方法并调用REST端点 我已经编写了一个方面,它在生产环境中工作,但非常混乱,必须在其中实现多个不同的方法,以适应返回不同对象和处理不同方法参数的需要。是否有一种方法可以将其推广,这样我就可以像一个方法一样执行工作,并且能够在将来的情况下使用if,而不必担心对象类型和方法签名 目前的执行情况如下。我如何修改它,使之成为一种单一的方法,能够适应未来的方法和响应类型,而不必改变我的方面 为了澄清这一点,我有以下3种方法,我想用Aspect模拟它们。如您所见,我有一个自定义注释,它接受一个名称。该方法的参数都没有对齐Java 如何编写方面以适应不同的对象和方法参数,java,spring,aop,aspectj,Java,Spring,Aop,Aspectj,我希望编写一个方面,它可以根据方法中传递的某些值来模拟数据。此模拟将替换实际的REST调用。如果值不匹配,则返回实际方法并调用REST端点 我已经编写了一个方面,它在生产环境中工作,但非常混乱,必须在其中实现多个不同的方法,以适应返回不同对象和处理不同方法参数的需要。是否有一种方法可以将其推广,这样我就可以像一个方法一样执行工作,并且能够在将来的情况下使用if,而不必担心对象类型和方法签名 目前的执行情况如下。我如何修改它,使之成为一种单一的方法,能够适应未来的方法和响应类型,而不必改变我的方面
@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
}
当前的方面类如下所示。我在注释中使用name值来确定调用哪个方法以及返回哪个值类型。正如您所看到的,这不是非常可伸缩的。每次实现新的mock时,我都需要编写一个新的方法和逻辑
@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}"
}
]
}
}
编辑:添加了我的方面类,如下所示:
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;
}
}
package com.company.a.b.mock;
导入com.domain.abc.b.logging.util.MyLogger;
导入com.domain.abc.a.util.generic.ConfigHelper;
导入com.fasterxml.jackson.databind.DeserializationFeature;
导入com.fasterxml.jackson.databind.ObjectMapper;
导入org.aspectj.lang.ProceedingJoinPoint;
导入org.aspectj.lang.annotation.Around;
导入org.aspectj.lang.annotation.Aspect;
导入org.aspectj.lang.reflect.MethodSignature;
导入org.springframework.stereotype.Component;
导入java.io.IOException;
导入java.util.List;
导入java.util.Map;
@面貌
@组成部分
公共类AspectMocking{
私有最终ConfigHelper ConfigHelper;
私人最终MyLogger MyLogger;
公共AspectMocking(ConfigHelper ConfigHelper、MyLogger MyLogger){
this.configHelper=configHelper;
this.myLogger=myLogger;
}
@周围(“@annotation(myMock)”)
公共对象getMockedData(ProceedingJoinPoint pjp,MyMock MyMock)抛出可丢弃的{
最终字符串env=System.getProperty(“spring.profiles.active”);
字符串响应=null;
objectreturnobject=null;
字符串logMessage=null;
字符串servName=myMock.name();
Object[]methodArguments=pjp.getArgs();
试一试{
if(“test_env1.equals(env)| |“test_env2.equals(env)){
MethodSignature=(MethodSignature)pjp.getSignature();
类returnType=signature.getReturnType();
Map allDataFromMockedFile=getMockedDataFromFile();
Map GetResultWorkey=(Map)allDataFromMockedFile.get(myMock.name());
列表结果=空;
if(GetResultWorkey!=null){
结果=(列表)getResultWorkey.get(“响应”);
}
开关(myMock.name()){
案例“ABC”:
response=fetchABCMockedData(result,(String)methodArguments[0]);
logMessage=“为ABC响应获取ABC模拟数据:”+响应;
打破
案例“V2_ABC”:
response=fetchABCMockedData(getIntlResult(myMock.name(),(String)methodArguments[2]),(String)methodArguments[0]);
logMessage=“为V2 ABC响应获取V2 ABC模拟数据:”+响应;
打破
案例“DEF”:
response=fetchDEFMockedData(result,(String)methodArguments[0]);
logMessage=“获取DEF模拟数据:”+响应;
打破
案例“GHJ”:
response=fetchGHJMockedOfferData(result,(String)methodArguments[0],(String)methodArguments[1],(String)methodArguments[2]);
logMessage=“获取GHJ模拟数据:”+响应;
打破
}
ObjectMapper mapper=new ObjectMapper().configure(UNKNOWN属性上的反序列化feature.FAIL,false);
returnObject=mapper.readValue(响应,returnType);
}
}捕获(异常扩展){
myLogger.addMessageAsWarning(字符串
.format(“加载模拟json时服务%s发生异常,因此命中实际服务:”
+exp.getMessage(),servName));
}
if(returnObject==null){
returnObject=pjp.procedure(methodArguments);
}
myLogger.addMessage(日志消息);
返回对象;
}
私有列表getIntlResult(字符串名称、字符串区域设置){
Map localBasedMockFile=getMockedDataFromFile(locale.toLowerCase());
Map localspecificmockeddata=(Map)localBasedMockFile.get(name);
返回(列表)localspecificmockeddata.get(“响应”);
}
//最近不得不添加这个,因为我们现在需要区域设置值才能转到diff文件。
私有映射getMockedDataFromFile(字符串countryCode){
最终字符串数据\u URL=String.format(“/a/b/c/%s/DATA.json”,countryCode);
返回configHelper.getMAVDataAsObject(数据URL,Map.class);
}
私有映射getMockedDataFromFile(){
最终字符串DATA_URL=“/a/b/c/zh/mock.json”;
返回configHelper.getMAVDataAsObject(数据URL,Map.class);
}
私有字符串fetchABCMo