简体   繁体   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)

I am using Spring 3.1.0.RC1. 我正在使用Spring 3.1.0.RC1。 I have an Apache CXF based web service. 我有一个基于Apache CXF的Web服务。 I am trying to advise each @Transactional annotated service method. 我试图建议每个@Transactional带注释的服务方法。

I have an aspect with some Around advice. 我有一些“周围”建议的方面。 Inside the method I am squirreling away data into a request-scoped transaction payload bean. 在方法内部,我将数据存储到请求范围内的事务有效负载bean中。 Later I ask for the uuid out of this payload in another singleton scoped bean. 稍后,我在另一个单例作用域的bean中从此有效载荷中请求uuid。 The result is a null pointer exception. 结果是空指针异常。

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

So what's going on in this stack trace? 那么,此堆栈跟踪中发生了什么?

A web service method (ie, postOfferSet) was invoked. 调用了Web服务方法(即postOfferSet)。 That method was advised by an aspect with an @Around pointcut. 该方法是由具有@Around切入点的方面建议的。 That method is also @Transactional annotated. 该方法也带有@Transactional注释。

UPDATE I have since updated the original configuration to be Java config based. 更新自此以来,我已将原始配置更新为基于Java的配置。 With either an XML config or a Java config I run into the same problem. 使用XML配置或Java配置时,都会遇到相同的问题。 My Java config is the one currently in play, and the one I'd like help diagnosing. 我的Java配置是当前正在使用的配置,也是我想要帮助诊断的配置。 Please scroll down (skip ahead) to see this Java config below. 请向下滚动(向前跳)以查看下面的Java配置。 (For others new to this post read on). (对于其他新手,请继续阅读)。

My aspect config looks a little like 我的方面配置看起来像

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

As mentioned earlier the loggableTransactionToken is request-scoped. 如前所述,loggableTransactionToken是请求范围的。

Here's the bean def for it 这是它的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>

The aforementioned web service method delegates to a Handler 前面提到的Web服务方法委托给Handler

Here's a snippet from the 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();
}

Notice the Handler uses a Responder? 注意处理程序使用响应程序吗?

Here's a snippet from the Responder, aka WSResponseAgent 这是响应者(又名WSResponseAgent)的摘录

@Override
public ConfirmationType getResponse() {
    ConfirmationType response = new ConfirmationType();
    LoggableTransaction lt = token.getToken();
    response.setTransactionID(lt.getUuid());
    logger.debug(ReflectUtil.toString(lt));
    return response;
}

Hopefully, I've provided enough to help you help me sleuth why I'm getting the NPE? 希望我已经提供了足够的帮助您帮助我了解为什么要获得NPE?

UPDATE Here's the Java config and additional impl 更新这是Java配置和其他暗示

AOP config 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;
}


}

Here's a snippet from the config responsible for setting up the NewAgeTransactionLoggingService and the LoggableTransactionToken 这是配置文件中的片段,负责设置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;
}

Here's the aspect's method with @Around advice. 这是带有@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;
}

So, I had to solve this one myself again. 所以,我不得不自己解决这个问题。

The basics: 基础:

1) Create a custom ServletFilter, create the LoggableTransaction and set on token there. 1)创建一个自定义ServletFilter,创建LoggableTransaction并在那里设置令牌。 I extended Spring's OncePerRequestFilter and used WebApplicationContextUtils to fetch request scoped bean within doFilterInternal method. 我扩展了Spring的OncePerRequestFilter并使用WebApplicationContextUtils在doFilterInternal方法中获取请求范围的bean。

2) Register this Filter in web.xml. 2)在web.xml中注册此过滤器。 Also be sure to register Spring's RequestContextListener and ContextLoaderListener. 另外,请确保注册Spring的RequestContextListener和ContextLoaderListener。

3) The aspect implementation above, remains the same, save for the pointcut reference. 3)上面的方面实现,保持不变,除了切入点参考。 The pointcut now references a service method (one that the web service delegates to). 现在,切入点引用了一种服务方法(Web服务委托给它)。

4) This was the most critical... if the token does NOT implement an interface, then the bean definition should be adorned with @Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS). 4)这是最关键的...如果令牌未实现接口,则应使用@Scope(value =“ request”,proxyMode = ScopedProxyMode.TARGET_CLASS)装饰Bean定义。 You'll need CGLib on the classpath. 您将在类路径上需要CGLib。 I needed it anyway for Hibernate. 无论如何,我都需要Hibernate。

Things worked beautifully afterwards. 之后,事情进行得很顺利。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM