[英]How to get Method annotated with given annotation in springboot / java app
有沒有辦法直接獲取使用給定對象中存在的給定注釋進行注釋的方法(非靜態)? 我不想遍歷所有方法的列表並檢查給定的注釋是否存在。 在下面的示例代碼中,我使用了虛擬方法(不存在) getMethodAnnotatedWith() 。 我需要用實際方法替換它。
public class MyController {
@PostMapping("/sum/{platform}")
@ValidateAction
public Result sum(@RequestBody InputRequest input, @PathVariable("platform") String platform) {
log.info("input: {}, platform: {}", input, platform);
return new Result(input.getA() + input.getB());
}
}
class InputRequest {
@NotNull
private Integer a;
@NotNull
private Integer b;
@MyValidator
public boolean customMyValidator() {
log.info("From customMyValidator-----------");
return false;
}
}
@Aspect
@Component
@Slf4j
public class AspectClass {
@Before(" @annotation(com.example.ValidateAction)")
public void validateAspect(JoinPoint joinPoint) throws Throwable {
log.info(" MethodName : " + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
log.info("args[0]==>"+args[0] +", args[1]==>"+args[1]);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Parameter[] parameters = method.getParameters();
Method customMyValidator = parameters[0].getType().getMethodAnnotatedWith(MyValidator.class); // InputRequest class type which has a method annotated with @MyValidator
customMyValidator.invoke(args[0]);
}
}
MethodUtils (Apache Commons Lang) 可用於實現該要求。
示例代碼中使用的 API: MethodUtils.getMethodsListWithAnnotation
方面代碼
@Component
@Aspect
public class CallAnnotatedMethodAspect {
@Pointcut("within(rg.so.q64604586.service.*) && @annotation(rg.so.q64604586.annotation.ValidateAction)")
public void validateActionMethod() {
};
@Before("validateActionMethod() && args(request,..)")
public void adviceMethod(InputRequest request)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
List<Method> methodList = MethodUtils.getMethodsListWithAnnotation(request.getClass(), MyValidator.class);
for (Method m : methodList) {
m.invoke(request, new Object[] {});
}
}
}
注意: MyValidator
注釋的保留策略是運行此代碼的運行時間。
如果切面中獲取的InputRequest
實例是一個 proxy ,則需要修改代碼以獲取相同的實際類。
這是我的獨立 AspectJ MCVE 。 我剛剛導入了一些 Spring 類。 語法在 Spring AOP 中是相同的。
輔助類:
package de.scrum_master.app;
public class Result {
public Result(int i) {}
}
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 ValidateAction {}
自定義驗證器接口(不是注釋):
package de.scrum_master.app;
public interface MyValidator {
boolean validate();
}
實現自定義驗證器接口的類:
package de.scrum_master.app;
public class InputRequest implements MyValidator {
private Integer a;
private Integer b;
public InputRequest(Integer a, Integer b) {
this.a = a;
this.b = b;
}
@Override
public boolean validate() {
System.out.println("Performing custom validation");
return false;
}
public Integer getA() {
return a;
}
public Integer getB() {
return b;
}
@Override
public String toString() {
return "InputRequest(a=" + a + ", b=" + b + ")";
}
}
看? 您只需覆蓋接口方法,而不是注釋驗證器方法。 它和以前一樣簡單,但類型更安全,感覺更“標准”。 AOP 也更容易處理,正如您將在下面找到的那樣。
控制器:
控制器看起來和以前一樣。 您仍然可以使用@ValidateAction
注釋該方法並將InputRequest
作為其第一個參數。
package de.scrum_master.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public class MyController {
@PostMapping("/sum/{platform}")
@ValidateAction
public Result sum(@RequestBody InputRequest input, @PathVariable("platform") String platform) {
System.out.println("input: " + input + ", platform: " + platform);
return new Result(input.getA() + input.getB());
}
}
示例驅動程序應用程序:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new MyController().sum(new InputRequest(11, 22), "foo");
}
}
方面:
方面現在非常簡單,就像我在對您的問題的評論中所說的那樣。 切入點檢查方法
@ValidateAction
和MyValidator
的第一個參數。 然后它通過args()
將MyValidator
參數綁定到一個建議方法參數。
請注意,在 Spring AOP 中您可以省略尾隨的&& execution(* *(..))
因為它只支持方法execution()
連接點,而在 AspectJ 中也有call()
連接點,這會導致雙重驗證和日志輸出。
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import de.scrum_master.app.MyValidator;
@Aspect
@Component
public class MyValidatorAspect {
@Before("@annotation(de.scrum_master.app.ValidateAction) && execution(* *(..)) && args(validator, ..)")
public void validateAspect(JoinPoint joinPoint, MyValidator validator) throws Throwable {
System.out.println(joinPoint);
validator.validate();
}
}
控制台日志:
execution(Result de.scrum_master.app.MyController.sum(InputRequest, String))
Performing custom validation
input: InputRequest(a=11, b=22), platform: foo
更新回答后續問題:請閱讀一些文檔。 Spring 手冊是一個很好的來源。
&& args(validator, ..)
是什么意思?
它被稱為參數綁定。 這個特定的切入點指示符意味着:匹配所有目標方法,其中第一個參數與通知方法參數列表中的validator
類型匹配,並將值綁定到該參數。 如您所見,該參數聲明為MyValidator validator
。 , ..
表示任何后續目標方法參數(如果有)都無關緊要。 有關更多信息,請參閱本手冊段落。
- 如果多個類實現
MyValidator
接口會發生什么。 我的意思是 FW 如何確定在調用當前控制器操作時必須通過哪個實現?
FW是什么意思? 也許框架? 無論如何,我沒有看到問題。 為什么框架必須弄清楚存在哪種實現? 這就是 OOP 和虛擬方法的美妙之處:您不需要弄清楚任何事情,因為每個實現都有一個boolean validate()
然后會被調用。 它很簡單,類型安全,沒有麻煩。 您的解決方案不是這些。 這種方法很有效。 與其問,你為什么不試試呢?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.