Spring AOP - @Pointcut:@Test 方法的@Before 建议不起作用
Spring AOP - @Pointcut: @Before advice for @Test methods does not work
我正在与:
- Spring 框架 4.3.2
- AspectJ 1.8.9
- JUnit
- Gradle
该项目基于多模块。
在 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是:
- 服务器端使用
@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 {
您可以使用 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 的作者)
我正在与:
- Spring 框架 4.3.2
- AspectJ 1.8.9
- JUnit
- Gradle
该项目基于多模块。
在 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是:
- 服务器端使用
@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 {
您可以使用 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 的作者)