/*
Creating the interceptor binding
*/
@InterceptorBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface BindException {
}
定义拦截器绑定之后,我们需要定义拦截器绑定实现
/*
Creating the interceptor implementation
*/
@Interceptor
@BindException
public class ExceptionCDIInterceptor {
@AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("Invoked method " + ctx.getMethod().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// code for Exception handler
}
}
}
现在我们只需要将拦截器应用于我们的方法
/*
Some Service class where you want to implement the interceptor
*/
@ApplicationScoped
public class Service {
// adding annotation to thisMethodIsBound method to intercept
@BindException
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionCDIInterceptor
}
}
您也可以使用AspectJ实现相同的特性。
/*
Creating the Aspect implementation
*/
@Aspect
public class ExceptionAspectInterceptor {
@Around("execution(* com.package.name.SomeService.thisMethodIsBound.*(..))")
public Object methodInterceptor(ProceedingJoinPoint ctx) throws Throwable {
System.out.println("Invoked method " + ctx.getSignature().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// codes for Exception handler
}
}
}
现在我们只需要为应用程序配置启用AspectJ
/*
Enable the AspectJ in your application
*/
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public SomeService SomeService() {
return new SomeService();
}
}
/*
Some Service class where you want to implement the Aspect
*/
package com.package.name;
public class SomeService {
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionAspectInterceptor
}
}
@ExceptionHandler(DataIntegrityViolationException.class)
public void handleIntegrityViolation() {
// do stuff for integrity violation here
}
@ExceptionHandler(Exception.class)
public void handleEverythingElse() {
// do stuff for everything else here
}
@RestController
public class MyController {
@RequestMapping("/foo")
public String foo() {
throw new IllegalArgumentException();
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Specific handler", HttpStatus.BAD_REQUEST);
}
}
AnotherController,不含任何以@ExceptionHandler注解的方法:
@RestController
public class AnotherController {
@RequestMapping("/bar")
public String bar() {
throw new IllegalArgumentException();
}
}
一个全局@ControllerAdvice类:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Global handler", HttpStatus.BAD_REQUEST);
}
}
@RestController
public class MyController {
@Autowired // but better to use constructor injection
private MyService myService;
public ResponseDto doSomething(RequestDto request) {
try {
myService.doSomething(request);
} catch (DataIntegrityViolationException ex) {
// process exception
}
}
}
@Transactional
class MyService {
public void doSomething() {
// do your processing which uses jpa/hibernate under the hood
}
}
9条答案
按热度按时间t0ybt7op1#
您需要使用AOP工具,如CDI Interceptor或AspectJ来实现这个横切关注点。关注点是一个术语,指的是基于功能划分的系统的一部分。
基本上,这种类型的功能用于处理日志记录、安全性,以及处理错误......这些不是业务逻辑的一部分......
比如你想把log4j改成sl4j,那么你需要检查每一个你用过log4j的类,然后改变它。但是如果你用过AOP工具,那么你只需要去拦截器类,改变它的实现。类似于即插即用和非常强大的工具。
下面是使用JavaEE CDI拦截器的代码片段
定义拦截器绑定之后,我们需要定义拦截器绑定实现
现在我们只需要将拦截器应用于我们的方法
您也可以使用AspectJ实现相同的特性。
现在我们只需要为应用程序配置启用AspectJ
我在我的git repo https://github.com/prameshbhattarai/javaee-exceptionBinding中有使用CDI拦截器的代码示例。
mxg2im7a2#
只是作为一种选择(显然,这并不理想):您可以将异常 Package 到某个方法中的自定义异常中,然后在
@ExceptionHandler
然后
bcs8qyzn3#
你能解释一下为什么你需要这个吗?我问是出于好奇,因为我从来没有觉得这是必要的,下面是原因:
异常通常代表一个非常具体的“错误”--以一种非常具体的方式出错的东西。基本上,异常代表一个错误,而不是一个流...
Spring可以支持两个开箱即用的“自由度”:
1.异常参数。可能是像错误代码这样的东西,它可以声明为异常本身的数据字段。
1.例外状况继承。范例:
如果您的系统中有一个
UserDoesNotExistException
,并且您希望在管理某些流中退休的用户的系统的情况下更具体一些,那么您始终可以创建一个更具体的异常:显然,Spring可以支持这两种情况:在
ExceptionMapper
中,无论如何都可以访问异常,因此可以执行以下操作:在第二种情况下,对于不同类型的异常,只有不同的异常Map器。
您还可以考虑使用
@ControllerAdvice
注解来重用代码或其他内容。v8wbuo2f4#
我不认为可以为方法指定特定的
@ExceptionHandler
,但是可以将@ExceptionHandler
方法绑定到特定的Exception
。因此,如果您希望以一种方式处理所有
DataIntegrityViolationException
,而以另一种方式处理所有其他异常,您应该能够通过以下方式实现:zzzyeukh5#
您可以根据想要的行程方式,从其他方法掷回的一般例外状况衍生子例外状况。
假设你已经声明了父异常为
ParentException
,派生子类如ChildAException extends ParentException
,ChildBException extends ParentException
等。定义捕捉
ParentException
的@ControllerAdvice
类,并定义委托方法中的特定行为。k3bvogb16#
我也遇到了和你一样的问题。所以我检查了spring的源代码。看起来spring会先在@Controller类中搜索任何用
@ExceptionHandler
注解的方法,如果没有匹配的方法,它会继续搜索所有用@ControllerAdvice
注解的类。所以你可以使用下面的策略:@ExceptionHandler
方法计算MyController
:AnotherController
,不含任何以@ExceptionHandler
注解的方法:@ControllerAdvice
类:然后,如果您访问http://ip:port/foo,您将获得带有特定处理程序的400状态代码,当您访问http://ip:port/bar时,将获得带有全局处理程序的400状态代码。
a2mppw5e7#
我同意,无法Map特定的@ExceptionHandler以仅处理@RestController中的一个特定方法应该是一个非常理想的特性。
uyhoqukh8#
我尝试了try{}catch(Exception ex){},但没有捕获到异常。但是异常处理程序很好地处理了它。
由于我们讨论的是休眠异常,这些异常通常在事务的提交阶段抛出。这里的问题是,看起来你好像在控制器中打开了事务,这被认为是一种糟糕的做法。
你应该做的是-在应用层打开事务。
Controller只是将xml/jsonMap到传入的RequestDto对象,然后调用Service来处理业务逻辑。Service(或其方法)应该使用
@Transactional.
进行注解一旦你这样做了,try catch将开始在控制器级别上按照预期的那样运行。但是,我甚至会更进一步,因为DatabaseExeption不应该真的走到控制器那么远。替代方案是在服务内部使用手动事务,并在那里执行try catch。
然后在服务层将数据库异常转换为更一般的异常,其中包含控制器处理所需的所有信息。
然后,您应该在控制器中捕获更一般的异常(MyDatabaseAccessException),并根据表示层的需要进行转换。
这里建议的
@ControllerAdvice
适用于跨控制器的全局异常处理。@ExceptionHandler
并不适用于每个方法,除非你希望每个方法都有控制器。即使在那之后,它也会与global @ControllerAdvice冲突。我不知道为什么spring不允许在方法级别使用
@ExceptionHandler
,它可以简化很多类似的情况。8yoxcaq79#
我的解决方案是使用标记来注解方法: