簡體   English   中英

使用AspectJ風格的Spring AOP的請求范圍內的bean的空指針異常(@Around建議@Transactional Webservice方法)

[英]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.

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