溫馨提示 : 本 blog 皆使用專案管理工具 Maven,其相關依賴要自己去https://mvnrepository.com/ 複製唷!
What is Spring MVC?
為 Spring framework 中的一個模組(webmvc),設計模式上為前置控制器模式(Front Controller Pattern)。
在 Spring MVC 中,前置控制器稱為 DispatcherServlet,底層依然依靠 Servlet API 運作。
使用概念 :
- 使用 Spring MVC 提供之 API Controller 來撰寫控制器(Controller)
- 其也提供了 Model,View 等 API
- 一樣使用 IoC/DI 將 Controller 託管至 IoC 容器
- DispatcherServlet 收到請求(Request),會控制交由指定控制器做處理
Front Controller Pattern
- Design Pattern 中,針對 MVC 架構提出的模式
- 所有請求集中給 Front Controller 接收,再經由⼀個分派器(Dispatcher),將請求分派給對應的控制器處理
- 處理完後回到前置控制器,再由前置控制器取得 View
- 最後由前置控制器做出回應(Response)
DispatcherServlet
- 將前置控制器與分派器合而為一,型態為 DispatcherServlet
- 為 Servlet API 之類別 HttpServlet 之子代類別
- 在⼀般使⽤情況下,不會直接使⽤到 DispatcherServlet 物件,而是透過 web.xml 註冊
- Spring MVC 中唯一的 Servlet
MVC 架構
Spring MVC 的 MVC 架構,是以 DispatcherServlet 為中⼼設計,再配合其他重要物件來完成請求回應
- HandlerMapping : 網址與控制器的映射表
- Controller : 控制器,即 Spring MVC 中的控制器 類別/⽅法
- Model : 在此僅指 Model 層運算完的結果
- ViewResolver : View 解析器,可將 View 的邏輯名稱解析成真實的 View
- View : 真實的 View,EX. JSP 檔
註冊 DispatcherServlet
⼀般會將 DispatcherServlet 設定映射網站根路徑”/“,即對此網站的所有請求,皆會經過 DispatcherServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?xml version="1.0" encoding="UTF-8"?> <web-app ..略>
<servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value>core.config.MvcConfig</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
</web-app>
|
同時註冊核心組態類別
除了加上@Configuration 以外,需要實作 WebMvcConfiguer 介面
以及做驅動設定@EnableWebMvc,還需要加上@ComponentScan 將 Controller 做掃描
1 2 3 4 5 6
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer {
}
|
託管 ViewResolver 元件與靜態資源管理
託管 ViewResolver
需要 override configureViewResolvers(),實作並且設定
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/"); viewResolver.setSuffix(".jsp"); viewResolver.setContentType("text/html;charset=UTF-8"); registry.viewResolver(viewResolver); } }
|
靜態資源管理
需要針對靜態資源處理(DefaultServlet)以及重新映射靜態資源
靜態資源處理(DefaultServlet) : 覆寫 configureDefaultServletHandling()
重新映射靜態資源 : 覆寫 addResourceHandlers()
(可隱藏真實路徑)
在 MvcConfig 類別中 override configureDefaultServletHandling()
1 2 3 4 5 6 7 8 9
| @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("/WEB-INF/"); }
|
如果有一靜態資源 /WEB-INF/index.html,會被重新映射至/index.html
Model 支援
- Spring MVC 實作 MVC 概念,其支援了 Model(指 Model 層運算完之結果)
- 提供 Model 相關 API,做資料綁定(Data Binding),其資料即為 Servlet API 中的屬性物件(Attribute Object)
- 取代 Servlet API 中,scope object 寫法
像是 request.setAttribute、request.getAttribute
大部分 MVC / MVVM 框架,對 Model 支援僅有資料綁定
使用@ModelAttribute(“識別名”)來綁定屬性物件
- ⽤在⽅法 : 綁定⽅法的回傳值。執⾏控制器⽅法前,會先執⾏此⽅法
- ⽤在控制器⽅法的參數 : 綁定請求參數
每次對 member/useModelAttribute1 發出請求,會先呼叫 memberList(),並綁定此⽅法的回傳值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Controller @RequestMapping("member") public class MemberController { @Autowired private MemberService service;
@GetMapping("useModelAttribute1") public String useModelAttribute1() { return "member/manage"; } @ModelAttribute("memberList") private List<Member> memberList() { return service.findAll(); } }
|
請求參數會⾃動封裝⾄ member 中,加上@ModelAttribute 會再將其綁定
1 2 3 4 5 6 7 8 9 10 11
| @Controller @RequestMapping("member") public class MemberController { @Autowired private MemberService service; @GetMapping("useModelAttribute2") public String useModelAttribute2(@ModelAttribute("member") Member member) { return "member/result"; } }
|
Session scope
如要綁定範圍物件,需先使用@ModelAttribute 綁定屬性物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Controller @RequestMapping("member") @SessionAttributes({ "member" }) public class MemberController { @Autowired private MemberService service; @PostMapping("login") public String login(Member member) { service.login(member); return "redirect:/result.jsp"; } @ModelAttribute("member") public Member member() { return new Member(); } }
|
為了取得 Session 範圍物件,使用@SessionAttribute(“識別名”)
**只能取得
1 2 3 4 5 6 7 8 9
| @Controller @RequestMapping("member") public class MemberController { @GetMapping("getInfo") public String getInfo(@SessionAttribute("member") Member member) { return "member/edit"; } }
|
RedirectAttributes
- Model 的⼦型態,用來綁定暫存(Flash)屬性物件
- Redirect 時可以共享屬性物件
- 接收到屬性物件後,自動取消綁定
- Jsp 無法使用
- 傳送端與接收端 :
- addFlashAttribute(“識別名”)
- @ModelAttribute(“識別名”)
- 若接收端要再往下共享,則可接著使⽤ Model 物件
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Controller public class TestController { @GetMapping("deliverer") public String deliverer(RedirectAttributes redirectAttributes) { redirectAttributes.addFlashAttribute("name", "William"); return "redirect:/recipient"; } @GetMapping("recipient") public String recipient(@ModelAttribute("name") String name, Model model) { model.addAttribute("name", name); return "some_page"; } }
|
若想要綁定屬性物件至 Context Scope,可以直接使用 Servlet API 中的 ServletContext 物件,透過@Autowired 注⼊,即可取得 ServletContext 物件
1 2 3 4 5 6
| @Controller public class TestController { @Autowired private ServletContext servletContext; }
|
ViewResolver
- ViewResolver 為 Spring MVC 重要物件,為 View 的解析器
- 將控制器回傳的 View 的邏輯名稱 解析成 真實的 View
- 真實的 View,包含網址中的路徑、檔名,及其他細節設定
- Spring MVC 架構中,⾄少要有⼀個 ViewResolver
- 共同⽗型態為 ViewResolver
InternalResourceViewResolver
- 未託管任何 View 解析器時,預設的 View 解析器
- 配合 InternalResourceView 使⽤,即 View 為 Servlet 或 JSP 或 HTML
- 將控制器回傳的 View 邏輯名稱,加上 前綴字/後綴字
- setPrefix(“前綴字”) : 設定 View 名稱的前綴字
- setSuffix(“後綴字”) : 設定 View 名稱的後綴字
- setContentType(“MIME Type”) :設定 View 內容的 MIME Type,亦可同時設定⽂字編碼
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/"); viewResolver.setSuffix(".jsp"); viewResolver.setContentType("text/html;charset=UTF-8"); registry.viewResolver(viewResolver); } }
|
若控制器回傳邏輯名稱”member/result”,則會解析出的真實名稱為”/WEB-INF/member/result.jsp”
ContentNegotiatingViewResolver
- Spring MVC 架構中,可同時有多個 View 解析器,用以對應多種 View 格式
- 所以需要 ContentNegotiatingViewResolver 來管理多個 View 解析器
- 解析方式是依照請求標頭(Request Header)中的 Accept 屬性之值,選擇對應的 View 解析器
1 2 3 4 5 6 7 8 9
| public class JsonViewResolver implements ViewResolver { @Override public View resolveViewName(String viewName, Locale locale) { MappingJackson2JsonView view = new MappingJackson2JsonView(); view.setPrettyPrint(true); return view; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer {
@Bean public ViewResolver jspViewResolver() { InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setPrefix("/WEB-INF/"); internalResourceViewResolver.setSuffix(".jsp"); return internalResourceViewResolver; }
@Bean public ViewResolver jsonViewResolver() { return new JsonViewResolver(); }
@Bean public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) { ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver(); cnvr.setContentNegotiationManager(manager);
cnvr.setViewResolvers(Arrays.asList(jspViewResolver(), jsonViewResolver())); return cnvr; }
@Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.defaultContentType(MediaType.TEXT_HTML); } }
|
RedirectView
- 不會經過 View 解析器的解析,所以內含的 View 名稱應為真實名稱
- 僅表⽰要重新導向,View 名稱可以是任何資源
- View 名稱不能包含”WEB-INF”
1 2 3 4 5 6 7 8 9 10 11
| @Controller @RequestMapping("member") public class MemberController { @Autowired private MemberService service; @PostMapping("login") public View login(Member member) { service.login(member); return new RedirectView("../index.html"); } }
|
跳脫ViewResolver
- 在某些情況下,確實會希望能不經過View解析器,意即跳脫View解析器
- 託管了InternalResourceViewResolver,固定會替View名稱..
- 前⾯加上”/WEB-INF/“;後⾯加上”.jsp”
- 但想轉發(forward/redirect)⾄”index.html”
- 除了使⽤RedirectView之外,Spring MVC另提供特殊前綴字,⽤以跳脫(不能包含”WEB-INF”)
特殊前綴字 :
- 定義在 UrlBasedViewResolver 型態
- FORWARD_URL_PREFIX : 值為”forward:”
- REDIRECT_URL_PREFIX : 值為”redirect:”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Controller @RequestMapping("member") public class MemberController { @Autowired private MemberService service; @GetMapping("login") public String login(Member member) { boolean result = service.login(member); if (result) { return "redirect: ../index.html"; } else { return "forward: login.html"; } } }
|
Spring 於 JSTL 支援
- 有定義自己的 JSTL(Jsp Standard Tag Library)
- JSP 這種伺服器端渲染(Server Side Render)技術,會造成伺服器負荷過高
- 現今則多採⽤客⼾端渲染(Client Side Render)技術,由客⼾端分擔負荷
現今有更好的選擇 – 前端框架
詳情請看講義。
欄位檢核
- 實作 View 層時,常需處理欄位檢核
- 欄位需符合當下定義的檢核條件,才會接續下⼀步動作
- 實作可使⽤ Hibernate Validator
- 另搭配 spring-form 標籤庫及 BindingResult 請看講義
@NotNull
@NotBlank
- 不可為 null/空字串
- 只能⽤在 CharSequence 型態
@NotEmpty
- 不可為 null/空
- 可⽤在 CharSequence、Array、Collection、Map 等型態
@Size(min = 長度下限, max = 長度上限)
- 元素個數上下限
- 可⽤在 CharSequence、Array、Collection、Map 等型態
- ⽤在 CharSequence 表⽰字元個數的上下限
@Pattern(regexp = “表示式”)
- 正規表⽰式(Regular Expression)
@Email
@Digital(integer = 整數位數, fraction = 小數位數)
@Max(最大值)、@Min(最小值)
@Positive、@Negative
@PositiveOrZero、@NegativeOrZero
@DateTimeFormat(pattern = “格式”)
- ⽇期時間格式
- 由 Spring 提供
- EX.@DateTimeFormat(pattern = “yyyy-MM-dd”)
@Past
@PastOrPresent
在 Spring MVC 中使用檔案傳輸
檔案可以放在
- 資料庫端 : BLOB
- applocation sever 端 EX. Tomcat 根⽬錄/files 可⽤以下⽅式取得 Tomcat 根⽬錄之路徑
以 Java based 為例
1 2
| @Value("#{systemProperties['catalina.home']}") private String tomcatRootPath;
|
在 web.xml 加上設定啟用檔案上傳功能
1 2 3 4 5 6 7 8 9
| <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> 略.. </init-param> <load-on-startup>1</load-on-startup> <multipart-config /> </servlet>
|
並在 pom.xml 中加入依賴 Apache Commons FileUpload 支援
1 2 3 4 5
| <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
|
託管 CommonsMultipartResolver 元件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer {
@Bean public CommonsMultipartResolver commonsMultipartResolver() { CommonsMultipartResolver resolver = new CommonsMultipartResolver(); resolver.setDefaultEncoding("UTF-8"); resolver.setMaxUploadSizePerFile(500 * 1024 * 1024); esolver.setMaxUploadSize(4 * 1024 * 1024 * 1024); return resolver; } }
|
檔案上傳
控制器方法 :
- 接收檔案 : 參數加上@RequestParam,型態 MultipartFile[]
- 另存新檔 (非必要) : 呼叫 transferTo()
1 2 3 4 5 6
| @PostMapping("upload") public void upload(@RequestParam("img") MultipartFile[] files) throws IOException { for (MultipartFile file : files) { file.transferTo(Paths.get(fileRootPath, file.getOriginalFilename())); } }
|
img 為請求參數的名稱,須對應前端(HTML/JavaScript)中的名稱
1 2 3 4
| <form action="file/upload" method="POST" enctype="multipart/form-data"> <input type="file" name="img" multiple> <input type="submit"> </form>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <input type="file" multiple> <button>Submit</button> <script> const input = document.querySelector('input'); document.querySelector('button').addEventListener('click', () => { const formData = new FormData(); for (let file of input.files) { formData.append('img', file, file.name); } fetch('file/upload', { method: 'POST', body: formData }); }); </script>
|
檔案下載
- 讓控制器⽅法回傳 byte[]
- 須設定 MIME TYPE
- @GetMapping(..,produces = MediaType.檔案的 MIME TYPE)
- @PostMapping(..,produces = MediaType.檔案的 MIME TYPE)
- 設定回傳值為是 HTTP 的回應本體(Response Body)
假設檔案放在 Tomcat 根⽬錄/files
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Controller @RequestMapping("file") public class FileController {
@Value("#{systemProperties['catalina.home'].concat('/files/')}") private String fileRootPath;
@GetMapping(path = "download", produces = MediaType.IMAGE_PNG_VALUE) @ResponseBody public byte[] download() throws IOException { return Files.readAllBytes(Paths.get(fileRootPath, "polar-bears.jpg")); } }
|
跳出下載視窗
- 若希望跳出下載視窗,則可將MIME Type設定成application/octet-stream
1 2 3 4 5 6 7 8
| @GetMapping( path = "download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE ) @ResponseBody public byte[] download() throws IOException { return Files.readAllBytes(Paths.get(fileRootPath, "polar-bears.jpg")); }
|
另外可以指定預設檔名,詳看講義
Spring MVC 的例外處理
- MVC 架構中,常將例外處理放在 Controller 層
- 其他層則是直接將例外拋出(throw),⽽不可捕捉(catch)
- Spring MVC 對此提供了⽀援,例外處理分成 類別範圍 跟 全域範圍
- 對此提供了 例外映射 View 的設定
規則 :
- 須加上 @ExceptionHandler(欲捕捉的例外型態.class)
- 可加⼊⼀個例外參數,型態為欲捕捉的例外型態
類別範圍的例外處理
1 2 3 4 5 6 7 8 9 10
| @Controller public class TestController { private static final Logger logger = LogManager.getLogger(MemberController.class);
@ExceptionHandler(SQLException.class) public String handleSQLException(SQLException exception) { logger.error(exception.getMessage(), exception); return "error"; } }
|
全域範圍例外處理
1 2 3 4 5 6 7 8 9
| @ControllerAdvice public class ExceptionAdvice { private static final Logger logger = LogManager.getLogger(ExceptionAdvice.class); @ExceptionHandler(SQLException.class) public String handleSQLException(SQLException exception) { logger.error(exception.getMessage(), exception); return "error"; } }
|
RESTful API
大重點,我真滴不想再寫 JSP ㄌ
在 RESTful API 有其精神,包含 4 個參數傳遞請求方法~
前後端分離架構
Web API
- 僅以回應本體(Response Body)回傳資料,不轉發(forward/redirect)
- 前端只需透過網址及相關參數(有時可能會加⼊驗證),就可以跟後端交換資料
EX. 透過網址 http://william.idv.tw/member/getInfo…
- 前端 : 傳”1”給後端
- 後端 : 回”1 ithan0117 William”(會員編號、使⽤者名稱、暱稱)給前端
- 資料傳遞,現今流行使⽤ JSON 格式
- 業界常將 Web API 簡稱為 API
RESTful API
- 一種架構風格(符合 REST ⾵格)
- 操作同⼀個資源(Resource),使⽤同⼀個網址(URI)
- 再以 HTTP 的請求⽅法(Request Method),來決定進⾏何種操作
- 精神為使用請求方法要符合作的事情
四個請求方法 :
- POST
- 操作 : 新增
- 參數傳遞方式 : 使用請求本體(Request Body)
- 格式 : 現今流⾏使⽤ JSON 格式
- PUT
- 操作 : 修改
- 參數傳遞方式 : 使用請求本體(Request Body)
- 格式 : 現今流⾏使⽤ JSON 格式
- DELETE
- 操作 : 刪除
- 參數傳遞方式 : 使用路徑變數(Path Variable)
- 格式 : ⽤網址路徑當參數
- GET
- 操作 : 查詢
- 參數傳遞方式 : 使用路徑變數(Path Variable)
- 格式 : ⽤網址路徑當參數
因為需要傳遞 JSON 資料需要做組態設定,Spring MVC 提供 MappingJackson2HttpMessageConverter 型態,此型態底層使用 JSON API 為 JACKSON
1 2 3 4 5
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.1</version> </dependency>
|
託管 MappingJackson2HttpMessageConverter
1 2 3 4 5 6 7 8 9 10 11 12
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer {
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); messageConverter.setPrettyPrint(true); converters.add(messageConverter); } }
|
對應各請求方法
- @PostMapping/@DeleteMapping/@PutMapping/@GetMapping
- 可⽤@RequestMapping(method = {方法 1,方法 2,…,方法 N}),對應多個請求⽅法
參數傳遞
- 路徑變數 : @PathVariable
- 請求本體 : @RequestBody
回應本體
- @ResponseBody
- 控制器類別改加上@RestController,則可省去@ResponseBody
- 可配合 POJO、List<POJO>、Map、void(無回應本體)、ResponseEntity 等型態
1 2 3 4
| @RestController @RequestMapping("member") public class MemberRestController { }
|
member,前端統⼀使用此網址,操作會員資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @PostMapping public Core create(@RequestBody Member member) { return service.register(member); }
@DeleteMapping("{id}") public Core delete(@PathVariable Integer id) { final boolean result = service.remove(id); Core core = new Core(); core.setSuccessful(result); core.setMessage(result ? "刪除成功" : "刪除失敗"); return core; }
@PutMapping public Core update(@RequestBody Member member) { final boolean result = service.save(member); Core core = new Core(); core.setSuccessful(result); core.setMessage(result ? "修改成功" : "修改失敗"); return core; }
@GetMapping("{id}") public Member read(@PathVariable Integer id) { return dao.selectById(id); }
@GetMapping public List<Member> readAll() { return dao.selectAll(); }
|
RESTful 也有檔案傳輸,以及 JSON 格式屬性對應和日期時間格式化,處理跨域請求,詳見講義
處理(做掉) DispatcherServlet(自行定義部屬描述類別)
- Servlet 3 後,web.xml 為非必要,可用 Java based 取代
- Spring MVC 有支援,可在專案啟動時,就啟用 DispatcherServlet(不需要再設定 loadOnStartup)
自行定義部屬描述類別 :
- 需要繼承 AbstractAnnotationConfigDispatcherServletInitializer
- 覆寫 getRootConfigClasses() –指定 Spring 核⼼組態類別
- 覆寫 getServletConfigClasses() – 指定 Spring MVC 核⼼組態類別
- 覆寫 getServletMappings() – 設定 DispatcherServlet 映射路徑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class<?>[] getRootConfigClasses() { return new Class[] { AppConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[] { MvcConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
|
設定filter
在 部屬描述類別 實作 getServletFilters()
1 2 3 4 5 6 7 8 9 10 11
| public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); OpenSessionInViewFilter openSessionInViewFilter = new OpenSessionInViewFilter(); return new Filter[] { characterEncodingFilter, openSessionInViewFilter }; } }
|
檔案上傳
需要使用再加即可
1 2 3 4 5 6 7 8
| public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig(new MultipartConfigElement("")); } }
|
預設首頁是放在 Spring MVC 組態類別而非部屬描述類別
- 同 web.xml 中的<welcome-file-list><welcome-file>
- 須配合 ViewResolver/靜態資源管理 使⽤
- 前面設定前綴字的那個要註解掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration @EnableWebMvc @ComponentScan("web.*.controller") public class MvcConfig implements WebMvcConfigurer {
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("/WEB-INF/"); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index.html"); } }
|
預設⾸⾴為 WEB-INF/index.html
跟 web.xml 說再見
pom.xml 預設下必須有 web.xml,要殺掉他需要設定略過檢查 web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0" encoding="UTF-8"?> <project 略..> <build> <finalName>spring-exercise</finalName> <plugins> <!-- 略 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> <!-- 大重點 --> </configuration> </plugin> </plugins> </build> </project>
|
Spring-WebSocket
dependency 自己加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ChatWebSocketHandler extends TextWebSocketHandler {
private Set<WebSocketSession> connectedSessionSet = Collections.synchronizedSet(new HashSet<>());
@Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { connectedSessionSet.add(session); }
@Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { for (WebSocketSession connectedSession : connectedSessionSet) { if (connectedSession.isOpen()) { connectedSession.sendMessage(message); } else { connectedSessionSet.remove(connectedSession); } } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { connectedSessionSet.remove(session); } }
|
自定義WebSocket核心組態類別
1 2 3 4 5 6 7 8 9 10 11 12
| @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler(), "/chat"); } @Bean public WebSocketHandler webSocketHandler() { return new ChatWebSocketHandler(); } }
|
前端程式詳見講義
Spring STOMP 為進階寫法,詳情請洽官方文件
Spring Scheduling
定義在模組 Spring-Context
自定義 Scheduling 核心組態類別
- 加上@Scheduled(cron = “CRON 表⽰式”) – 設定觸發時機
- 加上@Async – 設定非同步
- 撰寫想要做的事
1 2 3 4 5 6 7 8 9 10
| @Configuration @EnableScheduling @EnableAsync public class SchedulingConfig { @Scheduled(cron = "* * * * * *") @Async public void task1() { System.out.println("task1: "+ new Time(System.currentTimeMillis())); } }
|