Исключение нулевого указателя для bean-объекта в области запросов, использующего AspectJ-стиль Spring AOP (@Around рекомендует метод веб-сервиса @Transactional) - PullRequest
0 голосов
/ 21 октября 2011

Я использую Spring 3.1.0.RC1. У меня есть веб-сервис на основе Apache CXF. Я пытаюсь рекомендовать каждый аннотированный метод обслуживания @Transactional.

У меня есть аспект с некоторыми советами. Внутри метода я сжимаю данные в бин полезных данных транзакции в области запроса. Позже я прошу 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

Так что же происходит в этой трассировке стека?

Был вызван метод веб-службы (то есть postOfferSet). Этот метод был рекомендован аспектом с @Around pointcut. Этот метод также аннотирован @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 является областью запроса.

Вот бобовое определение для него

<!-- 
    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>

Упомянутый выше метод веб-службы делегирует Обработчику

Вот фрагмент из обработчика

 @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();
}

Заметили, что обработчик использует ответчик?

Вот фрагмент из ответчика, a.k.a. 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 config

@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 advice.

@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 Ответ

0 голосов
/ 21 ноября 2011

Итак, мне пришлось снова решить эту проблему самостоятельно.

Основы:

1) Создайте собственный ServletFilter, создайте LoggableTransaction и установите там токен. Я расширил Spring Once OncePerRequestFilter и использовал WebApplicationContextUtils для выборки bean-объекта с областью запроса в методе doFilterInternal.

2) Зарегистрируйте этот фильтр в web.xml. Также не забудьте зарегистрировать Spring RequestContextListener и ContextLoaderListener.

3) Аспект реализации выше, остается тем же самым, за исключением ссылки на pointcut. Теперь pointcut ссылается на метод сервиса (тот, которому делегирует веб-сервис).

4) Это было самым важным ... если токен НЕ реализует интерфейс, то определение компонента должно быть украшено @Scope (value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS). Вам понадобится CGLib на пути к классам. В любом случае мне это было нужно для Hibernate.

После этого все работало прекрасно.

...