I have the following code
public Object handlePermission(ProceedingJoinPoint joinPoint, RequirePermission permission) throws AccessException, Throwable {
System.out.println("Permission = " + permission.value());
if (user.hasPermission(permission.value())) {
System.out.println("Permission granted ");
return joinPoint.proceed();
} else {
System.out.println("No Permission");
throw new AccessException("Current user does not have required permission");
}
}
When I use a user that does not have permissions, I get java.lang.reflect.UndeclaredThrowableException
instead of AccessException
.
asked Mar 30, 2011 at 17:53
user373201user373201
10.9k34 gold badges112 silver badges168 bronze badges
1
AccessException
is a checked exception, but it was thrown from the method that doesn’t declare it in its throws
clause (actually — from the aspect intercepting that method). It’s an abnormal condition in Java, so your exception is wrapped with UndeclaredThrowableException
, which is unchecked.
To get your exception as is, you can either declare it in the throws
clause of the method being intercepted by your aspect, or use another unchecked exception (i.e. a subclass of RuntimeException
) instead of AccessException
.
answered Mar 30, 2011 at 18:12
6
I have the following code
public Object handlePermission(ProceedingJoinPoint joinPoint, RequirePermission permission) throws AccessException, Throwable {
System.out.println("Permission = " + permission.value());
if (user.hasPermission(permission.value())) {
System.out.println("Permission granted ");
return joinPoint.proceed();
} else {
System.out.println("No Permission");
throw new AccessException("Current user does not have required permission");
}
}
When I use a user that does not have permissions, I get java.lang.reflect.UndeclaredThrowableException
instead of AccessException
.
asked Mar 30, 2011 at 17:53
user373201user373201
10.9k34 gold badges112 silver badges168 bronze badges
1
AccessException
is a checked exception, but it was thrown from the method that doesn’t declare it in its throws
clause (actually — from the aspect intercepting that method). It’s an abnormal condition in Java, so your exception is wrapped with UndeclaredThrowableException
, which is unchecked.
To get your exception as is, you can either declare it in the throws
clause of the method being intercepted by your aspect, or use another unchecked exception (i.e. a subclass of RuntimeException
) instead of AccessException
.
answered Mar 30, 2011 at 18:12
6
Предисловие
Я начал писать веб-интерфейс в последние два дня, так как бэк-энд проекта — мой единственный человек, писать его медленно. , Обнаружено много странных проблем, и в основном их может решить только один человек. Сегодня днем надолго задержался на этом вопросе. Теперь у меня все еще болит мозг.
проблема
Бизнесу необходимо реализовать функцию перехвата параметров запроса. Проверьте, включен ли токен. Проект реализован на базе springmvc, здесь естественно использовать технологию пружинного аспекта. Перехватите все запросы контроллера, а затем проверьте, передан ли параметр токена. Если он не переносится, генерируется настраиваемое исключение. Затем вызовите единый класс обработки исключений для его обработки.
Участвуют следующие классы:
package com.insigmaunited.lightai.base;
import com.insigmaunited.lightai.exception.TokenEmptyException;
import com.insigmaunited.lightai.result.Response;
import com.insigmaunited.lightai.util.StringUtil;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* Разрешение на перехват АОП
* @author Administrator
*
*/
@Component
@Aspect
public class PermissionAop {
private final Logger logger = LoggerFactory.getLogger(PermissionAop.class);
// Определяем pointcut
@Pointcut("execution(* com.insigmaunited.lightai.controller.*Controller.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void before() throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
System.out.println(url);
System.out.println(method);
System.out.println(uri);
System.out.println(queryString);
if (StringUtil.isNotEmpty(queryString) && queryString.indexOf("token") != -1 ){
}else{
выбросить новое TokenEmptyException («токен отсутствует»);
}
}
}
Пользовательский класс исключения
package com.insigmaunited.lightai.exception;
public class TokenEmptyException extends Exception {
public TokenEmptyException(String message) {
super(message);
}
}
Единый класс обработки исключений
package com.insigmaunited.lightai.exception;
import com.insigmaunited.lightai.base.BaseException;
import com.insigmaunited.lightai.result.Response;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.insigmaunited.lightai.exception.TokenEmptyException;
import javax.xml.bind.ValidationException;
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice extends BaseException {
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ValidationException.class)
public Response handleValidationException(ValidationException e) {
logger.error («Ошибка проверки параметра», e);
return new Response().failure("validation_exception");
}
/**
* 405 - Method Not Allowed
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Response handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
logger.error («Текущий метод запроса не поддерживается», д);
return new Response().failure("request_method_not_supported");
}
/**
* 415 - Unsupported Media Type
*/
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Response handleHttpMediaTypeNotSupportedException(Exception e) {
logger.error («Текущий тип носителя не поддерживается», д);
return new Response().failure("content_type_not_supported");
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(TokenEmptyException.class)
public Response handleTokenEmptyException(Exception e) {
logger.error («отсутствует параметр токена», e);
return new Response (). failure («параметр токена отсутствует»);
}
/**
* 500 - Internal Server Error
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Response handleException(Exception e) {
logger.error ("Ненормальная работа службы", e);
вернуть новый Response (). failure («Ненормальная работа службы»);
}
}
Интерфейс вызывается в это время, и ожидается, что он должен
{
"success": false,
"message": "Отсутствует параметр токена",
"data": null
}
На самом деле возвращено
{
"success": false,
"message": "Служба работает ненормально",
"data": null
}
Ошибка консоли выглядит следующим образом:
http://localhost:8080/user/3/profile
GET
/user/3/profile
null
[ОШИБКА] 2017-12-08 18:29:19-com.insigmaunited.lightai.exception.ExceptionAdvice-ExceptionAdvice.java (63) - Ненормальная операция службы
java.lang.reflect.UndeclaredThrowableException
at com.insigmaunited.lightai.controller.UserController$$EnhancerBySpringCGLIB$$e4eb8ece.profile(<generated>)
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.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:475)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:498)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:796)
at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.doRun(Nio2Endpoint.java:1688)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.net.AbstractEndpoint.processSocket(AbstractEndpoint.java:914)
at org.apache.tomcat.util.net.Nio2Endpoint$Nio2SocketWrapper$4.completed(Nio2Endpoint.java:536)
at org.apache.tomcat.util.net.Nio2Endpoint$Nio2SocketWrapper$4.completed(Nio2Endpoint.java:514)
at sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:126)
at sun.nio.ch.Invoker$2.run(Invoker.java:218)
at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Вызвано: com.insigmaunited.lightai.exception.TokenEmptyException: токен отсутствует
at com.insigmaunited.lightai.base.PermissionAop.before(PermissionAop.java:53)
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.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:620)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:602)
at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:41)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
... 45 more
Это странно. Почему созданное мной настраиваемое исключение превратилось в UndeclaredThrowableException, которое не могло быть обработано единым обработчиком исключений.
причина
Через поисковик наконец нашел причину:
Наш класс обработки исключений на самом деле является реализацией динамического прокси.
Если исключение является проверенным исключением и не объявлено в интерфейсе динамического прокси, оно будет заключено в оболочку UndeclaredThrowableException.
Определенное нами настраиваемое исключение определяется как проверенное исключение, что приводит к его упаковке как UndeclaredThrowableException.
Официальное объяснение документа
Аномальное различие
решить
Узнать причину очень просто. Либо вызовите непроверенное исключение java.lang.RuntimeException или java.lang.Error, либо интерфейс должен объявить исключение.
Здесь вы можете изменить настраиваемое исключение как исключение времени выполнения.
package com.insigmaunited.lightai.exception;
public class TokenEmptyException extends RuntimeException {
public TokenEmptyException(String message) {
super(message);
}
}
урок
1. По возможности определяйте настраиваемые исключения как исключения времени выполнения.
2. Понятие исключения неясно. Фундамент не прочный.
Автор оригинала: Mona Mohamadinia.
1. Обзор
В этом уроке мы рассмотрим, что заставляет Java выбрасывать экземпляр UndeclaredThrowableException exception.
Во-первых, мы начнем с небольшой теории. Затем мы попытаемся лучше понять природу этого исключения с помощью двух реальных примеров.
Теоретически говоря, Java создаст экземпляр UndeclaredThrowableException , когда мы попытаемся создать необъявленное проверенное исключение . То есть мы не объявляли проверенное исключение в предложении throws , но мы выбрасываем это исключение в теле метода.
Можно возразить, что это невозможно, так как компилятор Java предотвращает это с помощью ошибки компиляции. Например, если мы попытаемся скомпилировать:
public void undeclared() { throw new IOException(); }
Компилятор Java завершает работу с сообщением:
java: unreported exception java.io.IOException; must be caught or declared to be thrown
Даже если выбрасывание необъявленных проверенных исключений может не произойти во время компиляции, это все еще возможно во время выполнения. Например, давайте рассмотрим прокси-сервер времени выполнения, перехватывающий метод, который не создает никаких исключений:
public void save(Object data) { // omitted }
Если прокси-сервер сам создает проверенное исключение, с точки зрения вызывающего, метод save создает это проверенное исключение. Вызывающий, вероятно, ничего не знает об этом прокси-сервере и будет обвинять save в этом исключении.
В таких обстоятельствах Java обернет фактическое проверенное исключение в исключение UndeclaredThrowableException и вместо этого выбросит исключение UndeclaredThrowableException . Стоит отметить, что UndeclaredThrowableException само по себе является непроверенным исключением.
Теперь, когда мы достаточно знаем об этой теории, давайте рассмотрим несколько реальных примеров.
3. Динамический прокси-сервер Java
В качестве нашего первого примера давайте создадим runtime proxy для java.util.Перечислите интерфейс и перехватите вызовы его методов. Во-первых, мы должны реализовать интерфейс InvocationHandler и поместить туда дополнительную логику:
public 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(); } } public class SomeCheckedException extends Exception { public SomeCheckedException(String message) { super(message); } }
Этот прокси-сервер выдает проверенное исключение, если проксируемый метод имеет размер . В противном случае он вызовет непроверенное исключение.
Давайте посмотрим, как Java справляется с обеими ситуациями. Сначала мы вызовем метод List.size() :
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);
Как показано выше, мы создаем прокси-сервер для интерфейса List и вызываем на нем метод size . Прокси, в свою очередь, перехватывает вызов и выдает проверенное исключение. Затем Java обертывает это проверенное исключение внутри экземпляра UndeclaredThrowableException. Это происходит потому, что мы каким-то образом выбрасываем проверенное исключение, не объявляя его в объявлении метода.
Если мы вызовем любой другой метод в интерфейсе List :
assertThatThrownBy(proxy::isEmpty).isInstanceOf(RuntimeException.class);
Поскольку прокси-сервер выдает непроверенное исключение, Java позволяет исключению распространяться как есть.
4. Пружинный аспект
То же самое происходит, когда мы выбрасываем проверенное исключение в аспекте Spring , в то время как рекомендуемые методы их не объявляли. Давайте начнем с аннотации:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ThrowUndeclared {}
Теперь мы собираемся посоветовать все методы, аннотированные этой аннотацией:
@Aspect @Component public class UndeclaredAspect { @Around("@annotation(undeclared)") public Object advise(ProceedingJoinPoint pjp, ThrowUndeclared undeclared) throws Throwable { throw new SomeCheckedException("AOP Checked Exception"); } }
В принципе, этот совет заставит все аннотированные методы выдавать проверенное исключение, даже если они не объявили такое исключение . Теперь давайте создадим сервис:
@Service public class UndeclaredService { @ThrowUndeclared public void doSomething() {} }
Если мы вызовем аннотированный метод, Java выдаст экземпляр UndeclaredThrowableException exception:
@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); } }
Как показано выше, Java инкапсулирует фактическое исключение в качестве причины и вместо этого вызывает исключение UndeclaredThrowableException .
5. Заключение
В этом уроке мы увидели, что заставляет Java выбрасывать экземпляр UndeclaredThrowableException exception.
Как обычно, все примеры доступны на GitHub .
I have the following code
public Object handlePermission(ProceedingJoinPoint joinPoint, RequirePermission permission) throws AccessException, Throwable {
System.out.println("Permission = " + permission.value());
if (user.hasPermission(permission.value())) {
System.out.println("Permission granted ");
return joinPoint.proceed();
} else {
System.out.println("No Permission");
throw new AccessException("Current user does not have required permission");
}
}
When I use a user that does not have permissions, I get java.lang.reflect.UndeclaredThrowableException
instead of AccessException
.
asked Mar 30, 2011 at 17:53
user373201user373201
10.7k33 gold badges111 silver badges168 bronze badges
1
AccessException
is a checked exception, but it was thrown from the method that doesn’t declare it in its throws
clause (actually — from the aspect intercepting that method). It’s an abnormal condition in Java, so your exception is wrapped with UndeclaredThrowableException
, which is unchecked.
To get your exception as is, you can either declare it in the throws
clause of the method being intercepted by your aspect, or use another unchecked exception (i.e. a subclass of RuntimeException
) instead of AccessException
.
answered Mar 30, 2011 at 18:12
6
Summary:
Ctors
| Methods
| Inherited Methods
public
class
UndeclaredThrowableException
extends RuntimeException
java.lang.Object | ||||
↳ | java.lang.Throwable | |||
↳ | java.lang.Exception | |||
↳ | java.lang.RuntimeException | |||
↳ | java.lang.reflect.UndeclaredThrowableException |
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.
An UndeclaredThrowableException
instance contains
the undeclared checked exception that was thrown by the invocation
handler, and it can be retrieved with the
getUndeclaredThrowable()
method.
UndeclaredThrowableException
extends
RuntimeException
, so it is an unchecked exception
that wraps a checked exception.
As of release 1.4, this exception has been retrofitted to
conform to the general purpose exception-chaining mechanism. The
«undeclared checked exception that was thrown by the invocation
handler» that may be provided at construction time and accessed via
the getUndeclaredThrowable()
method is now known as the
cause, and may be accessed via the Throwable.getCause()
method, as well as the aforementioned «legacy
method.»
Summary
Public constructors |
---|
Constructs an |
Constructs an |
Public methods |
|
---|---|
|
Returns the cause of this exception (the |
|
Returns the |
Inherited methods |
||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
From class java.lang.Throwable
|
||||||||||||||||||||||||||
From class java.lang.Object
|
Public constructors
UndeclaredThrowableException
public UndeclaredThrowableException (Throwable undeclaredThrowable)
Constructs an UndeclaredThrowableException
with the
specified Throwable
.
Parameters | |
---|---|
undeclaredThrowable |
Throwable : the undeclared checked exceptionthat was thrown |
UndeclaredThrowableException
public UndeclaredThrowableException (Throwable undeclaredThrowable, String s)
Constructs an UndeclaredThrowableException
with the
specified Throwable
and a detail message.
Parameters | |
---|---|
undeclaredThrowable |
Throwable : the undeclared checked exceptionthat was thrown |
s |
String : the detail message |
Public methods
getCause
public Throwable getCause ()
Returns the cause of this exception (the Throwable
instance wrapped in this UndeclaredThrowableException
,
which may be null
).
Returns | |
---|---|
Throwable |
the cause of this exception. |
getUndeclaredThrowable
public Throwable getUndeclaredThrowable ()
Returns the Throwable
instance wrapped in this
UndeclaredThrowableException
, which may be null
.
This method predates the general-purpose exception chaining facility.
The Throwable#getCause()
method is now the preferred means of
obtaining this information.
Returns | |
---|---|
Throwable |
the undeclared checked exception that was thrown |
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.
An UndeclaredThrowableException
instance contains
the undeclared checked exception that was thrown by the invocation
handler, and it can be retrieved with the
getUndeclaredThrowable()
method.
UndeclaredThrowableException
extends
RuntimeException
, so it is an unchecked exception
that wraps a checked exception.
As of release 1.4, this exception has been retrofitted to
conform to the general purpose exception-chaining mechanism. The
«undeclared checked exception that was thrown by the invocation
handler» that may be provided at construction time and accessed via
the getUndeclaredThrowable()
method is now known as the
cause, and may be accessed via the Throwable.getCause()
method, as well as the aforementioned «legacy
method.»
Public Constructor Summary
Public Method Summary
Throwable |
getCause() Returns the cause of this exception (the |
Throwable |
getUndeclaredThrowable() Returns the |
Inherited Method Summary
From class
java.lang.Object
Object |
clone() Creates and returns a copy of this |
boolean |
equals(Object obj) Compares this instance with the specified object and indicates if they |
void |
finalize() Invoked when the garbage collector has detected that this instance is no longer reachable. |
final Class<?> |
getClass() Returns the unique instance of |
int |
hashCode() Returns an integer hash code for this object. |
final void |
notify() Causes a thread which is waiting on this object’s monitor (by means of |
final void |
notifyAll() Causes all threads which are waiting on this object’s monitor (by means |
String |
toString() Returns a string containing a concise, human-readable description of this |
final void |
wait(long timeout, int nanos) Causes the calling thread to wait until another thread calls the |
final void |
wait(long timeout) Causes the calling thread to wait until another thread calls the |
final void |
wait() Causes the calling thread to wait until another thread calls the |
Public Constructors
public
UndeclaredThrowableException
(Throwable undeclaredThrowable)
(Throwable undeclaredThrowable)
Constructs an UndeclaredThrowableException
with the
specified Throwable
.
Parameters
undeclaredThrowable | the undeclared checked exception that was thrown |
---|
public
UndeclaredThrowableException
(Throwable undeclaredThrowable, String s)
(Throwable undeclaredThrowable, String s)
Constructs an UndeclaredThrowableException
with the
specified Throwable
and a detail message.
Parameters
undeclaredThrowable | the undeclared checked exception that was thrown |
---|---|
s | the detail message |
Public Methods
public
Returns the cause of this exception (the Throwable
instance wrapped in this UndeclaredThrowableException
,
which may be null
).
Returns
- the cause of this exception.
public
Throwable
getUndeclaredThrowable
()
Throwable
getUndeclaredThrowable
()
Returns the Throwable
instance wrapped in this
UndeclaredThrowableException
, which may be null
.
This method predates the general-purpose exception chaining facility.
The Throwable.getCause()
method is now the preferred means of
obtaining this information.
Returns
- the undeclared checked exception that was thrown
Предисловие
Я начал писать веб-интерфейс в последние два дня, так как бэк-энд проекта — мой единственный человек, писать его медленно. , Обнаружено много странных проблем, и в основном их может решить только один человек. Сегодня днем надолго задержался на этом вопросе. Теперь у меня все еще болит мозг.
проблема
Бизнесу необходимо реализовать функцию перехвата параметров запроса. Проверьте, включен ли токен. Проект реализован на базе springmvc, здесь естественно использовать технологию пружинного аспекта. Перехватите все запросы контроллера, а затем проверьте, передан ли параметр токена. Если он не переносится, генерируется настраиваемое исключение. Затем вызовите единый класс обработки исключений для его обработки.
Участвуют следующие классы:
package com.insigmaunited.lightai.base;
import com.insigmaunited.lightai.exception.TokenEmptyException;
import com.insigmaunited.lightai.result.Response;
import com.insigmaunited.lightai.util.StringUtil;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* Разрешение на перехват АОП
* @author Administrator
*
*/
@Component
@Aspect
public class PermissionAop {
private final Logger logger = LoggerFactory.getLogger(PermissionAop.class);
// Определяем pointcut
@Pointcut("execution(* com.insigmaunited.lightai.controller.*Controller.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void before() throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
System.out.println(url);
System.out.println(method);
System.out.println(uri);
System.out.println(queryString);
if (StringUtil.isNotEmpty(queryString) && queryString.indexOf("token") != -1 ){
}else{
выбросить новое TokenEmptyException («токен отсутствует»);
}
}
}
Пользовательский класс исключения
package com.insigmaunited.lightai.exception;
public class TokenEmptyException extends Exception {
public TokenEmptyException(String message) {
super(message);
}
}
Единый класс обработки исключений
package com.insigmaunited.lightai.exception;
import com.insigmaunited.lightai.base.BaseException;
import com.insigmaunited.lightai.result.Response;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.insigmaunited.lightai.exception.TokenEmptyException;
import javax.xml.bind.ValidationException;
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice extends BaseException {
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ValidationException.class)
public Response handleValidationException(ValidationException e) {
logger.error («Ошибка проверки параметра», e);
return new Response().failure("validation_exception");
}
/**
* 405 - Method Not Allowed
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Response handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
logger.error («Текущий метод запроса не поддерживается», д);
return new Response().failure("request_method_not_supported");
}
/**
* 415 - Unsupported Media Type
*/
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Response handleHttpMediaTypeNotSupportedException(Exception e) {
logger.error («Текущий тип носителя не поддерживается», д);
return new Response().failure("content_type_not_supported");
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(TokenEmptyException.class)
public Response handleTokenEmptyException(Exception e) {
logger.error («отсутствует параметр токена», e);
return new Response (). failure («параметр токена отсутствует»);
}
/**
* 500 - Internal Server Error
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Response handleException(Exception e) {
logger.error ("Ненормальная работа службы", e);
вернуть новый Response (). failure («Ненормальная работа службы»);
}
}
Интерфейс вызывается в это время, и ожидается, что он должен
{
"success": false,
"message": "Отсутствует параметр токена",
"data": null
}
На самом деле возвращено
{
"success": false,
"message": "Служба работает ненормально",
"data": null
}
Ошибка консоли выглядит следующим образом:
http://localhost:8080/user/3/profile
GET
/user/3/profile
null
[ОШИБКА] 2017-12-08 18:29:19-com.insigmaunited.lightai.exception.ExceptionAdvice-ExceptionAdvice.java (63) - Ненормальная операция службы
java.lang.reflect.UndeclaredThrowableException
at com.insigmaunited.lightai.controller.UserController$$EnhancerBySpringCGLIB$$e4eb8ece.profile(<generated>)
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.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:475)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:498)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:796)
at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.doRun(Nio2Endpoint.java:1688)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.net.AbstractEndpoint.processSocket(AbstractEndpoint.java:914)
at org.apache.tomcat.util.net.Nio2Endpoint$Nio2SocketWrapper$4.completed(Nio2Endpoint.java:536)
at org.apache.tomcat.util.net.Nio2Endpoint$Nio2SocketWrapper$4.completed(Nio2Endpoint.java:514)
at sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:126)
at sun.nio.ch.Invoker$2.run(Invoker.java:218)
at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Вызвано: com.insigmaunited.lightai.exception.TokenEmptyException: токен отсутствует
at com.insigmaunited.lightai.base.PermissionAop.before(PermissionAop.java:53)
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.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:620)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:602)
at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:41)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
... 45 more
Это странно. Почему созданное мной настраиваемое исключение превратилось в UndeclaredThrowableException, которое не могло быть обработано единым обработчиком исключений.
причина
Через поисковик наконец нашел причину:
Наш класс обработки исключений на самом деле является реализацией динамического прокси.
Если исключение является проверенным исключением и не объявлено в интерфейсе динамического прокси, оно будет заключено в оболочку UndeclaredThrowableException.
Определенное нами настраиваемое исключение определяется как проверенное исключение, что приводит к его упаковке как UndeclaredThrowableException.
Официальное объяснение документа
Аномальное различие
решить
Узнать причину очень просто. Либо вызовите непроверенное исключение java.lang.RuntimeException или java.lang.Error, либо интерфейс должен объявить исключение.
Здесь вы можете изменить настраиваемое исключение как исключение времени выполнения.
package com.insigmaunited.lightai.exception;
public class TokenEmptyException extends RuntimeException {
public TokenEmptyException(String message) {
super(message);
}
}
урок
1. По возможности определяйте настраиваемые исключения как исключения времени выполнения.
2. Понятие исключения неясно. Фундамент не прочный.
java.lang.reflect.Proxy and checked exceptions
On-the-Fly Proxy
Say on have an interface Foo
with several (hundreds) methods. Is it possible to implement an
interface on-the-fly? Without having an implementation code? Yes. It is possible.
The standard possibility is
java.lang.reflect.Proxy.
The newProxyInstance
method helps to create an on-the-fly implementation. One provides
an interceptor object that is called for every method invocation on the interface implementation instance.
Besides the standard Proxy API, there are libraries, that do the same thing, for example,
Byte Buddy or CGLIB.
In this post, we will use the standard JRE API — java.lang.reflect.Proxy
Proxy and Checked Exceptions
Let’s consider the following code in Java:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Foo {
void bar();
}
public static void main(String[] args) {
final Foo proxy = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[]{Foo.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new Exception("fail");
}
});
//what is the exception?
proxy.bar();
}
The code is trivial. We have the interface Foo
, and we implement it via Proxy#newProxyInstance
.
The implementation of the Proxy instance throws an exception of type Exception
.
Will we have the exception of type Exception
as a result?
Running the Example
Let’s execute the example and see what we have:
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at $Proxy0.bar(Unknown Source)
at ProxyJava.main(ProxyJava.java:22)
Caused by: java.lang.Exception: fail
at ProxyJava$1.invoke(ProxyJava.java:17)
... 2 more
The answer is NO. We have java.lang.reflect.UndeclaredThrowableException
exception.
Checked Exceptions in Java
As we all know, Java has checked exceptions. It means one declares what exceptions are
possibly thrown from a method. The main class of all exceptions is
java.lang.Throwable.
In Java language, we use throws
to indicate that a method may throw an exception. For example,
throws IOException
.
There are two specific sub-classes of Throwable
, which does not require to be declared
by the throws
keyword — java.lang.Error
and java.lang.RuntimeException
. All sub-classes
of those two types are free to throw without declaration.
The UndeclaredThrowableException
is the specific exception type that is used in the create a proxy implementation of an interface
to preserve checked exceptions in Java.
As we see from the Javadoc, the exception is used to wrap any checked exceptions that are not
declared with the throws
block in the interface declaration.
Proxy and JVM Languages
JVM ecosystem is huge. There are many languages for the JVM, including
Kotlin,
Groovy,
Scala
and so on, that does not have checked exceptions.
Checked exceptions are checked by the compiler, on the JVM bytecode level,
there is no difference between exceptions at all.
It is quite easy to get UndeclaredThrowableException
at some
unexpected places if mixing such languages with java.lang.reflect.Proxy
!
For example, in Kotlin:
import java.lang.reflect.Proxy
internal interface Foo {
fun bar()
}
fun main(args: Array<String>) {
val proxy = Proxy.newProxyInstance(
Foo::class.java.classLoader,
arrayOf<Class<*>>(Foo::class.java)
) { _, _, _ ->
throw Exception("fail")
} as Foo
proxy.bar()
}
The same code reads correctly but does not work. It is allowed in Kotlin to throw Exception
from
a method (because exceptions are not checked), but it will not work via the
java.lang.reflect.Proxy
. We will have the following execution result
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.bar(Unknown Source)
at ProxyKTKt.main(ProxyKT.kt:15)
Caused by: java.lang.Exception: fail
at ProxyKTKt$main$proxy$1.invoke(ProxyKT.kt:12)
at ProxyKTKt$main$proxy$1.invoke(ProxyKT.kt)
... 2 more
Fixing the UndeclaredThrowableException
To avoid the UndeclaredThrowableException
one need to declare the exceptions
explicitly with throws
block. That solves the problem in Java example above.
Similarly, it solves the problem in the Kotlin snippet too: we add the
@Throws(Exception::class)
annotation on the bar
function.
One may have a look at the implementation of the Proxy#newProxyInstance
in the sources of JVM. It turns out it is not possible to disable that
logic in the implementation. One is not allowed to breach Java’s checked
exceptions with Proxy#newProxyInstance
.
There are two ways. One is to declare throws
for all interfaces
that are used with Proxy#newProxyInstance
. Of course, it is too easy to
forget doing in languages without checked exceptions. Tests may help.
An alternative could be to implement or use another variant of the
Proxy#newProxyInstance
, that does not do the check. Let me know
in the comments if you’d like to learn more, how exactly the
Proxy#newProxyInstance
or similar proxies are implemented.
comments powered by