私は、提供されたエンティティに関する情報を表示するshowメソッドを含むコントローラを持っています。私はhttp://localhost:8080/owners/1 URLを使用し、この操作を呼び出すために@ExceptionHandlerがSpringフォーマッタからスローされた例外をキャッチしない
@Controller
@RequestMapping("/owners")
public class OwnersController {
@RequestMapping(value = "/{owner}", method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
public String show(@PathVariable Owner owner, Model model) {
// Return view
return "owners/show";
}
}
。
識別子1を有効な所有者要素に変換するには、Spring Formatterを定義し、WebMvcConfigurerAdapter
のaddFormatters
メソッドに登録する必要があります。
私はOwnerFormatter
次があります。
public class OwnerFormatter implements Formatter<Owner> {
private final OwnerService ownerService;
private final ConversionService conversionService;
public OwnerFormatter(OwnerService ownerService,
ConversionService conversionService) {
this.ownerService = ownerService;
this.conversionService = conversionService;
}
@Override
public Owner parse(String text, Locale locale) throws ParseException {
if (text == null || !StringUtils.hasText(text)) {
return null;
}
Long id = conversionService.convert(text, Long.class);
Owner owner = ownerService.findOne(id);
if(owner == null){
throw new EntityResultNotFoundException();
}
return owner;
}
@Override
public String print(Owner owner, Locale locale) {
return owner == null ? null : owner.getName();
}
}
あなたが見ることができると、そこにいない任意のものであるので、私は、このメソッドはnullを返した場合happends何、ID 1と所有者を取得するためにfindOne
メソッドを使用しましたが、 ID 1のオーナーですか?
これを防ぐために、EntityResultNotFoundException
というカスタム例外をスローします。私はこの例外をスローしたときerrors/404.html
を返すことができるようにプロジェクトを設定したい
public class EntityResultNotFoundException extends RuntimeException {
public EntityResultNotFoundException() {
super("ERROR: Entity not found");
}
}
ので、Spring documentation次、私は2つのオプションがあります:この例外は、次のコードが含まれています
オプションA)
を :EntityResultNotFoundException
を管理
設定@ControllerAdvice
@ExceptionHandler
と注釈を付け方法
しかし、これは動作しません。上記の実装をコントローラメソッド内で例外をスローするように変更すると、完璧に動作します。
呼び出しコントローラの実装が@ControllerAdvice
でモニタされない前に呼び出されるSpring Formatterのようです。
私にとっては、提供された@RequestMapping
の必要なパラメータを準備するためにコントローラメソッドを呼び出す前にSpring Formatterが呼び出されているので、これは意味がありません... Springはどのメソッドが呼び出されるかを知っています...なぜSpring要求を準備するために使用されたフォーマッタは、@ControllerAdvice
によって監視されておらず、この「準備プロセス」中に発生した可能性のあるすべての例外をキャッチしていますか?
UPDATE:Serge Ballesta氏が答えてくれたように、@ControllerAdviceはコントローラのメソッドについてのAOPアドバイスとして実装されています。したがって、コントローラの外部にスローされた例外を傍受する方法はありません。
私はこのオプションを拒否します。春FrameworkのJIRAにおけるいくつかの回答の後、they suggest meは、すべての例外をキャッチし、一般的な@ExceptionHandler
を使用して、根本原因を確認するために、例外の原因があるかどうかを知ることができるように、いくつかの条件文を使用してする:UPDATE 2
私がSpring Formatterで呼び出した例外です。私は@ExceptionHandler
を使用してSpring Formattersからスローされた例外をキャッチすることができないので、これがSpring MVCの別の改善であると思います。アップロードした証明書を確認できます。here
このオプションも拒否しています。
オプションB)
ビュー識別子を持つ例外をマップするために@Configuration
クラス内に新しい@Bean
SimpleMappingExceptionResolver
を含めます。
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
[...]
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver =
new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("EntityResultNotFoundException", "errores/404");
resolver.setExceptionMappings(mappings);
return resolver;
}
}
しかし、上記の実装はSpring Formatterでスローされた例外では機能しません。
更新日::私はSpringコードをデバッグしていましたが、Spring Frameworkの興味深い改善点が2つあります。
まず、DispatcherServlet
はすべてHandlerExceptionResolver
をinitStrategies
およびinitHandlerExceptionResolvers
のメソッドからロードしています。この方法は、正しい順序ですべてのHandlerExceptionResolversを取られたが、その後、再びそれらを注文するには、次のコードを使用しています:
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
問題があることHandlerExceptionResolverから順番を取得しようとしfindOrder
方法でこのメソッドを委譲していますOrdered
のインスタンスです。ご覧のように、私は登録された@Beanで注文を定義していないので、宣言されたbeanから注文を取得しようとすると、SimpleMappingExceptionResolver
はLOWEST_PRECEDENCEを使用しています。これにより、最初に結果を返すので、SpringはDefaultHandlerExceptionResolver
を使用します。
これを解決するために、次のコードを使用して宣言したBeanにorder値を追加しました。 AnnotationAwareOrderComparator
ソートがすべてHandlerExceptionResolver
SimpleMappingExceptionResolver
登録今
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver =
new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("EntityResultNotFoundException", "errores/404");
resolver.setOrder(-1);
resolver.setExceptionMappings(mappings);
return resolver;
}
は、最初の一つであり、それはリゾルバとして使用されます。 とにかく、まだ動作していません。私はデバッグを続行しましたが、現在はのdoResolveException
を使用して例外を解決していることがわかりました。ただし、マップされたビューを取得しようとするメソッドfindMatchingViewName
はnullを返します。
問題はfindMatchingViewName
がSimpleMappingExceptionResolver
のexceptionMappingsに定義されたいくつかの例外を除いて受け取った例外一致かどうかを確認しようとしているが、それは唯一のgetDepth
メソッド内のスーパークラスをチェックするということです。原因の例外をチェックする必要があります。
public class CauseAdviceSimpleMappingExceptionResolver extends SimpleMappingExceptionResolver{
/**
* Find a matching view name in the given exception mappings.
* @param exceptionMappings mappings between exception class names and error view names
* @param ex the exception that got thrown during handler execution
* @return the view name, or {@code null} if none found
* @see #setExceptionMappings
*/
@Override
protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
String viewName = null;
String dominantMapping = null;
int deepest = Integer.MAX_VALUE;
for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
String exceptionMapping = (String) names.nextElement();
int depth = getDepth(exceptionMapping, ex);
if (depth >= 0 && (depth < deepest || (depth == deepest &&
dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) {
deepest = depth;
dominantMapping = exceptionMapping;
viewName = exceptionMappings.getProperty(exceptionMapping);
}else if(ex.getCause() instanceof Exception){
return findMatchingViewName(exceptionMappings, (Exception) ex.getCause());
}
}
if (viewName != null && logger.isDebugEnabled()) {
logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
"], based on exception mapping [" + dominantMapping + "]");
}
return viewName;
}
}
(ちょうどSimpleMappingExceptionResolver
を拡張し、深さが有効でない場合、原因の例外を除いて再びビューのマッチング見つけようとするfindMatchingViewName
メソッドを実装します)作業を継続するには、以下の回避策を適用した
スーパークラスの例外だけを使用するのではなく、原因の例外クラスを使用するので、本当に面白いです。この改善を含め、Spring Frameworkのgithubに新しいPull-Requestを作成します。
これらの2つの変更(注文と拡張SimpleMappingExceptionResolver
)を使用すると、Spring Formatterからスローされた例外をキャッチしてカスタムビューを返すことができます。
私は、Spring Frameworkで次の問題を作成しました。JIRA https://jira.spring.io/browse/SPR-14291 – jcgarcia
これで問題が解決するかどうかわかりませんが、コントローラのアドバイスでクラスに注釈を付ける必要があると思います実行する4つの@Component、Repository、Service、Controllerのいずれかを使用します。したがって、ownerformatterクラスに注釈を付けることができるかどうかを確認してください。 –