@Bean @ConfigurationProperties( prefix = "spring.messages" ) public MessageSourceProperties messageSourceProperties() { return new MessageSourceProperties(); } @Bean public MessageSource messageSource(MessageSourceProperties properties) { /* * ResourceBoundleMessageSource extends AbstractResourceBasedMessageSource implements BeanClassLoaderAware * 该实现类容许用户经过beanName指定一个资源名:包括类路径的全限定资源名 * 或者经过beanName指定一组资源名 */ ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) { /* * setBasenames 设置国际化资源文件去掉语言国家代码的基础名, * 国际化资源文件能够直接放在类路径下叫 messages.properties, * 也能够在配置文件中指定基础名 spring.messages.basename */ String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages"); messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename()))); } if (properties.getEncoding() != null) { messageSource.setDefaultEncoding(properties.getEncoding().name()); } /* * 若是没有找到特定语言环境的文件,是否返回系统区域设置 * 默认为true * 若是是关闭的,将会使用惟一的默认文件:好比baseName的“message”的 message.properties */ messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale()); Duration cacheDuration = properties.getCacheDuration(); if (cacheDuration != null) { messageSource.setCacheMillis(cacheDuration.toMillis()); } /* * 设置是否始终应用消息格式组件,解析没有参数的消息 * 好比:MessageFormat但愿单引号被转义为""", * 若是消息文本所有使用这样的转义编写,即便没有定义参数占位符,也须要将此标志设为true * 不然,只有具备实际意义的参数消息文本才会用MessageFormat的转义来编写 */ messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat()); /* * 是否使用消息代码做为默认消息,而不是抛出NoSuchMessageException异常, * 适用于开发和调试,默认值为false */ messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); return messageSource; }
MessageSource解析:html
- MessageSource架构图:
![]()
- MessageSource: 抽象化的消息接口
- HierarchicalMessageSource: 分层的消息源接口,可获取父消息源
- MessageSourceSupport: 消息源解析的抽象类,经过指定"消息格式化组件MessageFormat"格式化消息
- DelegatingMessageSource: 消息源解析委派类. 用户未指定消息源解析类时,SpringContext默认使用这个类. 功能比较简单:将字符串和参数数组格式化为一个消息字符串
- AbstractMessageSource: 支持"配置文件"的方式国际化资源的抽象类. 内部提供一个与区域设置无关的公共消息配置文件,消息代码为关键字
- StaticMessageSource: 主要用于程序测试. 容许经过编程的方式提供国际化信息
- ResourceBundleMessageSource: 该实现类容许用户经过beanName指定一个资源名,包括类的全限定资源名. 或者经过beanName指定一组资源名. 不一样的区域获取加载不一样资源文件,以达到国际化的目的
- ReloadableResourceBundleMessageSource:
- ReloadableResourceBundleMessageSource和ResourceBundleMessageSource的区别:
- 加载资源类型及方式:
- ReloadResourceBundleMessageSource依托Spring的ResourceLoader加载Resource资源,功能更增强大,支持 .class和 .properties文件
- ResourceBundleMessageSource依托JDK自带的ResourceBundle加载资源,支持绝对路径和工程路径,支持 .class和 .properties文件
- 缓存时间:
- ReloadResourceBundleMessageSource每次加载都会记录每一个资源加载的时间点,在缓存资源过时后会再次比较文件的修改时间,若是不变则不须要加载,同时刷新本次加载时间点
- ResourceBundleMessageSource主要利用ResourceBundle.Control实现简单的自动加载
- 编码方式:
- ReloadResourceBundleMessageSource不只能够指定统一的默认编码方式,也同时支持为每一个文件单独制定编码方式
MessageSource接口:java
方法 描述 String getMessage(String code, Object[] args, String defaultMessge, Locale locale) 获取消息,若是没有找到消息,就返回默认值 String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException 获取消息,若是没法找到消息,则视为错误 String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException 尝试使用传入的{@code MessageSourceResolvable}参数中包含的全部属性来解析消息. 必须在此方法上抛出{@code NoSuchMessageException}, 由于在调用此方法时,没法肯定可解析的{@code defaultMessage}属性是否为空 MessageSourceResolvable解析消息要素的包装接口和类:web
方法 描述 String[] getCode() 返回用于解决此消息的代码,按照这些代码应该尝试的顺序. 所以,最后的一个代码将是默认代码 Object[] getArguments() 返回要用于解析此消息的参数数组 String getDefaultMessage() 返回要用于解析此消息的默认消息 HierarchicalMessageSource消息源分层接口:spring
方法 描述 void setParentMessageSource(MessageSource parent) 设置将用于解决次对象没法解析的消息的父级
参数parent是将用于解析此对象没法解析的消息的父MessageSource.多是{@code null},在这种状况下不须要解决MessageSource getParentMessageSource() 返回当前MessageSource的父级,不然返回{@Code null} MessageSourceSupport用于支持消息源解析的抽象类:编程
方法 描述 void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) 设置是否始终应用消息格式组件,解析没有参数的消息
好比: MessageFromat但愿单引号转义为"""
若是消息文本所有用这样的转义编写,即便没有定义参数占位符,只须要将此标志设为"true"
不然,只有具备实际参数的消息文本才会用MessageFormat转义类编写boolean isAlwaysUseMessageFormat() 返回是否应用消息格式组件,解析没有参数的消息 String renderDefaultMessage(String defaultMessage, Object[] args, Locale locale) 渲染给定的默认消息字符串 String formatMessage(String msg, Object[] args, Locale locale) 渲染给定的消息字符串 MessageFormat createMessageFormat(String msg, Locale locale) 为给定的消息和区域设置建立一个MessageFormat DelegatingMessageSource消息源解析委派类:json
方法 描述 String getMessage(String code, Object[] args, String defaultMessage, Locale locale) 解析消息
父消息解析源不为null时,则采用父消息源解析消息.不然使用自身消息源解析消息String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException 解析消息
若是父消息解析源不为null时,则采用父消息源解析消息,不然抛出异常String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException 解析消息
若是父消息解析源不为null时,则采用父消息源解析消息,不然使用自身消息源解析消息AbstractMessageSourc抽象类Spring中支持配置文件的方式国际化资源的抽象类:数组
方法 描述 void setUseCodeAsDafaultMessage(boolean useCodeAsDefaultMessage) 设置是否使用消息代码做为默认消息,而不是抛出NoSuchMessageException.默认为false String getMessageInternal(String code, Object[] args, Locale locale) 将给定的代码和参数解析为给定的区域中设置的消息,若是没有找到则返回{@code null} String getMessageFromPArent(String code, Object[] args, Locale locale) 若是父MessageSource中存在消息则尝试从父MessageSource检索给定的消息 String getDefaultMessage(String code) 返回默认消息 Object[] resolveArgements(Object[] args, Locale locale) 经过给定的参数数组搜索,找到MessageSourceResolve对象并解析 String resolveCodeWithoutArguments(String code, Locale locale) 解析不带参数的消息 StaticMessageSource是AbstractMessageSource容许经过编程的方式提供国际化信息:浏览器
方法 描述 void addMessage(String code, Locale locale, String msg) 将给定的消息与给定的代码相关联 void addMessage(Map<String, String> messages, Locale locale) 批量将给定的消息与给定的代码相关联 ResourceBundleMessageSource是AbstractMessageSource的实现类,容许用户经过beanName指定一个资源名- 包括类路径的全限定名, 或者经过beanNames指定一组资源名:缓存
方法 描述 void setBaseName(String basename) 设置资源文件 void setBaseNames(String... basenames) 批量设置资源文件 void setDefaultEncoding(String defaultEncoding) 设置用于解析绑定的资源文件的默认字符集 void setFallBackToSystemLocale(boolean fallbackToSystemLocale) 若是没有找到特定语言环境的文件,是否返回到系统区域设置
默认为true. 若是为false,惟一的备用文件将是默认文件void setCacheSeconds(int cacheSeconds) 设置缓存加载绑定的资源文件的秒数 String resolveCodeWithoutArguments(String code, Locale locale) 将给定的消息代码解析为已注册资源包中的key,按照原样返回捆绑包中的值,不使用MessageFormat解析 MessageFormat resolveCode(String code, Locale locale) 将给定的消息代码解析为注册资源包中的key,每一个消息代码使用缓存的MessageFormat实例 ResourceBundle getResourceBundle(String baseName, Locale locale) 为给定的baseName和代码返回一个ResourceBundle,从缓存中提取已生成的MessageFormat ResourceBundle doGetBundle(String baseName, Locale locale) throws MissingResourceException 获取给定baseName和locale设置的资源包 MessageFormat getMessageFormat(ResourceBundle resourceBundle, String code, Locale locale) throws Missing ResourceException 为给定的包和代码返回一个MessageFormat,从缓存中提取已生成的MessageFormats String getStringOrNull(ResourceBundle resourceBundle, String key) 获取资源包中指定key所对应的值 ReloadableResourceBundleMessageSource实现类容许用户经过beanName指定一个资源名,包括类路径和全限定名.或者经过beanNames指定一组资源名:restful
方法 描述 String resolveCodeWithoutArguments(String code, Locale locale) 将消息代码解析为检索到的包文件中的key,按原样返回包中找到的值,不使用MessageFormat解析 MessageFormat resolveCode(String code, Locale locale) 将给定的消息代码解析为检索到的包文件中的key,每一个消息代码使用缓存的MessageFormat实例 PropertiesHolder getMergedProperties(Locale locale) 获取locale所对应的持有properties对象 List< String > calculateAllFilenames(String basename, Locale locale) 计算给定的捆绑包基础名称和区域设置的全部文件名
将计算给定区域设置的文件名,系统区域设置默认文件List < String > calculateFilenamesForLocale(String basename, Locale locale) 计算给定捆绑基础包名称和区域设置的文件名 Properties loadProperties(Resource resource, String filename) 解析给定的resource资源,返回对应的properties对象 void clearCache() 清除全部资源包对应的properties文件 void clearCacheIncludingAncestors() 清除当前MessageSource及全部父资源的缓存
- MessageFormat消息组件格式化: 主要就是将消息串,参数格式化成字符串
3.在页面获取国际化的值
标签体中: th:text="#{}" th:placeholder="#{}" 非标签体,行内表达式 [[#{}]]
@Bean @ConditionalOnMissingBean @ConditionalOnProperty( prefix = "spring.mvc", name = {"locale"} ) // 默认的区域信息解析器就是根据请求头的区域信息获取Locale进行国际化解析 public LocaleResolver localeResolver() { if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) { return new FixedLocaleResolver(this.mvcProperties.getLocale()); } else { AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); localeResolver.setDefaultLocale(this.mvcProperties.getLocale()); return localeResolver; } } public Locale resolveLocale(HttpServletRequest request) { Locale defaultLocale = this.getDefaultLocale(); if (defaultLocale != null && request.getHeader("Accept-Language") == null) { return defaultLocale; } else { Locale requestLocale = request.getLocale(); List<Locale> supportedLocales = this.getSupportedLocales(); if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) { Locale supportedLocale = this.findSupportedLocale(request, supportedLocales); if (supportedLocale != null) { return supportedLocale; } else { return defaultLocale != null ? defaultLocale : requestLocale; } } else { return requestLocale; } } }
th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"
普通 CRUD(URI来区分操做) | RESTful CRUD | |
---|---|---|
查询 | getEmp | emp--GET |
添加 | addEmp?xxx | emp--POST |
修改 | updateEmp?id=xx&xxx | emp/{id}--PUT |
删除 | deleteEmp?id=xx | emp/{id}--DELETE |
请求URI | 请求方式 | |
---|---|---|
查询全部员工 | emps | GET |
查询某个员工(来到修改页面) | emp/{id} | GET |
进入添加页面 | emp | GET |
添加员工 | emp | POST |
进入修改页面(查出员工信息进行回显) | emp/{id} | GET |
修改员工 | emp/{id} | PUT |
删除员工 | emp/{id} | DELETE |
<div th:fragment="copy"> </div>
<div th:insert="~{footer :: copy}"></div> 引入公共片断的两种方式: ~{templatename::selector} 模板名::选择器 ~{templatename::fragmentname} 模板名::片断名 其中模板名(公共片断来源的文件名)会使用thymeleaf的先后缀配置规则进行解析
<div th:insert="footer :: copy"></div> <div th:replace="footer :: copy"></div> <div th:include="footer :: copy"></div>
C:
U:
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put("timestamp", new Date()); this.addStatus(errorAttributes, webRequest); this.addErrorDetails(errorAttributes, webRequest, includeStackTrace); this.addPath(errorAttributes, webRequest); return errorAttributes; }
2.BasicErrorController:处理默认/error请求
@Controller @RequestMapping({"${server.error.path:${error.path:/error}}"}) public class BasicErrorController extends AbstractErrorController { private final ErrorProperties errorProperties; public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) { this(errorAttributes, errorProperties, Collections.emptyList()); } public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) { super(errorAttributes, errorViewResolvers); Assert.notNull(errorProperties, "ErrorProperties must not be null"); this.errorProperties = errorProperties; } public String getErrorPath() { return this.errorProperties.getPath(); } @RequestMapping( produces = {"text/html"} ) //产生html数据,处理浏览器发送的请求 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); // 去哪一个页面做为错误页面,包含页面地址和页面内容 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView("error", model); } @RequestMapping //产生json数据,处理其它客户端请求 public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = this.getStatus(request); return new ResponseEntity(body, status); }
3.ErrorPageCustomizer:系统出现4开头和5开头的错误,该组件生效,定制错误响应规则.就会来到/error请求.
@Value("${error.path:/error}") private String path = "/error"; //系统出现错误之后来到error请求进行处理
4.DefaultErrorViewResolver:
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { // 默认SpringBoot能够找到页面-error/404 String errorViewName = "error/" + viewName; // 若是模板引擎能够解析这个页面地址就使用模板引擎解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); // 模板引擎可用的话返回到errorViewName指定的视图地址;若是模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面.假如静态资源文件夹没有对应的页面则返回null return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model); }
错误处理步骤:
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { Iterator var5 = this.errorViewResolvers.iterator(); ModelAndView modelAndView; do { if (!var5.hasNext()) { return null; } ErrorViewResolver resolver = (ErrorViewResolver)var5.next(); modelAndView = resolver.resolveErrorView(request, status, model); } while(modelAndView == null); return modelAndView; }
@ControllerAdvice public class MyExceptionHandler { //没有自适应效果-浏览器和客户端都是返回的json数据 @ResponseBody @ExceptionHandler(RuntimeException.class) public Map<String,Object> handleException(Exception e){ Map<String,Object> map=new HashMap<>(); map.put("code","运行异常"); map.put("message",e.getMessage()); return map; } }
2.转发到forward:/error进行自适应响应效果处理
@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(RuntimeException.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map=new HashMap<>(); // 传入本身的错误状态码,不然就不会进入定制错误页面的解析流程--------Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code"); request.setAttribute("javax.servlet.error.status_code","500"); map.put("code","运行异常"); map.put("message",e.getMessage()); //转发到/error,实现自适应效果 return "forward:/error"; } }
3.将定制数据携带出去:出现错误之后,会来到/error请求,这个请求会被BasicErrorController处理,响应的数据是由getErrorAttributes(由AbstractErrorController(ErrorController)规定的方法)获得的
@Component public class MyErrorAttributes extends DefaultErrorAttributes { //返回值map是页面和json能获取的全部字段 @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> map=super.getErrorAttributes(webRequest, includeStackTrace); map.put("company","oxford"); //异常处理器携带的数据 Map<String,Object> ext=(Map<String, Object>) webRequest.getAttribute("ext",0); map.put("ext",ext); return map; } }