【问题标题】:Spring AOP - @Pointcut: @Before advice for @Test methods does not workSpring AOP - @Pointcut:@Test 方法的 @Before 建议不起作用
【发布时间】:2016-09-21 14:54:57
【问题描述】:

我正在与:

  • Spring 框架 4.3.2
  • AspectJ 1.8.9
  • JUnit
  • 分级

该项目基于多模块。

src/main/java (main) 我有一些@Aspect 类,它们按预期工作。我可以通过运行时和测试确认它

现在我需要 JUnit 通过日志显示执行的 @Test 方法名称

因此在src/test/java (test) 我有以下内容:

class TestPointcut {

    @Pointcut("execution(@org.junit.Test * *())")                         
    public void testPointcut(){}

}

@Aspect
@Component
public class TestAspect {

    private static final Logger logger = LoggerFactory.getLogger(TestAspect.class.getSimpleName());

    @Before(value="TestPointcut.testPointcut()")
    public void beforeAdviceTest(JoinPoint joinPoint){
        logger.info("beforeAdviceTest - Test: {} - @Test: {}", joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName() );
    }

}

观察第二个类有@Aspect@Component因此它被Spring识别

注意:我可以确认如果我写错了 @Pointcut 语法或表达式,我会出错。

问题是当我执行 @Test 方法时,对于 TestAspect 类,@Before 建议永远不会起作用。

我在 Google 进行了一项研究,发现 @Pointcut("execution(@org.junit.Test * *())") 模式是正确的。 即使我使用更明确的方式,例如:@Pointcut(value="execution(public void com.manuel.jordan.controller.persona.*Test.*Test())"),它也不起作用。

考虑我有以下Gradle

project(':web-27-rest') {
    description 'Web - Rest'
    dependencies {
       compile project(':web-27-service-api')

       testRuntime project(':web-27-aop')
       testRuntime project(':web-27-aop').sourceSets.test.output

有什么遗漏或错误?

阿尔法

一种测试类是:

  • 服务器端使用@Parameters@ClassRule + @Rule

因此:

@RunWith(Parameterized.class)
@ContextConfiguration(classes={RootApplicationContext.class})
@Transactional
public class PersonaServiceImplTest {

    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE= new SpringClassRule();

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

    @Autowired
    private PersonaService personaServiceImpl;

    ...

    @Parameters
    public static Collection<Persona[]> data() {
     .....
        });
    }

    ...

    @Test
    @Sql(scripts={"classpath:....-script.sql"})
    public void saveOneTest(){
    ....
    }

其他是:

  • Web 端使用 (@WebAppConfiguration) 和:
    • @Parameters@ClassRule + @Rule
    • 没有@Parameters@ClassRule + @Rule

因此(在第二种方法之下):

@Transactional
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
public class PersonaDeleteOneControllerTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    private ResultActions resultActions;

    ...

    @BeforeClass
    public static void setUp_(){
      ...
    }

    @Before
    public void setUp(){
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void deleteOneHtmlGetTest() throws Exception {

【问题讨论】:

  • 你能在这里提供你的测试类代码吗?
  • 您好 @SergeyBespalov 代码已更新,请参阅 Alpha 部分。谢谢!
  • 你可以尝试为你的测试类添加@EnableAspectJAutoProxy(proxyTargetClass=true)注解。
  • 你好@SergeyBespalov,它不起作用。似乎“缺少”其他东西
  • 您使用的是哪种编织方式?是spring aop代理吗?

标签: junit aop aspectj spring-aop


【解决方案1】:

JUnit 实例化您的测试类。因此,不涉及 Spring,因此无法将 AOP 建议应用于测试实例。

正如 Sergey Bespalov 所提到的,将 AspectJ 建议应用于您的测试实例的唯一方法是使用编译时或加载时编织。请注意,这不会在 Spring 中配置。 Spring 可用于为 Spring 管理的 bean 配置 AOP,但测试实例由测试框架(即您的场景中的 JUnit 4)管理。

但是,对于使用 Spring TestContext Framework 的测试,我不推荐使用 AspectJ。相反,最好的解决方案是实现一个自定义的TestExecutionListener 来执行日志记录。然后,您可以通过@TestExecutionListeners 显式注册TestExecutionListener,或者为您的整个套件自动获取它。对于后者,请参阅 Spring 参考手册的测试章节中关于自动发现的讨论。

问候,

Sam(Spring TestContext 框架的作者

【讨论】:

  • 非常感谢 Sam 的宝贵解释。我将对您的建议进行研究。关于JUnit instantiates your test class. Thus, Spring is not involved and therefore cannot apply AOP advice to the test instance。似乎我错了当我看到JUnit 类和@ContextConfiguration 时,Spring 100% 参与其中。我对那段感到困惑。请以最好的意图尝试扩展更多的想法。
  • 实例化你的测试类的总是运行器。因此,如果您使用的是 Parameterized 运行器,那么实例化您的测试类(而不是 Spring)的是该运行器。如果您使用SpringJUnit4ClassRunner,您可能认为 Spring 实例化您的测试类;然而,SpringJUnit4ClassRunner 实际上扩展了 JUnit 的 BlockJUnit4ClassRunner,并且物理实例化测试实例的是 BlockJUnit4ClassRunner(同样不是 Spring)。
  • 谢谢山姆。但请记住,即使@RunWith(SpringJUnit4ClassRunner.class) 我也遇到了同样的问题
  • 我编辑了我的评论以解决SpringJUnit4ClassRunner
  • 具体来说,JUnit 4 正是在这种方法中实例化了您的测试类:org.junit.runners.BlockJUnit4ClassRunner.createTest()
【解决方案2】:

您可以使用 AspectJ Compile 或 Load time weaving 作为 spring-aop 代理的替代方案。在这种方法中,您将不依赖于 spring 上下文复杂的逻辑来在代码中应用建议。方面代码只会在编译或类加载阶段被内联。 下面的示例展示了如何启用 AspectJ 编译时间编织:

pom.xml

此 Maven 配置启用 AspectJ 编译器,该编译器可对您的类进行字节码后处理。

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
    </dependency>
</dependencies>
<plugins>
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.6</version>
        <configuration>
            <showWeaveInfo>true</showWeaveInfo>
            <source>${java.source}</source>
            <target>${java.target}</target>
            <complianceLevel>${java.target}</complianceLevel>
            <encoding>UTF-8</encoding>
            <verbose>false</verbose>
            <XnoInline>false</XnoInline>
        </configuration>
        <executions>
            <execution>
                <id>aspectj-compile</id>
                <goals>
                    <goal>compile</goal>
                </goals>
            </execution>
            <execution>
                <id>aspectj-compile-test</id>
                <goals>
                    <goal>test-compile</goal>
                </goals>
            </execution>
        </executions>
        <dependencies>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>${aspectj.version}</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjtools</artifactId>
                <version>${aspectj.version}</version>
            </dependency>
        </dependencies>
    </plugin>
</plugins>

applicationContext.xml

您可能还需要将方面实例添加到 Spring Application Context 以进行依赖注入。

<bean class="TestAspect" factory-method="aspectOf"/>

【讨论】:

  • 非常有趣,想知道Gradle 的等价物是什么。让我做个研究。
  • 你好 Sergey,考虑一下 Sam 的回答。也非常感谢您的宝贵支持。
  • Sam 的回答解释了这个问题,但奇怪的是为什么测试实例不是 ApplicationContext 的一部分。我觉得这可能是一个错误。我建议您尝试 AspectJ 编译器,它可以解决您在网站上提出的这个问题和其他 AOP 问题。我在所有项目中都使用它,并且可以说它很好。
  • 是的,but it is strange why Test instance is not part of ApplicationContext 我也很困惑,但是经过分析:我认为这不是一个错误,因为即使你看到带有一组 Spring 注释的 Test 类,它确实不包含@Configuration@Component@Service 等。因此它既不是由 Spring 创建也不是由 Spring 处理的。它是由 JUnit 创建的,然后由于 Test 类中声明的当前注释,一些 bean 是“可用的”。我通过 Sam 的建议完成了我的方法。
  • `我建议你试试 AspectJ 编译器,它会解决这个问题以及你在网站上提出的其他 AOP 问题`让我们看看。但我已经确认其中一个是一种错误,我已经在另一篇帖子中给你写了一条关于JIRA 的评论。不确定,但你能在这里帮忙stackoverflow.com/questions/39708410/… 谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
相关资源
最近更新 更多