特定 class 的异常 Handling/Mapping

Exception Handling/Mapping for a particular class

我有资源 class,它本身与内部服务对话。此资源充当服务的休息 API。服务层可以抛出意外异常,因此资源应该处理那些已处理的意外异常并记录下来。我正在使用 dropwizard 框架,它又使用球衣。事情是这样的。

@PATH(/user)
@GET
public Response getUser(@QueryParam("id") String userId) {
   assertNotNull(userId);
   try {
      User user = service.getUser(userId);
      return Response.ok(user).build();
   }
   catch (MyOwnException moe) { //basically 400's
     return Response.status(400).entity(moe.getMsg()).build();
   }
   catch (Exception e) {   //unexpected exceptions

      logger.debug(e.getMessage);
      return Response.status(500).entity(moe.getMsg()).build();
   }
}

这里的问题是我必须为每个 REST api 端点执行完全相同的异常处理。我可以为这个特定资源做某种异常映射,以便我可以将所有处理逻辑和日志记录放在那里吗? 我知道我可以为球衣中的特定异常构建映射器,但那是针对整个模块的,而不是单个 class.

为什么不将异常处理分解为私有方法?

@PATH(/user)
@GET
public Response getUser(@QueryParam("id") String userId) {
    assertNotNull(userId);
    return handleExceptions(() -> {
        User user = service.getUser(userId);
        return Response.ok(user).build();
    });
}

private Response handleExceptions(Callable<Response> callable) {
    try {
        return callable.call();
    }
    catch (MyOwnException moe) { //basically 400's
        return Response.status(400).entity(moe.getMsg()).build();
    }
    catch (Exception e) {   //unexpected exceptions
        logger.debug(e.getMessage);
        return Response.status(500).entity(e.getMessage()).build();
    }
}

请注意,您无法将 ExceptionMapper 注册到资源方法。我已经通过实现 DynamicFeature 来尝试此操作,该 DynamicFeature 正在寻找自定义注释,然后尝试使用 FeatureContext.

注册自定义 ExceptionMapper

结果令人失望:
WARNING: The given contract (interface javax.ws.rs.ext.ExceptionMapper) of class path.to.CustomExceptionMapper provider cannot be bound to a resource method.

可能不起作用:
但是...

对于资源 class 这实际上很容易。只需在 ResourceConfig 中为资源 class 注册 ExceptionMapper。对我来说它看起来像:

@ApplicationPath("/")
public class ApplicationResourceConfig extends ResourceConfig {
    public ApplicationResourceConfig() {
        // [...]
        register(YourExceptionMapper.class, YourResource.class);
        // [...]
    }
}

因此,如果您可以在资源 class 级别上使用它,那么就这样做吧。

否则您可能需要使用方面 (但我看不出有任何理由这样做)。示例:

看点

@Aspect
public class ResourceAspect {

    Logger logger = [...]

    private static final String RESOURCE = "execution(public !static javax.ws.rs.core.Response path.to.resources..*(..)) && @annotation(path.to.HandleMyOwnException)";

    @Around(RESOURCE)
    public Object translateRuntimeException(ProceedingJoinPoint p) throws Throwable {

        try {
            return p.proceed();
        } catch (MyOwnException moe) {
            return Response.status(400).entity(moe.getMsg()).build();
        } catch (Exception e) {   //unexpected exceptions
            logger.debug(e.getMessage);
            return Response.status(500).entity(e.getMessage()).build();
        }
    }

}

请注意,RESOURCE 配置。此处它适用于 path.to.resources 下的 none static 方法,它返回 Response 并使用 HandleMyOwnException 注释进行注释。

HandleMyOwnException

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HandleMyOwnException {}

资源方法

@GET
@PATH("/user")
@HandleMyOwnException
public Response getUser(@QueryParam("id") String userId) {
   assertNotNull(userId);
   return Response.ok(service.getUser(userId)).build();
}

pom.xml

<!-- deps -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.2</version> <!-- or newer version -->
</dependency>

<!-- build plugins -->
<plugins>
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.7</version>
        <configuration>
            <complianceLevel>1.8</complianceLevel>
            <showWeaveInfo>true</showWeaveInfo>
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>compile</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>
<pluginManagement>
    <plugins>
        <plugin>
            <groupId>org.eclipse.m2e</groupId>
            <artifactId>lifecycle-mapping</artifactId>
            <version>1.0.0</version>
            <configuration>
                <lifecycleMappingMetadata>
                    <pluginExecutions>
                        <pluginExecution>
                            <pluginExecutionFilter>
                                <groupId>org.codehaus.mojo</groupId>
                                <artifactId>aspectj-maven-plugin</artifactId>
                                <versionRange>[1.7,)</versionRange>
                                <goals>
                                    <goal>compile</goal>
                                </goals>
                            </pluginExecutionFilter>
                            <action>
                                <ignore></ignore>
                            </action>
                        </pluginExecution>
                    </pluginExecutions>
                </lifecycleMappingMetadata>
            </configuration>
        </plugin>
    <plugins>
<pluginManagement>

祝你有愉快的一天!

已编辑

~添加了更完整的pom.xml配置
~ 更正了 ResourceAspect

中注释的缺失路径