![](/img/trans.png)
[英]How can I access methods attributes with Spring AOP (AspectJ-style)?
[英]Null pointer exception for request scoped bean using AspectJ-style Spring AOP (@Around advising an @Transactional webservice method)
我正在使用Spring 3.1.0.RC1。 我有一個基於Apache CXF的Web服務。 我試圖建議每個@Transactional帶注釋的服務方法。
我有一些“周圍”建議的方面。 在方法內部,我將數據存儲到請求范圍內的事務有效負載bean中。 稍后,我在另一個單例作用域的bean中從此有效載荷中請求uuid。 結果是空指針異常。
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
at com.spp.mui.jaxws.service.virtual._0_1.VirtualWebService.postOfferSet(VirtualWebService.java:209)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at com.spp.mui.aop.tx.NewAgeTransactionLoggingAspect.logTransaction(NewAgeTransactionLoggingAspect.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at com.spp.mui.aop.ws.TraceWebServiceMethodAspect.invoke(TraceWebServiceMethodAspect.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy95.postOfferSet(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:173)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:89)
... 49 more
Caused by: java.lang.NullPointerException
at com.spp.mui.jaxws.agent.WSResponseAgent.getResponse(WSResponseAgent.java:28)
at com.spp.mui.jaxws.agent.WSResponseAgent.getResponse(WSResponseAgent.java:18)
at com.spp.mui.jaxws.handler.offer.PostVirtualOffersHandler.handle(PostVirtualOffersHandler.java:58)
at com.spp.mui.jaxws.service.virtual._0_1.VirtualWebService.postOfferSet(VirtualWebService.java:205)
... 88 more
那么,此堆棧跟蹤中發生了什么?
調用了Web服務方法(即postOfferSet)。 該方法是由具有@Around切入點的方面建議的。 該方法也帶有@Transactional注釋。
更新自此以來,我已將原始配置更新為基於Java的配置。 使用XML配置或Java配置時,都會遇到相同的問題。 我的Java配置是當前正在使用的配置,也是我想要幫助診斷的配置。 請向下滾動(向前跳)以查看下面的Java配置。 (對於其他新手,請繼續閱讀)。
我的方面配置看起來像
<aop:aspectj-autoproxy />
<bean id="newAgeTxLoggingAspect" class="com.spp.mui.aop.tx.NewAgeTransactionLoggingAspect">
<property name="transactionLogService" ref="txLogServiceEngine" />
<property name="securable" ref="securityAgent" />
<property name="loggableTransactionToken" ref="loggableTransactionToken" />
<property name="logQuery" value="${newAgeTxLoggingAspect.logQuery}" />
<property name="logReply" value="${newAgeTxLoggingAspect.logReply}" />
<property name="logSubmit" value="${newAgeTxLoggingAspect.logSubmit}" />
<property name="maxReplySize" value="${newAgeTxLoggingAspect.maxReplySize}" />
<property name="maxRequestSize" value="${newAgeTxLoggingAspect.maxRequestSize}" />
<property name="order" value="5" />
</bean>
如前所述,loggableTransactionToken是請求范圍的。
這是它的bean定義
<!--
Request scoped bean that allows LoggableTransaction instances to be created and accessed on a single-thread of execution
Consult http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-scopes-other-injection
-->
<bean id="loggableTransactionToken" class="com.spp.mui.commons.spring.LoggableTransactionToken" scope="request">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
前面提到的Web服務方法委托給Handler
這是處理程序的摘錄
@Override
public ConfirmationType handle(OfferSetType request) {
// step 1: convert from JAXB type to domain objects
List<MktVirtualOffer> candidateOffers = codecService.convert(request, List.class);
...
// step 4: get response -- response encapsulates transaction id
return responder.getResponse();
}
注意處理程序使用響應程序嗎?
這是響應者(又名WSResponseAgent)的摘錄
@Override
public ConfirmationType getResponse() {
ConfirmationType response = new ConfirmationType();
LoggableTransaction lt = token.getToken();
response.setTransactionID(lt.getUuid());
logger.debug(ReflectUtil.toString(lt));
return response;
}
希望我已經提供了足夠的幫助您幫助我了解為什么要獲得NPE?
更新這是Java配置和其他暗示
AOP配置
@Configuration
@EnableAspectJAutoProxy
@Import(value = { LoggingConfig.class, AuthConfig.class })
public class AopConfig {
@Resource
private Environment env;
@Resource
private LoggingConfig loggingConfig;
@Resource
private AuthConfig authConfig;
/* Consult
* static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction-declarative-applying-more-than-just-tx-advice
* and http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-ataspectj-advice-ordering
*/
@Bean
public NewAgeTransactionLoggingAspect txLoggingAspect() {
NewAgeTransactionLoggingAspect aspect = new NewAgeTransactionLoggingAspect();
aspect.setEnvironment(env);
aspect.setLoggableTransactionToken(loggingConfig.loggableTxToken());
aspect.setSecurable(authConfig.securable());
aspect.setTransactionLogService(loggingConfig.txLogService());
aspect.setLogQuery(Boolean.valueOf(env.getProperty("newAgeTxLoggingAspect.logQuery", Boolean.toString(true))));
aspect.setLogReply(Boolean.valueOf(env.getProperty("newAgeTxLoggingAspect.logReply", Boolean.toString(true))));
aspect.setLogSubmit(Boolean.valueOf(env.getProperty("newAgeTxLoggingAspect.logSubmit", Boolean.toString(true))));
aspect.setMaxRequestSize(Integer.valueOf(env.getProperty("newAgeTxLoggingAspect.maxRequestSize", "100000000")));
aspect.setMaxReplySize(Integer.valueOf(env.getProperty("newAgeTxLoggingAspect.maxReplySize", "100000000")));
aspect.setOrder(Integer.valueOf(env.getProperty("newAgeTxLoggingAspect.order", "5")));
return aspect;
}
}
這是配置文件中的片段,負責設置NewAgeTransactionLoggingService和LoggableTransactionToken
@Bean
public TransactionLogService txLogService() {
HibernateTransactionLogService service = new HibernateTransactionLogService();
service.setLogParameters(Boolean.valueOf(Boolean.toString(true)));
service.setTimeDispatcher(timeConfig.timeDispatcher());
service.setSessionFactory(sessionFactory);
return service;
}
/**
* @return <p>Request scoped bean that allows LoggableTransaction instances to be created and accessed on a request.<br/>
* Consult <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-scopes-other-injection">Bean Factory Scopes</a></p>
*/
@Bean
@Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
public Token<LoggableTransaction> loggableTxToken() {
// XXX Is this is the correct way to specify a request-scoped bean?
LoggableTransactionToken token = new LoggableTransactionToken();
return token;
}
這是帶有@Around建議的方面的方法。
@Around("com.spp.mui.aop.Advisables.transactionLoggable()")
public Object logTransaction(ProceedingJoinPoint pjp) throws Throwable {
// Prepare to log the execution time of the method call..
long t0 = 0;
t0 = System.currentTimeMillis();
Object resultObject = null;
try {
resultObject = pjp.proceed();
} catch (Throwable t) {
logFailedTransactionAndRethrow(pjp, t0, t);
}
logSuccessfulTransaction(pjp, t0, resultObject);
return resultObject;
}
所以,我不得不自己解決這個問題。
基礎:
1)創建一個自定義ServletFilter,創建LoggableTransaction並在那里設置令牌。 我擴展了Spring的OncePerRequestFilter並使用WebApplicationContextUtils在doFilterInternal方法中獲取請求范圍的bean。
2)在web.xml中注冊此過濾器。 另外,請確保注冊Spring的RequestContextListener和ContextLoaderListener。
3)上面的方面實現,保持不變,除了切入點參考。 現在,切入點引用了一種服務方法(Web服務委托給它)。
4)這是最關鍵的...如果令牌未實現接口,則應使用@Scope(value =“ request”,proxyMode = ScopedProxyMode.TARGET_CLASS)裝飾Bean定義。 您將在類路徑上需要CGLib。 無論如何,我都需要Hibernate。
之后,事情進行得很順利。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.