Spring AOP - @Pointcut:@Test 方法的@Before 建议不起作用

Spring AOP - @Pointcut: @Before advice for @Test methods does not work

我正在与:

该项目基于多模块。

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

现在我需要 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() );
    }

}

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

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

问题是当我执行我的 @Test 方法时,对于 TestAspect class @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

缺少什么或错了什么?

阿尔法:

一种测试class是:

因此:

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

其他是:

因此(下面第二种方法):

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

您可以使用 AspectJ 编译或加载时编织来替代 spring-aop 代理。在这种方法中,您不会依赖 spring 上下文复杂逻辑来在代码中应用建议。方面代码将在编译或 class 加载阶段内联。 下面的示例显示了如何启用 AspectJ 编译时编织:

pom.xml

此 Maven 配置启用 AspectJ 编译器,使字节码 post 处理您的 classes。

<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 应用程序上下文以进行依赖注入。

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

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

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

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

此致,

Sam(Spring TestContext Framework 的作者)