diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml
index 69de2d66fd..74b6f48b46 100644
--- a/spring-aop/pom.xml
+++ b/spring-aop/pom.xml
@@ -18,6 +18,11 @@
org.springframework.boot
spring-boot-starter-aop
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/SomeCheckedException.java b/spring-aop/src/test/java/com/baeldung/undeclared/SomeCheckedException.java
new file mode 100644
index 0000000000..bbb28681fa
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/SomeCheckedException.java
@@ -0,0 +1,7 @@
+package com.baeldung.undeclared;
+
+public class SomeCheckedException extends Exception {
+ public SomeCheckedException(String message) {
+ super(message);
+ }
+}
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/ThrowUndeclared.java b/spring-aop/src/test/java/com/baeldung/undeclared/ThrowUndeclared.java
new file mode 100644
index 0000000000..dd6436e722
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/ThrowUndeclared.java
@@ -0,0 +1,11 @@
+package com.baeldung.undeclared;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ThrowUndeclared {
+}
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredApplication.java b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredApplication.java
new file mode 100644
index 0000000000..37a0ec89b5
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.undeclared;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class UndeclaredApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(UndeclaredApplication.class, args);
+ }
+}
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredAspect.java b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredAspect.java
new file mode 100644
index 0000000000..076fdff66c
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredAspect.java
@@ -0,0 +1,16 @@
+package com.baeldung.undeclared;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+public class UndeclaredAspect {
+
+ @Around("@annotation(undeclared)")
+ public Object advise(ProceedingJoinPoint pjp, ThrowUndeclared undeclared) throws Throwable {
+ throw new SomeCheckedException("AOP Checked Exception");
+ }
+}
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredService.java b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredService.java
new file mode 100644
index 0000000000..6cc16c1ceb
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredService.java
@@ -0,0 +1,10 @@
+package com.baeldung.undeclared;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class UndeclaredService {
+
+ @ThrowUndeclared
+ public void doSomething() {}
+}
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java
new file mode 100644
index 0000000000..e64a45d418
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionIntegrationTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.undeclared;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.lang.reflect.UndeclaredThrowableException;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = UndeclaredApplication.class)
+public class UndeclaredThrowableExceptionIntegrationTest {
+
+ @Autowired private UndeclaredService service;
+
+ @Test
+ public void givenAnAspect_whenCallingAdvisedMethod_thenShouldWrapTheException() {
+ assertThatThrownBy(service::doSomething)
+ .isInstanceOf(UndeclaredThrowableException.class)
+ .hasCauseInstanceOf(SomeCheckedException.class);
+ }
+}
diff --git a/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java
new file mode 100644
index 0000000000..464239d12d
--- /dev/null
+++ b/spring-aop/src/test/java/com/baeldung/undeclared/UndeclaredThrowableExceptionUnitTest.java
@@ -0,0 +1,48 @@
+package com.baeldung.undeclared;
+
+import org.junit.Test;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class UndeclaredThrowableExceptionUnitTest {
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void givenAProxy_whenProxyUndeclaredThrowsCheckedException_thenShouldBeWrapped() {
+ ClassLoader classLoader = getClass().getClassLoader();
+ InvocationHandler invocationHandler = new ExceptionalInvocationHandler();
+ List proxy = (List) Proxy.newProxyInstance(classLoader, new Class[] { List.class }, invocationHandler);
+
+ assertThatThrownBy(proxy::size)
+ .isInstanceOf(UndeclaredThrowableException.class)
+ .hasCauseInstanceOf(SomeCheckedException.class);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void givenAProxy_whenProxyThrowsUncheckedException_thenShouldBeThrownAsIs() {
+ ClassLoader classLoader = getClass().getClassLoader();
+ InvocationHandler invocationHandler = new ExceptionalInvocationHandler();
+ List proxy = (List) Proxy.newProxyInstance(classLoader, new Class[] { List.class }, invocationHandler);
+
+ assertThatThrownBy(proxy::isEmpty).isInstanceOf(RuntimeException.class);
+ }
+
+ private static class ExceptionalInvocationHandler implements InvocationHandler {
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if ("size".equals(method.getName())) {
+ throw new SomeCheckedException("Always fails");
+ }
+
+ throw new RuntimeException();
+ }
+ }
+}