簡體   English   中英

從org.aspectj.lang.ProceedingJoinPoint獲取模板/通用java.lang.reflect.Method對象

[英]Getting a Template/Generic java.lang.reflect.Method object from org.aspectj.lang.ProceedingJoinPoint

如果AspectJ與EJB攔截器的工作方式相同,則將不存在此問題。

考慮EJB攔截器方式的基本方案:

@AroundInvoke
public Object log(final InvocationContext ctx) throws Exception {
    // do stuff before
    final Object result = ctx.proceed();
    // do stuff after
    return result;
}

現在,我需要被攔截的Method對象。 在這里,我可以簡單地做到這一點:

        Method method = ctx.getMethod();

就是這樣,在此之后,我將檢查攔截的方法的注釋。

現在,我正在使用已部署到servlet容器(Tomcat)的應用程序,因此沒有EJB。 其中的AOP使用Spring + AspectJ實現。

周圍的方法如下所示:

@Around("execution(* foo.bar.domain.integration.database.dao..*(..))")
public Object log(final ProceedingJoinPoint pjp) throws Throwable {
    // do stuff before
    final Object result = pjp.proceed();
    // do stuff after
    return result;
}

在這里,我不能再這樣做:

Method method = pjp.getMethod(); // no such method exists

相反,我不得不自己使用反射來獲取Method對象,如下所示:

    final String methodName = pjp.getSignature().getName();
    final Object[] arguments = pjp.getArgs();
    final Class[] argumentTypes = getArgumentTypes(arguments);
    final Method method = pjp.getTarget().getClass().getMethod(methodName, argumentTypes);

它一直有效,直到您希望掌握模板方法為止:

@Transactional
public <T extends Identifiable> T save(T t) {
    if (null == t.getId()) {
        create(t);
        return t;
    }
    return update(t);
}

假設您按以下方式調用此方法:

Person person = new Person("Oleg");
personService.save(person);

您將收到:

Caused by: java.lang.NoSuchMethodException: foo.bar.domain.integration.database.dao.EntityService.save(foo.bar.domain.entity.Person)

拋出:

pjp.getTarget().getClass().getMethod()

問題在於泛型在運行時不存在,實際的方法簽名為:

public Identifiable save(Identifiable t) {}

final Class [] argumentsTypes將包含一個元素,其類型為Person。 所以這個電話:

pjp.getTarget().getClass().getMethod(methodName, argumentTypes);

將查找方法:

save(Person p)

並且不會找到它。

所以問題是:如何獲取java的模板方法對象的實例,該對象表示已被攔截的確切方法?

我猜想其中一種方法是蠻力的:獲取參數超類/接口,並嘗試使用這些類型獲取方法,直到不再獲取NoSuchMethodException為止,但這很丑陋。 有正常的清潔方法可以做到嗎?

好吧,現在我的問題解決了。 仔細研究一下:methodSignature.getMethod()返回后,我注意到它返回了接口,而不是實現類,並且接口上當然沒有注釋。 這與EJB攔截器不同,在EJB攔截器中,getMethod()返回實現的類方法。

所以最終的解決方案是這樣的:

    final String methodName = pjp.getSignature().getName();
    final MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
    Method method = methodSignature.getMethod();
    if (method.getDeclaringClass().isInterface()) {
        method = pjp.getTarget().getClass().getDeclaredMethod(methodName, method.getParameterTypes());    
    }

如果需要,還可以在此處處理接口注釋。

還要注意這一點:method.getParameterTypes()如果沒有此設置,它仍然會引發NoSuchMethodException,因此,我們可以通過((MethodSignature)pjp.getSignature())。getMethod();獲得正確的簽名是一件好事。

希望沒有更多的驚喜,即使我對在這里使用反射不滿意,我也更喜歡擁有實現類的方法實例,就像在EJB的InvocationContext中一樣。

BTW,Spring沒有AspectJ的本機方法:

public Object invoke(final MethodInvocation invocation) throws Throwable {}

返回接口並且不實現類。 也為了知識而對其進行了檢查。

最好的問候,並感謝您的幫助,我真的很感激。 奧列格

MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();

這不是應該立即歸咎於API。

另請參見Spring AOP:如何獲取所建議方法的注釋 我自己還沒有測試過。 那里的OP說它為他解決了這個問題。 對於其他用途,需要額外的@Around(annotation...)注釋。 (例如,嘗試將目標設置為僅METHOD ,並查看其行為)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM