【问题标题】:Letting the presentation layer (JSF) handle business exceptions from service layer (EJB)让表示层 (JSF) 处理来自服务层 (EJB) 的业务异常
【发布时间】:2015-07-08 05:23:25
【问题描述】:

更新提供的实体的 EJB 方法(使用 CMT):

@Override
@SuppressWarnings("unchecked")
public boolean update(Entity entity) throws OptimisticLockException {
    // Code to merge the entity.
    return true;
}

如果检测到要由调用者(托管 bean)精确处理的并发更新,这将抛出 javax.persistence.OptimisticLockException

public void onRowEdit(RowEditEvent event) {
    try {
        service.update((Entity) event.getObject())
    } catch(OptimisticLockException e) {
        // Add a user-friendly faces message.
    }
}

但这样做会导致对表示层的javax.persistence API 产生额外的依赖,这是一种导致紧耦合的设计味道。

应该将它包装在哪个异常中,以便可以完全省略紧耦合问题?或者有没有一种标准的方法来处理这个异常,而这又不会导致在表示层上强制执行任何服务层依赖关系?

顺便说一句,我发现在 EJB 中(在服务层本身上)捕获此异常并然后将标志值返回给客户端(JSF)很笨拙。

【问题讨论】:

    标签: jsf jakarta-ee exception-handling ejb optimistic-locking


    【解决方案1】:

    创建一个自定义服务层特定的运行时异常,使用@ApplicationExceptionrollback=true 进行注释。

    @ApplicationException(rollback=true)
    public abstract class ServiceException extends RuntimeException {}
    

    为一般业务异常创建一些具体的子类,例如约束冲突、必需的实体,当然还有乐观锁。

    public class DuplicateEntityException extends ServiceException {}
    
    public class EntityNotFoundException extends ServiceException {}
    
    public class EntityAlreadyModifiedException extends ServiceException {}
    

    有的可以直接扔。

    public void register(User user) {
        if (findByEmail(user.getEmail()) != null) {
            throw new DuplicateEntityException();
        }
    
        // ...
    }
    
    public void addToOrder(OrderItem item, Long orderId) {
        Order order = orderService.getById(orderId);
    
        if (order == null) {
            throw new EntityNotFoundException();
        }
    
        // ...
    }
    

    其中一些需要全局拦截器。

    @Interceptor
    public class ExceptionInterceptor implements Serializable {
    
        @AroundInvoke
        public Object handle(InvocationContext context) throws Exception {
            try {
                return context.proceed();
            }
            catch (javax.persistence.EntityNotFoundException e) { // Can be thrown by Query#getSingleResult().
                throw new EntityNotFoundException(e);
            }
            catch (OptimisticLockException e) {
                throw new EntityAlreadyModifiedException(e);
            }
        }
    
    }
    

    ejb-jar.xml 中注册为默认拦截器(在所有 EJB 上)。

    <interceptors>
        <interceptor>
            <interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class>
        </interceptor>
    </interceptors>
    <assembly-descriptor>
        <interceptor-binding>
            <ejb-name>*</ejb-name>
            <interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class>
        </interceptor-binding>
    </assembly-descriptor>
    

    作为一般提示,在 JSF 中,您还可以有一个全局异常处理程序,它只添加一条面孔消息。当以this kickoff example 开头时,您可以在YourExceptionHandler#handle() 方法中执行类似的操作:

    if (exception instanceof EntityAlreadyModifiedException) { // Unwrap if necessary.
        // Add FATAL faces message and return.
    }
    else {
        // Continue as usual.
    }
    

    【讨论】:

    • 这有点离题,如果我无法获得解决方案,我可能会提出一个单独的问题。我有一个测试应用程序,它只包含一个拦截器 (javax.interceptor.Interceptor),这里提到的所有配置和一个 EJB。我总是在部署时遇到异常 - java.lang.IllegalStateException: Interceptor binding contains an interceptor class name = com.example.MyInterceptor that is not defined as an interceptor。一目了然可能的原因是什么?
    • 查看明确注册的更新答案。如果没有@InterceptorBinding,我猜GF4不会那样做。
    • 现在有效。谢谢。 (我没想到会以这种方式显式注册拦截器)。
    • Throwable exception = iter.next().getContext().getException();ExceptionHandlerWrapper 中的handle() 方法中总是返回javax.faces.FacesException (exception.getClass().getName())。因此,像exception instanceof EntityAlreadyModifiedException 这样的条件检查总是会失败,即使EntityAlreadyModifiedException 是从指定的拦截器中重新抛出的。
    • 评论说“必要时打开包装”。如果您使用的是OmniFaces,请使用Exceptions#unwrap(),或者更好的是Exceptions#is()
    猜你喜欢
    • 2012-12-28
    • 2015-06-06
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 2012-09-10
    • 2020-01-07
    • 1970-01-01
    • 2014-04-23
    相关资源
    最近更新 更多