@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; } }