Skip to content

Commit

Permalink
Test status quo for 'after' advice invocation order
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Jun 4, 2020
1 parent 70b3de4 commit 63e8609
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 25 deletions.
@@ -0,0 +1,107 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.aop.config;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

/**
* Integration tests for advice invocation order for advice configured via the
* AOP namespace.
*
* @author Sam Brannen
* @since 5.0.18
* @see org.springframework.aop.framework.autoproxy.AspectJAutoProxyAdviceOrderIntegrationTests
*/
class AopNamespaceHandlerAdviceOrderIntegrationTests {

@Nested
@SpringJUnitConfig(locations = "AopNamespaceHandlerAdviceOrderIntegrationTests-afterFirst.xml")
@DirtiesContext
class AfterAdviceFirstTests {

@Test
void afterAdviceIsInvokedFirst(@Autowired Echo echo, @Autowired EchoAspect aspect) throws Exception {
assertThat(aspect.invocations).isEmpty();
assertThat(echo.echo(42)).isEqualTo(42);
assertThat(aspect.invocations).containsExactly("after", "after returning");

aspect.invocations.clear();
assertThatExceptionOfType(Exception.class).isThrownBy(() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("after", "after throwing");
}
}

@Nested
@SpringJUnitConfig(locations = "AopNamespaceHandlerAdviceOrderIntegrationTests-afterLast.xml")
@DirtiesContext
class AfterAdviceLastTests {

@Test
void afterAdviceIsInvokedLast(@Autowired Echo echo, @Autowired EchoAspect aspect) throws Exception {
assertThat(aspect.invocations).isEmpty();
assertThat(echo.echo(42)).isEqualTo(42);
assertThat(aspect.invocations).containsExactly("after returning", "after");

aspect.invocations.clear();
assertThatExceptionOfType(Exception.class).isThrownBy(() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("after throwing", "after");
}
}


static class Echo {

Object echo(Object obj) throws Exception {
if (obj instanceof Exception) {
throw (Exception) obj;
}
return obj;
}
}

static class EchoAspect {

List<String> invocations = new ArrayList<>();

void echo() {
}

void succeeded() {
invocations.add("after returning");
}

void failed() {
invocations.add("after throwing");
}

void after() {
invocations.add("after");
}
}

}
@@ -0,0 +1,186 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.aop.framework.autoproxy;

import java.util.ArrayList;
import java.util.List;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

/**
* Integration tests for advice invocation order for advice configured via
* AspectJ auto-proxy support.
*
* @author Sam Brannen
* @since 5.0.18
* @see org.springframework.aop.config.AopNamespaceHandlerAdviceOrderIntegrationTests
*/
class AspectJAutoProxyAdviceOrderIntegrationTests {

@Nested
@SpringJUnitConfig(AfterAdviceFirstConfig.class)
@DirtiesContext
class AfterAdviceFirstTests {

@Test
void afterAdviceIsInvokedFirst(@Autowired Echo echo, @Autowired AfterAdviceFirstAspect aspect) throws Exception {
assertThat(aspect.invocations).isEmpty();
assertThat(echo.echo(42)).isEqualTo(42);
assertThat(aspect.invocations).containsExactly("after", "after returning");

aspect.invocations.clear();
assertThatExceptionOfType(Exception.class).isThrownBy(
() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("after", "after throwing");
}
}


@Nested
@SpringJUnitConfig(AfterAdviceLastConfig.class)
@DirtiesContext
class AfterAdviceLastTests {

@Test
void afterAdviceIsNotInvokedLast(@Autowired Echo echo, @Autowired AfterAdviceLastAspect aspect) throws Exception {
assertThat(aspect.invocations).isEmpty();
assertThat(echo.echo(42)).isEqualTo(42);
// On Java versions prior to JDK 7, we would expect the @After advice to be invoked last.
assertThat(aspect.invocations).containsExactly("after", "after returning");

aspect.invocations.clear();
assertThatExceptionOfType(Exception.class).isThrownBy(
() -> echo.echo(new Exception()));
// On Java versions prior to JDK 7, we would expect the @After advice to be invoked last.
assertThat(aspect.invocations).containsExactly("after", "after throwing");
}
}


@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
static class AfterAdviceFirstConfig {

@Bean
AfterAdviceFirstAspect echoAspect() {
return new AfterAdviceFirstAspect();
}

@Bean
Echo echo() {
return new Echo();
}
}

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
static class AfterAdviceLastConfig {

@Bean
AfterAdviceLastAspect echoAspect() {
return new AfterAdviceLastAspect();
}

@Bean
Echo echo() {
return new Echo();
}
}

static class Echo {

Object echo(Object obj) throws Exception {
if (obj instanceof Exception) {
throw (Exception) obj;
}
return obj;
}
}

/**
* {@link After @After} advice declared as first <em>after</em> method in source code.
*/
@Aspect
static class AfterAdviceFirstAspect {

List<String> invocations = new ArrayList<>();

@Pointcut("execution(* echo(*))")
void echo() {
}

@After("echo()")
void after() {
invocations.add("after");
}

@AfterReturning("echo()")
void succeeded() {
invocations.add("after returning");
}

@AfterThrowing("echo()")
void failed() {
invocations.add("after throwing");
}
}

/**
* {@link After @After} advice declared as last <em>after</em> method in source code.
*/
@Aspect
static class AfterAdviceLastAspect {

List<String> invocations = new ArrayList<>();

@Pointcut("execution(* echo(*))")
void echo() {
}

@AfterReturning("echo()")
void succeeded() {
invocations.add("after returning");
}

@AfterThrowing("echo()")
void failed() {
invocations.add("after throwing");
}

@After("echo()")
void after() {
invocations.add("after");
}
}

}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="echo" class="org.springframework.aop.config.AopNamespaceHandlerAdviceOrderIntegrationTests$Echo"/>

<bean id="echoAspect" class="org.springframework.aop.config.AopNamespaceHandlerAdviceOrderIntegrationTests$EchoAspect" />

<aop:config>
<aop:aspect id="echoAdvice" ref="echoAspect">
<aop:pointcut id="echoMethod" expression="execution(* echo(*))" />
<aop:after method="after" pointcut-ref="echoMethod" />
<aop:after-throwing method="failed" pointcut-ref="echoMethod" />
<aop:after-returning method="succeeded" pointcut-ref="echoMethod" />
</aop:aspect>
</aop:config>

</beans>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="echo" class="org.springframework.aop.config.AopNamespaceHandlerAdviceOrderIntegrationTests$Echo"/>

<bean id="echoAspect" class="org.springframework.aop.config.AopNamespaceHandlerAdviceOrderIntegrationTests$EchoAspect" />

<aop:config>
<aop:aspect id="echoAdvice" ref="echoAspect">
<aop:pointcut id="echoMethod" expression="execution(* echo(*))" />
<aop:after-returning method="succeeded" pointcut-ref="echoMethod" />
<aop:after-throwing method="failed" pointcut-ref="echoMethod" />
<aop:after method="after" pointcut-ref="echoMethod" />
</aop:aspect>
</aop:config>

</beans>

0 comments on commit 63e8609

Please sign in to comment.