preface
In the last article, we talked about how to integrate custom SPI with sentinel to realize fuse current limiting. In the process of implementing integration testing, an interesting exception Java lang.reflect.Undeclaredtowableexception. At that time, a global exception was caught in the code layer. The example is as follows
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e) {
String msg = e.getMessage();
return AjaxResult.error(msg,500);
}
@ExceptionHandler(BlockException.class)
public AjaxResult handleBlockException(BlockException e) {
String msg = e.getMessage();
return AjaxResult.error(msg,429);
}
}
Originally, it was expected that blockexception would be captured when current limiting was triggered, and then a layer of rendering would be encapsulated. Unexpectedly, blockexception could not be captured alive and dead.
Troubleshooting
Through debugging, it is found that the problem is caused by the JDK dynamic agent. Later, I found some information. Later, I found a paragraph in the official API document
The main idea is that if the invoke method of the call handler of the proxy instance throws a checked exception (which cannot be assigned to runtimeException or throwable of error), and the exception cannot be assigned to any exception class declared by the throws sub Office of the method, the method call on the proxy instance throws an undeclaredtrowableexception.
In this passage, we can analyze the following scenarios
1. There is no declared exception on the real instance method, and the checked exception is thrown when the proxy instance is called
2. The real instance method declares a non checked exception, and the proxy instance throws a checked exception when calling
Solution
Method 1: the real instance also declares the detected exception
Example:
public class SqlServerDialect implements SqlDialect {
@Override
public String dialect() throws Exception{
return "sqlserver";
}
Method 2: JDK can capture the invoke of dynamic agent, and can customize the exception thrown at the same time
Example:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
CircuitBreakerInvocation invocation = new CircuitBreakerInvocation(target,method,args);
try {
return new CircuitBreakerInvoker().proceed(invocation);
} catch (Throwable e) {
throw new CircuitBreakerException(429,"too many request");
}
}
Method 3: catch the invocationtargetexception exception and throw the real exception
The reason why we need invocationtargetexception is that our custom exception will be wrapped by invocationtargetexception
Example
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
CircuitBreakerInvocation invocation = new CircuitBreakerInvocation(target,method,args);
try {
return new CircuitBreakerInvoker().proceed(invocation);
//Use InvocationTargetException is java.lang.reflect.UndeclaredThrowableException
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
summary
If it is a component implemented by ourselves, we recommend directly using scheme 3, that is, capturing invocationtargetexception exceptions.
If the component is implemented by a third party, the recommended scheme 1 is to declare an exception in the called instance method. For example, when using springcloud Alibaba sentinel, there is a probability that an undeclaredtowableexception exception will occur, because it is also based on a dynamic agent, and the blockexception thrown by it is also a checked exception. Examples are as follows
public class SqlServerDialect implements SqlDialect {
@Override
public String dialect() throws BlockException{
return "sqlserver";
}
If you use a third-party component instead of scheme 1, you can also add a layer of agent on the basis of the third-party component, or intercept the third-party component.
Similar Posts:
- [Solved] IllegalArgumentException: object is not an instance of declaring class
- [Solved] Java Call Error: java.lang.IllegalArgumentException: wrong number of arguments
- Java error: No enclosing instance of type E is accessible. Must qualify the allocation with an enclosing
- [Two Solutions] non-static method xxx() cannot be referenced from a static context
- Access permission problem caused by object. Clone() method
- 【Java】No enclosing instance of type XXX is accessible. Must qualify the allocation with an enclos…
- Convert Object to List>, avoiding Unchecked cast: ‘java.lang.Object’ to ‘java.util.List
- [Solved] java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”
- [Solved] Java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double
- [Solved] NoSuchMethodError: org.springframework.boot.web.servlet.error.ErrorController.getErrorPath