App因某种原因会抛出异常,我们会记录在log中,同时也要告知用户出了问题,一些错误信息需要显示给用户,这就有国际化的需求。我们不应简单将原始的Exception直接显示给用户:
异常可能是在底层,例如仓库读写数据出现异常,中间经过service,controller,才到view。在每个环节中,如何知道异常已经被记录下来,无需再次log?➤ 可以自定义异常,如LoggedException,记录原始的log,然后抛出LoggedException,如果捕获的是LoggedException,我们不需要再次记录。LoggedException中可以将原始的异常作为cause。用户很难明白,另外可能会向黑客泄漏某些可以利用的信息,需要向用户显示有效的说明,并实现国际化。 ➤ 自定义异常,通过实现MessageSourceResolvable接口来实现国际化。结合问题和解决方式,我们将定义InternationalizedException实现MessageSourceResolvable,然后LoggedException继承它。
spring:message中支持直接传递MessageSourceResolvable对象,采用message参数。
Controller中的例子代码如下:
@RequestMapping(value = "/test", method = RequestMethod.GET) public String test(Map<String, Object> model){ ... ... //【1】模拟有一个异常,并进行了log记录 Exception exception = new Exception("testERROR",new Throwable("E . R . R . O . R")); logger.error("Log exception {} caused by {} : ", exception.toString(),exception.getCause()); //【2】将其封装为LoggedException(实际也是实现MessageSourceResolvable的InternationalizedException) // "foo.test"就是国际化中的code,参数为Test和Error message,如果找不到这个code进行国际化,则输出"This is a Error message" LoggedException loggedException = new LoggedException(exception, "foo.test", "This is a Error message", "Test","Error message"); //【3】将MessageSourceResolvable对象存放在model中,进而传递给jsp model.put("exception", loggedException); ... ... }相关的jsp代码 <c:if test="${exception != null}"> <spring:message message="${exception}" /> </c:if>一般来讲,我们没有必要在log中提供国际化,但在如果基于某些原因需要这样处理,我们该如何进行。
下面是一段小代码:
Exception exception = new Exception("testERROR",new Throwable("E . R . R . O . R")); InternationalizedException i18nException = new InternationalizedException(exception, "foo.test", "This is a Error message", "Test", "Error message"); logger.error(i18nException.toString());log的输出为: 16:47:49.900 [http-nio-8080-exec-23] [ERROR] - cn.wei.chapter15.exception.InternationalizedException: This is a Error message因为在InternationalizedException中,对应的构造函数如下,也就是设置了Exception的toString()为defaultMessage的值 public InternationalizedException(Throwable cause, String errorCode,String defaultMessage,Object ...objects ){ super(defaultMessage == null ? errorCode : defaultMessage, cause); ... ...我们在InternationalizedException中增加下面的代码: /* 【说明】我们需要将exception的输出内容进行国际化,可以重写Throwable接口的getLocalizedMessage()。 */ @Override public String getLocalizedMessage(){ return this.errorCode; }输出为: 16:55:32.958 [http-nio-8080-exec-22] [ERROR] - cn.wei.chapter15.exception.InternationalizedException: foo.test这表明getLocalizedMessage()影响了原来的toString(),但是有个问题,MessageSource是spring的bean,但InternationalizedException不在spring的框架中,因此不能inject。我们需要在这里创建一个MessageSource来处理,有点小麻烦,或者我们的这个国际化异常只为spring的组建服务,这样代码修订为:
private static final Locale DEFAULT_LOCALE = Locale.CHINA; //我们这里沿用了getLocalizedMessage的名字,但不是override,不会影响toString()的输出,只是类似的名字表达相似的功能。 public String getLocalizedMessage(MessageSource messageSource){ return this.getLocalizedMessage(messageSource,this.getLocale()); } public String getLocalizedMessage(MessageSource messageSource, Locale locale){ return messageSource.getMessage(this, locale); } protected final Locale getLocale(){ Locale locale = LocaleContextHolder.getLocale(); return locale == null ? InternationalizedException.DEFAULT_LOCALE : locale; }在某个spring 组建内,我们可以使用这国际化的log @Inject private MessageSource messageSource; ... ... logger.error(i18nException); logger.error(i18nException.getLocalizedMessage(messageSource)); logger.error(i18nException.getLocalizedMessage(messageSource,Locale.US)); 17:10:52.349 [http-nio-8080-exec-38] [ERROR] - cn.wei.chapter15.exception.InternationalizedException: This is a Error message 17:10:52.359 [http-nio-8080-exec-38] [ERROR] - 您好, Error message Test 17:10:52.369 [http-nio-8080-exec-38] [ERROR] - Hello, Test Error message 相关文章相关链接: 我的Professional Java for Web Applications相关文章