Spring Boot Sample 009之spring-boot-web-thymeleaf
一、环境
- Idea 2020.1
- JDK 1.8
- maven
二、目的
spring boot 整合thymeleaf模板开发web项目。
三、步骤
3.1、点击File -> New Project -> Spring Initializer,点击next
3.2、在对应地方修改自己的项目信息
3.3、选择Web依赖,选中Spring Web。可以选择Spring Boot版本,本次默认为2.2.6,点击Next
3.4、项目结构
四、添加配置文件
添加配置文件logback.xml 通过springProfile属性识别不同环境配置
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/base.xml"/> <!-- logger name="org.springframework" level="DEBUG"/--> </configuration>
配置默认application.properties
# Allow Thymeleaf templates to be reloaded at dev time spring.thymeleaf.cache: false server.tomcat.access_log_enabled: true server.tomcat.basedir: target/tomcat
配置message.properties
form.message=Message form.messages=Messages form.submit=Submit form.summary=Summary form.title=Messages : Create list.create=Create Message list.table.created=Created list.table.empty=No messages list.table.id=Id list.table.summary=Summary list.title=Messages : View all navbar.messages=Messages navbar.thymeleaf=Thymeleaf view.delete=delete view.messages=Messages view.modify=modify view.success=Successfully created a new message view.title=Messages : View
添加css样式文件
/static/css/bootstrap.min.css
添加页面文件
templates/fragments.html
templates/messages/form.html
templates/messages/list.html
templates/messages/view.html
添加实体类
package org.ouyushan.springboot.web.thymeleaf.entity; import javax.validation.constraints.NotEmpty; import java.util.Calendar; /** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/4/29 14:33 */ public class Message { private Long id; @NotEmpty(message = "Text is required.") private String text; @NotEmpty(message = "Summary is required.") private String summary; private Calendar created = Calendar.getInstance(); public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public Calendar getCreated() { return this.created; } public void setCreated(Calendar created) { this.created = created; } public String getText() { return this.text; } public void setText(String text) { this.text = text; } public String getSummary() { return this.summary; } public void setSummary(String summary) { this.summary = summary; } }
添加repository接口
package org.ouyushan.springboot.web.thymeleaf.repository; import org.ouyushan.springboot.web.thymeleaf.entity.Message; /** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/4/29 14:35 */ public interface MessageRepository { Iterable<Message> findAll(); Message save(Message message); Message findMessage(Long id); void deleteMessage(Long id); }
添加repository实体类
package org.ouyushan.springboot.web.thymeleaf.repository; import org.ouyushan.springboot.web.thymeleaf.entity.Message; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; /** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/4/29 14:38 */ public class InMemoryMessageRepository implements MessageRepository{ private static AtomicLong counter = new AtomicLong(); private final ConcurrentMap<Long, Message> messages = new ConcurrentHashMap<>(); @Override public Iterable<Message> findAll() { return this.messages.values(); } @Override public Message save(Message message) { Long id = message.getId(); if (id == null) { id = counter.incrementAndGet(); message.setId(id); } this.messages.put(id, message); return message; } @Override public Message findMessage(Long id) { return this.messages.get(id); } @Override public void deleteMessage(Long id) { this.messages.remove(id); } }
添加controller类
package org.ouyushan.springboot.web.thymeleaf.controller; import org.ouyushan.springboot.web.thymeleaf.entity.Message; import org.ouyushan.springboot.web.thymeleaf.repository.MessageRepository; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.validation.Valid; /** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/4/29 14:42 */ @Controller @RequestMapping("/") public class MessageController { private final MessageRepository messageRepository; public MessageController(MessageRepository messageRepository) { this.messageRepository = messageRepository; } @GetMapping public ModelAndView list() { Iterable<Message> messages = this.messageRepository.findAll(); return new ModelAndView("messages/list", "messages", messages); } @GetMapping("{id}") public ModelAndView view(@PathVariable("id") Message message) { return new ModelAndView("messages/view", "message", message); } @GetMapping(params = "form") public String createForm(@ModelAttribute Message message) { return "messages/form"; } @PostMapping public ModelAndView create(@Valid Message message, BindingResult result, RedirectAttributes redirect) { if (result.hasErrors()) { return new ModelAndView("messages/form", "formErrors", result.getAllErrors()); } message = this.messageRepository.save(message); redirect.addFlashAttribute("globalMessage", "view.success"); return new ModelAndView("redirect:/{message.id}", "message.id", message.getId()); } @RequestMapping("foo") public String foo() { throw new RuntimeException("Expected exception in controller"); } @GetMapping("delete/{id}") public ModelAndView delete(@PathVariable("id") Long id) { this.messageRepository.deleteMessage(id); Iterable<Message> messages = this.messageRepository.findAll(); return new ModelAndView("messages/list", "messages", messages); } @GetMapping("modify/{id}") public ModelAndView modifyForm(@PathVariable("id") Message message) { return new ModelAndView("messages/form", "message", message); } }
启动程序类
package org.ouyushan.springboot.web.thymeleaf; import org.ouyushan.springboot.web.thymeleaf.entity.Message; import org.ouyushan.springboot.web.thymeleaf.repository.InMemoryMessageRepository; import org.ouyushan.springboot.web.thymeleaf.repository.MessageRepository; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.core.convert.converter.Converter; @SpringBootApplication public class SpringBootWebThymeleafApplication { @Bean public MessageRepository messageRepository() { return new InMemoryMessageRepository(); } @Bean public Converter<String, Message> messageConverter() { return new Converter<String, Message>() { @Override public Message convert(String id) { return messageRepository().findMessage(Long.valueOf(id)); } }; } public static void main(String[] args) { SpringApplication.run(SpringBootWebThymeleafApplication.class, args); } }
五、接口测试
访问: http://localhost:8080/?form ### Controller * 使用了@PathVariable 从路径中获取参数,注入参数中必须有一属性名称与PathVariable变量名称相同 * 使用params处理路径中请求参数 * 使用@Valid校验参数,BindingResult 存储校验错误,RedirectAttributes 缓存上级页面参数 ### repository * 利用ConcurrentHashMap模拟线程安全数据库 private static AtomicLong counter = new AtomicLong(); private final ConcurrentMap<Long,Message> messages = new ConcurrentHashMap<>(); ### application启动配置类 * 定义了messageRepository bean以及messageConverter bean * @SpringBootApplication same as @Configuration @EnableAutoConfiguration @ComponentScan post方式 create localhost:8080?id=1&text=text&summary=summary get查询id=1 http://localhost:8080/1 @GetMapping(params = "form") localhost:8080?form=&id=1 localhost:8080?form=&id=1&text=text&summary=summary ``` ${} 变量表达式(美元表达式,哈哈),用于访问容器上下文环境中的变量,功能同jstl中${}。 *{} 选择表达式(星号表达式)。选择表达式与变量表达式有一个重要的区别:选择表达式计算的是选定的对象,而不是整个环境变量映射 #{} 消息表达式(井号表达式,properties资源表达式)。通常与th:text属性一起使用,指明声明了th:text的标签的文本是#{}中的key所对应的value,而标签内的文本将不会显示 @{} 超链接url表达式 #maps 工具对象表达式。常用于日期、集合、数组对象的访问 #dates #calendars #numbers #strings #objects #bools #arrays #lists #sets ```