2

我遇到了一个奇怪的问题,我很难追踪。我有一个类 (ServiceErrorInterceptor) 定义为@Aspect通过 XML 配置实例化为单例 bean。XML 配置允许我注入它的依赖 bean。

在我的正常工作流程中,一切正常。方面类已正确实例化,并且无论何时调用通知,注入的 bean 都符合我的预期。

但是,当我运行 JUnit 测试时,我注入的所有 bean 都是空的。这使我得出结论,通知是从不同的 bean 调用的 - 不是 Spring 实例化的同一个单例 bean。为了进一步验证我的假设,我在实例化过程中调用的 setter 上设置了一个断点,如果我在建议中设置断点,则 bean id 与 bean id 不同。

我必须在我的 JUnit 类中启用一些特殊配置来纠正这个问题吗?我的测试类已经注释:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { 
        "classpath:spring/applicationContext-base.xml", 
        "classpath:spring/applicationContext-calculateServices.xml", 
        "classpath:spring/applicationContext-dom.xml"})
public class LendingSimulationServiceImplTest {
...
...
}

我查看了日志(我启用了 spring 跟踪日志),但没有看到任何突出的内容。在这里发布整个日志可能会有点矫枉过正。如果日志的特定部分有价值,请告诉我,我会发布。

如果有帮助,我可以发布我的方面、我的 junit 和我的配置的代码。

application-context.xml 片段:

<!-- SPRING ASPECT BEAN.  POINTCUT DEFINED IN BEAN WITH ANNOTATION -->
<bean id="serviceErrorInterceptor" class="com.cws.cs.lendingsimulationservice.error.ServiceErrorInterceptor" scope="singleton">
    <property name="errorMessageProvider" ref="resourceBundleProviderImpl"/>
    <property name="defaultLocale">
        <util:constant static-field="java.util.Locale.ENGLISH" />
    </property>
</bean>

任何建议,将不胜感激。

编辑

我的bean实现为:

@Aspect
public class ServiceErrorInterceptor {

    /**
     * Logger
     */
    private static final Logger logger = LoggerFactory.getLogger(ServiceErrorInterceptor.class);

    /**
     * SOAP Header data
     */
    @Autowired
    private SOAPHeaderData soapHeaderData;

    public ServiceErrorInterceptor(){
        int x = 0;
        x=x+1;

    }

    /**
     * Exception Interceptor. 
     * @param ex
     */
    @AfterThrowing(pointcut = "execution(* com.cws.cs.lendingsimulationservice.process.CalculatorProcess.calculate (..))", throwing = "ex")
    public void errorInterceptor(Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error Message Interceptor started");
        }

    }

我的 pom 的相关部分:

    <!-- Aspect Oriented Programming (AOP) Framework (depends on spring-core, 
        spring-beans) Define this if you use Spring AOP APIs (org.springframework.aop.*) -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>

    <!-- Support for AspectJ Annotations -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${org.aspectj}</version>
    </dependency>

我已经完成了进一步的调试并在虚拟构造函数中放置了一个断点,我得到以下结果:

  • 使用 @Aspect 和 XML 配置,构造函数被调用两次(不同的 bean id)
  • 如果我删除 @Aspect 注释,那么它只会被调用一次。
  • 如果离开 @Aspect 但删除 XML 配置,则构造函数甚至不会被调用。
  • 如果我将 @Component 注释与 @Aspect 结合使用(但没有任何 XML 配置),那么 bean 会被构造两次。
  • 然而,奇怪的是,使用 @Component 和 @Aspect 注释以及XML 配置,构造函数仍然只调用了两次。

那么,为什么同时拥有 XML 配置和 @Aspect 注释会导致构造函数被调用两次,并使用两个不同的 bean id?

我已经进一步验证,如果我将整个 AOP 定义移动到 XML 配置中(删除 @Aspect 和 @Pointcut 注释),那么 bean 只会构造一次。

结束编辑

谢谢,

埃里克

4

3 回答 3

1

除了@Aspect 之外,您的方面是否有任何自动检测注释(@Component、@Service)等 - 如果有,这可能是一个不同的 bean 似乎与您定义的 bean 一起出现的原因之一配置。

此外,只需扫描所有配置以确保您没有在其他地方声明此 bean。

据我所知,在 Junit 级别上没有什么特别需要做的。

于 2012-04-12T20:53:58.253 回答
1

在 SpringSource STS 论坛上与人们进行大量讨论后(请参阅此线程),事实证明该问题与 AJDT 配置有关。目前,AJ 正在编织切面,而 Spring 正在将切面定位在 Classpath 上,因此它们都在执行。

不幸的是,AJ maven 插件缺少允许排除编织的配置参数;当前配置不包括 LTW 和 CTW。

因此,目前的解决方法是添加-xmlConfigured到 AJ 编译器标志,然后在 aop.xml 管理中指定一个 aop.xml 文件,该文件仅列出您想要包含在项目中的 AJ 方面。

要使其工作,请将“-xmlConfigured”添加到项目属性“非标准编译器选项”中,然后在 AspectJBuild>“aop.xml 管理”中将其指向一个简单的 aop.xml 文件:

<aspectj> 
    <aspects> 
       <aspect name="com.fooMyNewNoneSpringAspect"/>
    </aspects> 
</aspectj>

感谢 STS 论坛上的 Andy Clement 的发现和解决方法。他将提出 JIRA 问题,以进一步解决 maven 插件中的这一缺点。

于 2012-04-20T14:35:21.510 回答
0

您可能会发现自己处于相同情况的一种可能方式:您使用了new运算符,而不是让 Spring注入您的服务。

请记住,我们不是在这里进行 AspectJ 编译时编织。不,我们使用的是 Spring AOP 代理,因此 Spring 必须实例化对象并使用代理对其进行修饰。如果您像我一样愚蠢,并且在您的测试中创建了一个新服务,那么您将不会获得任何 AOP。

更有理由使用 AspectJ 进行编译时编织并跳过 Spring AOP 的所有缺点,例如运行时编织启动延迟、无法编织非公共和非最终类。

于 2013-06-04T18:42:27.540 回答