Spring + lombok + @SneakyThrows

白昼怎懂夜的黑 提交于 2021-02-07 06:10:28

问题


I'm using @SneakyThrows Lombok feature in my SpringBoot project. I have problems with this feature when CGLIB proxies implementation it throws java.lang.Exception: Unexpected exception, expected but was<java.lang.reflect.UndeclaredThrowableException>.
Can it be fixed somehow ?


Providing examples.

There is interface and two implementations.

public interface SneakyThrowsExample {
    void testSneakyThrows();
}

Simple implementation

import lombok.SneakyThrows;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component(value = "simpleSneakyThrowsExample")
public class SimpleSneakyThrowsExample implements SneakyThrowsExample {

    @Override
    @SneakyThrows
    public void testSneakyThrows() {
        throw new IOException();
    }
}

And @Transactional implementations. CGLIB will proxy this implementation.

import lombok.SneakyThrows;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

@Component(value = "transactionalSneakyThrowsExample")
public class TransactionalSneakyThrowsExample implements SneakyThrowsExample {

    @Override
    @SneakyThrows
    @Transactional
    public void testSneakyThrows() {
        throw new IOException();
    }
}

Create @SpringBootTest test and inject these 2 implementation

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DefaultSneakyThrowsExampleTest {

    @Autowired
    @Qualifier(value = "transactionalSneakyThrowsExample")
    SneakyThrowsExample transactionalSneakyThrowsExample;

    @Autowired
    @Qualifier(value = "simpleSneakyThrowsExample")
    SneakyThrowsExample simpleSneakyThrowsExample;

    @Test(expected = IOException.class)
    public void testSneakyThrowsSimple() throws Exception {
        this.simpleSneakyThrowsExample.testSneakyThrows();
    }

    @Test(expected = IOException.class)
    public void testSneakyThrowsTransactional() throws Exception {
        this.transactionalSneakyThrowsExample.testSneakyThrows();
    }
}

Test testSneakyThrowsTransactional fails with error

java.lang.Exception: Unexpected exception, expected<java.io.IOException> but was<java.lang.reflect.UndeclaredThrowableException>

    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.reflect.UndeclaredThrowableException
    at fine.project.TransactionalSneakyThrowsExample$$EnhancerBySpringCGLIB$$57df642e.testSneakyThrows(<generated>)
    at fine.project.DefaultSneakyThrowsExampleTest.testSneakyThrowsTransactional(DefaultSneakyThrowsExampleTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19)
    ... 20 more
Caused by: java.io.IOException
    at fine.project.TransactionalSneakyThrowsExample.testSneakyThrows(TransactionalSneakyThrowsExample.java:21)
    at fine.project.TransactionalSneakyThrowsExample$$FastClassBySpringCGLIB$$e5429d83.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
    ... 31 more

回答1:


When you use @Transactional then Spring will create a proxy for your bean via AOP proxies - Spring Framework’s declarative transaction

UndeclaredThrowableException cause:

Thrown by a method invocation on a proxy instance if its invocation handler's invoke method throws a checked exception (a Throwable that is not assignable to RuntimeException or Error) that is not assignable to any of the exception types declared in the throws clause of the method that was invoked on the proxy instance and dispatched to the invocation handler.

Lombock @SneakyThrows:

Can be used to sneakily throw checked exceptions without actually declaring this in your method's throws clause

It means that your TransactionalSneakyThrowsExample.testSneakyThrows() throws checked exception (that undeclared in the throws in method signature) it's illegal behavior when instance wrapped in proxy

In this case you can change expected exception to the Exception.class:

    @Test(expected = Exception.class)
        public void testSneakyThrowsTransactional() throws Exception {
            this.transactionalSneakyThrowsExample.testSneakyThrows();
    }

or you can use ExpectedException.expectCause() to cheack IOException.class in your test, take a look at JUnit expect a wrapped exception



来源:https://stackoverflow.com/questions/47017259/spring-lombok-sneakythrows

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!