一、Thymeleaf簡介
Thymeleaf模板引擎主要用來做視圖的展示。在springboot中默認支持thymeleaf,來替代原來ssm項目中的jsp。相較於jsp或其他的模板引擎,thymeleaf有如下特點:
1)動靜結合,thymeleaf 既可以在有后台交互的情況下運行,也可以在不與后台交互的情況下運行,方便前后端開發人員協同開發;
2)多方言的支持,支持spring的標准方言,可以和springboot完美整合;
二、Thymeleaf 使用
1)在pom.xml文件中導入依賴;
<!-- thymeleaf 模板依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
2) 在templates 目錄下新建html頁面,並加入thymeleaf的命名空間即可使用,Thymeleaf的命名空間為:xmlns:th="http://www.thymeleaf.org";
3)引入命名空間之后,我們便可以使用thymeleaf的語法來展示數據;
三、Thymeleaf 的語法
Thyemeleaf的使用與jsp中的jstl和el表達式使用方法相似;
Thymeleaf 表達式:用來取值,寫在thymeleaf屬性標簽中。
1) ${} :從域中取值,與el表達式類似;
注意:當對象不存在的情況下,去獲取對象的屬性的話,會拋出異常;
所以取值的時候,如果可能存在不存在的情況,需在對象后添加?判斷對象是否存在;
默認從request域中取值;
常見的內置對象:
1)session: 從session中獲取值,類似jsp中的${session} == ${sessionScope}
2)request: httpServletRequest對象,${request} == ${pageContext.request.}
3)servletContext: ServletContext對象(application域)
4)ctx : 上下文對象
5)vars: 上下文變量
6)local: 上下文的語言環境;
2) *{} :選擇變量表達式,需要配合th:object 屬性標簽一起使用。th:object可以綁定一個對象,*{屬性名} 去獲取綁定的對象的屬性;
3) #{} :獲取國際化消息表達式;
4) ~{} :代碼塊表達式,用來加載代碼片段。 需配合 th:replace th:insert th:include 三個屬性標簽使用。類似 <%@include >;
<body> <!-- insert 插入代碼片段 ,包含最外層的標簽 ~{模板名稱::代碼片段名稱} </head><header> <div>頭部導航欄</div> <ul> <li>首頁</li> <li>文章</li> </ul> </header></div> --> <div th:insert="~{admin/common::head}"> 原來內容 </div> <table> <tr> <td>序號</td> <td>用戶名</td> <td>密碼</td> <td>狀態</td> <td>創建時間</td> <td>操作</td> </tr> <!-- for(: xx) th:each="遍歷出來的單個對象,iterStat(狀態對象):要遍歷的集合 --> <tr th:object="${admin}" th:each="admin,iterStat:${adminPageInfo.list}"> <td th:text="${iterStat.count}">序號</td> <td th:text="*{account}">用戶名</td> <td th:text="*{password}">密碼</td> <td> <!-- th:if 判斷標簽是否顯示 --> <span th:if="*{status eq '0'}" style="color: coral">正常</span> <span th:if="*{status eq '1'}" style="color: red">注銷</span> </td> <td th:text="*{createtime}">創建時間</td> <td> <!-- @{} 中鏈接地址需要傳值的,通過在鏈接地址后面添加(key=value,key2=value2)的形式添加 --> <a th:href="@{/admin/edit(id=*{id})}">修改</a> <a th:href="@{/admin/delete(id=*{id})}">刪除</a> </td> </tr> </table> <!-- 替換內容,將引入的標簽,替換掉現有標簽 , 標簽內容全部過來 <footer> <div>版權所有,翻版必究</div> </footer> --> <div th:replace="~{admin/common::footerDiv}"> <span>原來內容</span> </div> <!-- include 加載代碼片段, 不包含最外面的標簽 <div> <div>版權所有,翻版必究</div> </div> --> <div th:include="~{admin/common::footerDiv}"> <span>原來內容</span> </div> </body>
5) @{} :用來定義鏈接url地址。 比如 img src a href <script > <link>等;
四、Thymeleaf 屬性標簽
編寫在html標簽上,替代原有的html標簽屬性,以達到動態展示數據。
Thymelaef屬性標簽都是以th:開頭。幾乎涵蓋了html標簽中所有的屬性。
常見的標簽:
1) th:text :設置當前標簽 的文本內容;
2) th:value : 設置當前元素的value值;
3) th:utext: 設置當前元素的html內容;
4) th:title ;
5) th:if :相當於<c:if> 用來做判斷,如果表達式為false,則當前標簽不顯示;
6) th:each :相當於<c:foreach> ,用來遍歷數據;
7) th:object : 聲明變量,配合*{} 一起使用;
8) th:fragment :用來定義一個代碼片段,以供th:insert replace include 調用;
9) th:insert : 將代碼片段的所有內容(包含最外層的標簽)插入到使用th:insert的html標簽中。 <div th:insert=”~{}”></div>;
10) th:replace : 將代碼片段替換掉使用th:insert的html標簽;
11) th:include :將代碼片段的內容(不包含最外層的標簽)插入到使用th:insert的html標簽中;
五、Thymeleaf 函數
Thymeleaf 函數寫在表達式中,用來對數據進行數據格式話,字符串操作,集合操作等
常用的函數:
1) #strings: 字符串操作函數,跟java中string的api類似。 跟jstl el 表達式中 fn:函數標簽類似。
2) #dates :用來對日期進行操作,日期的格式化,獲取日期的年月日,創建日期等
3) #numbers: 用來對數值進行格式化,保留指定小數位,分隔符展示等
4) #arrays 數組的操作,獲取數組長度,是否包含某個元素等等。。。
5) #maps: map集合操作
6) 。。。
測試代碼:
@Controller public class FunctionController { @GetMapping("/function") public String function(ModelMap modelMap){ modelMap.put("name","james"); modelMap.put("birthday",new Date()); modelMap.put("arrayData",new String[]{"james","yao","yi"}); return "function.html"; } }
function.html界面:
<body> <h1>字符函數</h1> 長度:<span><div th:text="${#strings.length(name)}"></div></span></br> 首字母大寫:<span><div th:text="${#strings.capitalize(name)}"></div></span></br> contains:<span><div th:text="${#strings.contains(name,'jam')}"></div></span></br> isEmpty:<span><div th:text="${#strings.isEmpty(name)}"></div></span></br> substring:<span><div th:text="${#strings.substring(name,0,2)}"></div></span></br> <h1>日期函數</h1> 不格式化:<span th:text="${birthday}"></span><br/> format 指定格式:<span th:text="${#dates.format(birthday,'yyyy-MM-dd HH:mm:ss')}"></span><br/> format不指定格式:<span th:text="${#dates.format(birthday)}"></span><br/> year:<span th:text="${#dates.year(birthday)}"></span><br/> month:<span th:text="${#dates.month(birthday)}"></span><br/> dayOfWeekName:<span th:text="${#dates.dayOfWeekName(birthday)}"></span><br/> 創建日期:<span th:text="${#dates.createToday()}"></span><br/> <h1>數值函數</h1> 整數的格式化:<span th:text="${#numbers.formatInteger(100,5)}"></span><br/> <!-- COMMA 逗號 POINT.WHITESPACE 空格--> 整數的格式化帶分隔符:<span th:text="${#numbers.formatInteger(100,5,'COMMA')}"></span><br/> <!-- 前兩個參數與整數一樣,第三個參數為保留的小數位,四舍五入--> 小數的格式化:<span th:text="${#numbers.formatDecimal(100.94876,5,3)}"></span><br/> <!-- COMMA 逗號 POINT.WHITESPACE 空格--> 小數的格式化帶分隔符:<span th:text="${#numbers.formatDecimal(100.94876,3,3,'COMMA')}"></span><br/> 百分比:<span th:text="${#numbers.formatPercent(0.783646,5,3)}"></span><br/> <h1>數組函數arrays</h1> 數組長度:<span th:text="${#arrays.length(arrayData)}"></span><br/> 是否為空:<span th:text="${#arrays.isEmpty(arrayData)}"></span><br/> 是否包含:<span th:text="${#arrays.contains(arrayData,'james')}"></span><br/> <h1>list集合函數</h1> <!-- #lists #maps #sets--> <!--<span th:text="${#lists.contains()}"></span>--> </body>
測試結果為:
六、Springboot中文件上傳下載
Springboot中文件上傳下載與springmvc 完全一致。
1)控制層代碼:
@Controller public class FileController { @GetMapping("/toUpload") public String toUpload(){ return "upload.html"; } @PostMapping("/upload") public String upload(MultipartFile multipartFile, ModelMap modelMap) throws IOException { // if (multipartFile==null){ throw new FileNotFoundException(); } //獲取文件名 String filename = multipartFile.getOriginalFilename(); // 日期 時間戳+用戶ID //隨機一個uuid名稱 String randName = UUID.randomUUID().toString(); //a.png String fileType = filename.substring(filename.lastIndexOf(".")); List<String> allowType= Arrays.asList(new String[]{".png",".jpg"}); if (!allowType.contains(fileType)){//不允許的文件類型 throw new NotAllowFileTypeException(" 001","上傳的格式不支持:"+fileType); } Date date = new Date(); SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd"); String today=sdf.format(date); String path="E:/headpic/"+ today; //創建文件對象用來指定保存的位置和名稱 File file=new File(path,randName+fileType); //如果父目錄不存在,創建 if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } //將文件保存到對應的目錄 multipartFile.transferTo(file); modelMap.put("filePath",today+"/"+randName+fileType); modelMap.put("fileName",randName+fileType); return "upload.html"; }; @GetMapping("/download") public ResponseEntity<byte[]> download(String fielName,String filePath) throws IOException { File file=new File("E:/headpic/",filePath); if(!file.exists()|| !file.isFile()){ throw new FileNotFoundException("未找到文件:"+filePath); } HttpHeaders httpHeaders=new HttpHeaders(); //設為返回的數據類型為二進制流 httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); //設置瀏覽器以附件形式打開下載 httpHeaders.setContentDispositionFormData("attachment",fielName); byte[] bytes = FileCopyUtils.copyToByteArray(file); return new ResponseEntity<>(bytes,httpHeaders, HttpStatus.OK); } }
2)后台upload.html頁面代碼為:
<body> <form th:action="@{/upload}" method="post" enctype="multipart/form-data"> 請選擇文件:<input type="file" name="multipartFile"> <input type="submit" value="上傳"> </form> <a th:href="@{/download(fileName=*{fileName},filePath=*{filePath})}" th:text="${'點擊下載'+fileName}"></a> </body>
3)要想實現文件的上傳和下載,必須要配置資源映射;
/** * springmvc相關配置類 * WebMvcConfigurer 用來配置springmvc的配置項 */ @Configuration//配置類注解 public class WebConfig implements WebMvcConfigurer { /** * 配置資源映射,相當於xml中的 <resource></resource> * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/headpic/**")//配置請求地址 .addResourceLocations("file:E:/headpic/");//配置資源目錄 } }
4)最后我們的界面是這樣的;
七、Springboot全局異常處理
Spring3.2之后,可以通過@ControllerAdvice+@ExceptionHandler 這兩個注解來實現全局的異常處理。
@ControllerAdvice 加載類上面;
@ExceptionHandler 添加在異常處理的方法上面;
步驟:
1)寫一個全局異常處理的類,並添加@ControllerAdvice注解;
2)寫對應異常處理的方法,並在方法上添加@ExceptionHandler注解;
@ControllerAdvice public class GlobalExceptionHandler { /** * @ExceptionHandler:指定處理特定異常,當項目發生指定的異常時,便會進入此方法 * @param e * @return */ @ExceptionHandler(FileNotFoundException.class) public Object handlerException(FileNotFoundException e){ String message = e.getMessage(); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("error/fileNotFound.html"); modelAndView.addObject("msg",message); return modelAndView; } @ExceptionHandler(Exception.class) public ModelAndView handlerException(Exception e){ String message = e.getMessage(); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("error/fileNotFound.html"); modelAndView.addObject("msg",message); return modelAndView; } }
3)寫一個自定義異常的NotAllowFileTypeException.java類;
/** * 自定義異常 */ public class NotAllowFileTypeException extends RuntimeException{ private String code; public NotAllowFileTypeException(String message, String code){ super(message); this.code=code; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } }
4)fileNotFound.html頁面;
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>文件找不到</title> </head> <body> <h1>您查找的文件不存在</h1> <div th:text="${msg}"></div> </body> </html>
5)我們現在先測試第一種情況,拋FileNotFoundException 的異常,是一個文件找不到的異常,我們還是在上一個文件上傳下載的頁面中來測試;
①、我們的方法時,先上傳圖片,然后再去那個文件夾中把所有圖片刪掉即可;
點擊鏈接下載,正常情況下,是可以下載的,但是
由於我們把文件夾中的圖片刪除了,文件夾里面就沒有圖片了,就會走fileNotFound.html頁面;拋出異常;
結果就是這樣的:
②我們再測試一個文件類型不對的異常,使用普通的異常Exception,當我們上傳.png,.jpg以外格式的圖片時,就會拋出異常;
點擊上傳,
八、Springboot中的攔截器
1)配置攔截器
/** * springmvc相關配置類 * WebMvcConfigurer 用來配置springmvc的配置項 */ @Configuration//配置類注解 public class WebConfig implements WebMvcConfigurer { /** * 配置springmvc攔截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor())//添加攔截器對象 .addPathPatterns("/**")//指定需要攔截的地址 .excludePathPatterns("/admin/login","/admin/toLogin","/headpic/**")//指定放行的請求 .order(1);//指定順序 } }
2)登錄攔截;
控制層:
//跳轉到登錄頁面 @RequestMapping("/toLogin") public String toLogin(){ return "/admin/login.html"; } @PostMapping("/login") public String login(TAdmin tAdmin, HttpSession session,ModelMap modelMap){ TAdmin tAdmin1=adminService.selectByAccount(tAdmin.getAccount()); if (tAdmin1==null||!tAdmin.getPassword().equals(tAdmin1.getPassword())){ //回傳登錄信息 modelMap.put("admin",tAdmin); modelMap.put("msg","用戶名密碼不正確"); return "/admin/login.html"; } //將對象放session域中 session.setAttribute("admin",tAdmin1); return "/admin/detail.html"; }
登錄攔截器LoginInterceptor,java;
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { TAdmin admin = (TAdmin) request.getSession().getAttribute("admin"); if (admin!=null){ return true; } response.sendRedirect(request.getContextPath()+"/admin/toLogin"); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
這樣,攔截器的話,差不多就可以了;