JavaRush /Java 博客 /Random-ZH /征服 Spring Boot
Surplus
第 37 级
Москва

征服 Spring Boot

已在 Random-ZH 群组中发布
美好的一天,亲爱的读者!很高兴认识你,即使这个浮夸的名字是我们研究关于第一次接触 Spring Boot 开发的朴素话题的主要原因。我想分享一下我在 JavaRush 门户网站上完成实习介绍性作业的经历,从一个完全普通的技术大学生的角度来展示自己的概述,他想要测试自己所积累的知识的强度。 征服春季靴 - 1我绝不否认所附代码或思维方法可能存在粗鲁之处,我欢迎建设性的批评,因为正是有了“坎坷和碰伤”,才有可能向专业方向发展。而且,我根本不会假装自己是解决给定条件的“万能药”,故意省略程序的各个片段,让进入一个相对复杂的话题的关键重要性不至于对神经系统产生丝毫影响。确实,否认显而易见的事实是鲁莽的:这对我来说很难,直到某个时刻之前绝对没有任何事情是清楚的。如果您在第一次执行任务时也有类似的感受,那么“欢迎!” 让我们在 Spring Boot 中编写一个 Web 应用程序,使用实习入学考试的简化类比,使用模板引擎Thymeleafquery查询本地 MySQL 服务器来过滤传入的信息数组。那么让我们开始吧!

春季启动。它是什么动物以及如何烹饪?

简而言之,它是Pivotel的一款优秀工具,可以在创建应用程序的过程中节省宝贵的时间,无需直接连接第三方库、编写令人印象深刻的映射画布和 servlet。使用Spring Initializr构建器就足够了,它集成到IntelliJ IDEA Ultimate Edition中(文件 - 新建 - 项目... - Spring Initializr)或位于start.spring.io Web 服务上,指定要包含的各种包优惠。
征服 Spring Boot - 2
按照提出的技术规范,我们将使用绅士设定的标准,使用MySQL数据库创建一个简单的 Web 应用程序:
  • WEB是开发Web应用程序的主要组件,包括位于标准地址localhost:8080的本地Apache Tomcat服务器和通用的Spring MVC框架。

  • DevTools - 当在编译的代码或模板中检测到更改时,用于在热 JVM 中快速重新启动应用程序;此外,如果项目中包含所选引擎,它可以使 Thymeleaf 免于清除缓存。

  • JPA是一种使用数据库所需的技术,它提供 Java 对象的对象关系映射,提供用于管理、保存和检索实体的 API(在我们的例子中为Hibernate )。

  • Thymeleaf(Mustache、AngularJS、Vaadin 等) - 用于应用程序可视化的模板引擎;由于我对 html 原理相对熟悉,我选择了 Thymeleaf,它将该语言推向了世界的基石。

  • MySQL - 连接 Java 数据库连接驱动程序以对数据库执行 SQL 查询。
在最终选择组件并创建之后,我们得到了一个普通的 Web 应用程序架构,其中的目录可供进一步填充。用于与视觉部分交互的片段,无论是 CSS 图形样式、标准 HTML 页面还是 JavaScript 功能,都应位于“resources”中,因此后端组件应放置在“java”中。我们还应该注意根范围内的pom.xml文件,它存储了项目结构和组件之间的依赖关系。如果你想用额外的包进一步扩展功能或者删除不必要的东西,你应该<dependencies></dependencies>按照类似的方法在标签之间进行操作。
征服 Spring Boot - 3

迈向美好未来的第一步

接下来,出现了一个相当有趣且非常合乎逻辑的问题:“现在该怎么办?这将如何运作?该程序建立在模型-视图-控制器的原则之上:它组织从连接的数据库(模型)中读取实体,并通过控件(视图)显示在用户界面中;组件之间的通信以及根据传输的请求执行操作是通过控制器进行的。关键要素的创建可作为持续发展的参考点。为了避免滑坡,维护工作领域战友的尊重,应将组件放置在适当的目录中(例如将Controller文件放置在“java”分支的controllers文件夹中),并小心保存工作场所秩序。

本质是一个大机制中的一个小部分

或者换句话说,我们的模型根据问题中设定的条件。离开讨论的主题,回到介绍性项目,我们可以自信地断言,任务之间的差异很小,并在进一步审查中遵循平均概念。比方说,笔记本中的笔记包括:
  • 用于确定在一般流程中的位置的识别号;
  • 一定字符数的短信;
  • 用户将其添加到常规列表的日期;
  • 用于确定“完成或未完成”(“读取或未读取”)的布尔变量。
因此,让我们在名为“entity”的目录中创建一个 Note 类并添加适当的字段:
@Entity
public class Note {

   @Id
   @GeneratedValue
   private int id;
   private String message;
   private Date date;
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}
另一个偏离讨论主题的问题是为了从理论立场更好地理解正在发生的事情。Spring中组件之间的连接是通过注解来指定的——对象前面的特殊指针,每个注解在机制中扮演着特定的角色,并以“@”符号开头。@Entity 注解向 Spring Boot 指示后续类数据属于“实体”,@Id 和 @GenerateValue 将选定的字段指定为标识符,并在处理信息数组时自动生成迭代器。我故意省略添加标准的 Getter 和 Setter 以增加视觉格式的紧凑性。接下来,考虑到使用数据库来存储记录,我们进入应用程序开发的下一步:我们将在“repository”目录中创建 NoteRepository 接口,作为交换链中的连接元素,并继承最适合进一步工作的存储库,指示要访问的存储实体和整数迭代器。
public interface NoteRepository extends JpaRepository<Note, Integer> {
}
事实上,仅此而已。简洁明了。现在 Spring Boot 将使用创建的组件来组织与数据库的交互。遗留存储库的类型相对较多,具有不同的行动潜力。JpaRepository处于阶梯的最顶端,最有潜力,包括下面的CrudRepository和PageAndSortingRepository。我们不会再进一步​​偏离主题,因为一些微妙之处可以在 Pivotel 网站的技术文档中找到。现在,在应用程序端实现数据镜像并指定通信方式后,您需要注意在适当的外部环境“MySQL Workbench”中创建MySQL数据库,该数据库在官方开发人员的程序集中预安装在桌面平台上带有用于创建本地服务器的附加包:
征服 Spring Boot - 4
接下来,单击主窗口中当前本地服务器的图标后,按照环境的指示,根据实体的字段(注)创建一个表格图,并填充适当的数据。有必要单独厘清MySQL方言的微妙之处,迫切需要引起重视才能顺利达到预期的结果:
  • 没有单独的布尔类型本身。任何请求处理动作都会将“true”或“false”分别转换为位值“1”或“0”;
  • 日期完全存储在 Timestamp 类型中。如果您使用核心熟悉的日期,您将不得不将自己限制在日历中的位置。
征服 Spring Boot - 5
最终完成准备步骤后,我们点击工具栏上的“闪电”图标,指示“MySQL Workbench”将数据发送到本地服务器。现在,如果添加信息正确完成,我们可以通过将当前数据库配置添加到 application.properties (通常位于“resources”目录中),自信地返回到本机 IDE 继续开发:
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
最后使用注释将 Note 实体绑定到 MySQL。@Table 表示使用具有选定名称和架构的表,@Column 表示变量属于特定字段。
@Entity
@Table(name = "test", schema = "test", catalog = "")
public class Note {

   @Id
   @GeneratedValue
   private int id;
   @Column(name = "message")
   private String message;
   @Column(name = "date")
   private Date date;
   @Column(name = "done")
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}

视图或用户界面

唉,我们可以有把握地说:“如果没有丝毫理论或实践知识,应用程序的可视化将成为主要的绊脚石。” 坦白说,前端部分占据了总工作量的惊人部分,并且在很长一段时间内自信地磨损了我的神经。但由于 Thymeleaf 令人惊叹的简单性,在经历了一系列令人着迷的失败后,我们有可能找到合适的折衷方案。进一步的讨论将涉及使用所选引擎的复杂性,尽管总体概念遵循类似的立场。主要技术是能够使用最纯粹的 HTML 并从各个片段组装最终显示,以避免相同部分的多次重复。假设 UI 架构由一个主页组成,该主页包含一个带有控件的导航栏(添加新条目,返回主页)和一个动态表,用于显示按添加注释的时间按升序 (ASC) 排序的实体或递减 (DESC) 方向。含义。让我们以所有记录按升序显示为标准位置。根据所选模板引擎的分层策略,组件可视化元素应位于“resources”目录中的“templates”分支上。因此,对组件的进一步操作会考虑所提出的条件。让我们在 html5 模板上创建一个名为“index”(或根据个人喜好的任何其他名称)的主页。例如:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/head :: head"></head>
<body>
<div class="container">
   <div th:replace="fragments/header :: header"></div>
   <div th:if="${not #lists.isEmpty(notes)}">
       <div th:replace="operations/list :: notebook"></div>
   </div>
   <div th:replace="fragments/footer :: footer"></div>
</div>
</body>
</html>
因此,让我们分解最终应用程序的关键组件。Thymeleaf 使用单独的语法来指示过程的使用,并以关键字“th:”开头,库的链接必须包含在开始的 <html> 标记中。
<div th:if="${not #lists.isEmpty(notes)}">
“if”操作与通常的处理方式完全没有什么不同,它会检查传入的“notes”属性是否存在对象以供进一步显示。值得一提的是,考虑到控制器用于组织模型和可视化交互的用途,该主题与控制器的使用有重叠。许多模糊的时刻在未来成形,如果你愿意就回去吧。
<head th:replace="operations/list :: notebook"></head>
“替换”操作表示用当前或单独页面中选定的片段替换“存根”或活动块 - 后一种情况在示例中清楚地观察到。我们将“operations”目录的“list.html”中名为“notebook”的片段复制到“index”文件的 <div></div> 中,完全替换最终目标中的内容。传出的内容如下:
<!DOCTYPE html>
<!--suppress ALL -->
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">

<div th:fragment="notebook">
   <table class="table table-bordered table-hover horizontal-align">
       <thead>
       <tr>
           <th style="width: 5%">#</th>
           <th style="width: 60%">Message</th>
           <th class="dropdown" style="width: 20%">Date
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}"><i class="fa fa-chevron-circle-up"></i></a>
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'DESC')}"><i class="fa fa-chevron-circle-down"></i></a>
           </th>
           <th style="width: 5%">Done</th>
           <th style="width: 5%">Edit</th>
           <th style="width: 5%">Delete</th>
       </tr>
       </thead>
       <tbody>
       <tr th:each="note : ${notes}">
           <td th:text="${note.id}" style="text-align: center">#</td>
           <td th:text="${note.message}">Message</td>
           <td th:text="${#dates.format(note.date, 'EEE, d MMM yyyy HH:mm')}" style="text-align: center">Date</td>
           <td style="text-align: center">
               <i th:if="${note.done} == true" class="fa fa-plus-square-o" style="font-size:20px;color:#337ab7"></i>
               <i th:if="${note.done} == false" class="fa fa-minus-square-o" style="font-size:20px;color:#337ab7"></i>
           </td>
           <td style="text-align: center"><a th:href="@{'/edit/{id}'(id=${note.id})}"><i class="fa fa-edit" style="font-size:20px"></i></a></td>
           <td style="text-align: center"><a th:href="@{'/delete/{id}'(id=${note.id})}"><i class="fa fa-trash" style="font-size:20px"></i></a></td>
       </tr>
       </tbody>
   </table>
</div>
</html>
让我们回到建设性概述并按顺序浏览使用的 Thymeleaf 函数,省略使用的标准 HTML 语法或图形样式,并专门关注理解模板引擎机制。
<div th:fragment="notebook">
“片段”操作指定片段的名称,并使得可以将块的内容用于“替换”命令。而且!绝不排除单个页面内的多次使用,再次与编程语言中的过程或函数进行类比。
<a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}">
在控制器中使用映射“/sort/{sortDate}”调用 @PostMapping 注释,其中 {sortDate} 是传出排序方向属性。在下面的块中可以看到类似的东西,它根据迭代循环中用户选择的元素的位置添加动态变化:
<a th:href="@{'/edit/{id}'(id=${note.id})}">
<tr th:each="note : ${notes}">
枚举值与 Java 语法中熟悉的 for 块的使用非常相似:变量“note”从输入属性数组 ${notes}(实体数组)中获取当前元素,并用于更改值​​稍后。坦率地说,我们可以专门写一篇文章来列出 Thymeleaf 的广泛功能和实际应用示例 - 模板引擎非常简单,根本不需要学习额外语法的令人印象深刻的包袱。上述功能在开发者官网的技术文档中有概述,对于组织与后端的沟通起着关键作用。因此,您可以自信地继续下一个也是最后一个部分。当然,可以将可视化的其余组件附加到文章末尾的已完成应用程序的链接中。

小公司的财务总监、管理员

“Web 应用程序架构中的基石”——也许没有办法找到更准确的描述来描述 Controller 组件在组织程序工作中的重要性:大多数操作都是由控制器之间的连接元素精确执行的。模型和视图。得益于 Spring Boot 的操作机制,您可以毫无问题地放心使用映射和 GET/POST 请求方法,并自动连接数据存储库。让我们在“controllers”目录下的单独文件中创建 NoteController 类,再次参考相应注释的使用:
@Controller
public class NoteController {

   private NoteService service;

   @Autowired
   public void setNoteService(NoteService service) {
       this.service = service;
   }

   @GetMapping("/")
   public String list(Model model) {
       return "index";
   }
}
细心的人可能会注意到应用程序体系结构设计中的一个重要变化,该变化与添加服务以将业务逻辑与数据库管理服务的工作隔离开来相关。完成的操作需要增加成品的多功能性,并为更改用户界面的功能提供广泛的范围,而无需更改与数据库的通信方法。标准表示绝不会从众多类似的表示中脱颖而出:该接口位于单独的目录中,并由带有 @Service 注解的类实现,用于 Spring Boot 检测:
public interface NoteService {
   Note getNoteById(Integer id);
   void saveNote(Note note);
   void updateNote(Integer id, String message, boolean done);
   void deleteNote(Integer id);
   List<Note> findAll();
}

@Service
public class NoteServiceImpl implements NoteService{

   private NoteRepository repository;

   @Autowired
   public void setProductRepository(NoteRepository repository) {
       this.repository = repository;
   }

   @Override
   public Note getNoteById(Integer id) {
       return repository.findOne(id);
   }

   @Override
   public void saveNote(Note note) {
       repository.save(note);
   }

   @Override
   public void updateNote(Integer id, String message, boolean done) {
       Note updated = repository.findOne(id);
       updated.setDone(done);
       updated.setMessage(message);
       repository.save(updated);
   }

   @Override
   public void deleteNote(Integer id) {
       repository.delete(id);
   }

   @Override
   public List<Note> findAll() {
       return repository.findAll();
   }
}
让我们回到回顾控制器并看看使用 Spring Boot 方法组织工作的复杂性。@Autowired 注解表示需要自动将服务绑定到适当类型的指定变量并与数据库建立连接。应该更多地关注视图通信的方式,由 @GetMapping("/") 注释指示,当收到对 localhost:8080 的调用时,它返回一个名为“index”的页面。您可以使用不同的方法,指定扩展描述 @RequestMapping(value = "/", method = RequestMethod.GET) 或用现成的 ModelAndView 替换返回类型。然而,根据目前实际应用中的经验状态,我没有注意到最终结果有任何根本差异,并使用通常的选项。让我们通过使用附加选项卡添加新元素来扩展控制器。用户点击导航栏元素后,调用@GetMapping("/new")并从“operations”目录重定向到“new”页面,使用按钮确认输入数据时返回一个名为“message”的参数并重定向到主块。需要特别提及的是,输入窗口中的变量名称与传输值的名称需要完全匹配。
<input type="text" class="form-control" id="message" th:name="message" placeholder="Enter your note." maxlength="100"/>
@GetMapping("/new")
public String newNote() {
   return "operations/new";
}

@PostMapping("/save")
public String updateNote(@RequestParam String message) {
   service.saveNote(new Note(message));
   return "redirect:/";
}
类似的技术用于更新记录。单击控件后,将调用 @GetMapping("/edit/{id}") 映射并传输 URL 字符串中的标识符,并添加“note”属性和条目以供进一步编辑。@RequestParam(value = "done", required = false) boolean done) 指定特定值在使用 Thymeleaf 模板引擎时在复选框的使用中起着关键作用,默认设置为“false”。
@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
   Note note = service.getNoteById(id);
   model.addAttribute("note", note);
   return "operations/edit";
}

@PostMapping("/update")
public String saveNote(@RequestParam Integer id, @RequestParam String message,
                      @RequestParam(value = "done", required = false) boolean done) {
   service.updateNote(id, message, done);
   return "redirect:/";
}
从数据库中删除项目非常简单,不需要通过使用传递的值调用适当的服务函数进行任何重大操作:
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
   service.deleteNote(id);
   return "redirect:/";
}
现在,让我们对完成的片段进行一些小调整,并继续使用 Spring Data JPA 中的查询查询与 MySQL 进行令人兴奋的通信,在关闭控制器之前单独添加一个用于管理简单过滤的功能。
@Controller
public class NoteController {

   private String sortDateMethod = "ASC";

   @GetMapping("/")
   public String list(Model model) {
       List<Note> notebook = filterAndSort();
       model.addAttribute("notes", notebook);
       model.addAttribute("sort", sortDateMethod);
       return "index";
   }

private List<Note> filterAndSort() {
   List<Note> notebook = null;
   switch (sortDateMethod) {
       case "ASC":
           notebook = service.findAllByOrderByDateAsc();
           break;
       case "DESC":
           notebook = service.findAllByOrderByDateDesc();
           break;
   }
   return notebook;
}

如此小,但如此重要的查询。

令人尴尬的是,与预期相反,过滤值竟然成为完成技术任务的另一个绊脚石,自信地克服了分页建立的复杂性阈值——将数据数组分成一定大小的单独页面以进一步显示。最有可能的是,累积的疲劳正在造成损害,但是......灵感来自于一次完全偶然的查询查询。
public interface NoteRepository extends JpaRepository<Note, Integer> {
   List<Note> findAllByOrderByDateAsc();
   List<Note> findAllByOrderByDateDesc();
}
Spring Data JPA 提供了创建高度精细的数据库查询的能力,消除了收到信息后对信息进行排序的需要,并且具有广泛的应用潜力。例如:
List<Note> findAllByOrderByDateAsc();
该方法将转换为 SQL 查询并显示按日期 (byDate) 升序 (Asc) 排序 (byOrder) 的所有记录 (findAll)。此外,您可以通过单一需求创建复杂的组合并跨多个领域进行采样。比如说,按日期值 (byDate) 递减 (Decs) 顺序 (byOrder) 选择所有 (findAll) 已完成 (byDoneTrue) 记录:
Page<Note> findAllByDoneTrueOrderByDateDesc(Pageable pageable);

结论或者是一个新手程序员的另一个自白

全部!您可以使用 Shift+F10 组合或单击相应的图标安全地启动 Web 应用程序。Spring Boot 将在 Apache Maven 上构建程序,并在 localhost:8080 安装本地 Apache Tomcat 服务器。现在您只需在任何浏览器中点击该链接即可。
征服春季靴 - 6
当然,还要开发一种方法来满足其他业务需求。应用程序的潜力受到开发人员的努力、足智多谋和想象力的限制。
征服 Spring Boot - 7
坦诚并关注走过的路,我一次又一次确信所选择方向的正确性,并体会到在JavaRush教育门户学习的好处。由于各种实际任务,我们有可能恢复对学习编程的诱人兴趣,这种兴趣在类似方向的高等教育机构的过时且令人惊讶的无聊课程中被完全压抑。与整年参加讲座和实验室课程相比,四个月积极学习后端技术堆栈中的材料所投入的知识要多得多。信不信由你。我希望你不要屈服于输入复杂材料的困难,因为只有通过克服障碍,我们才能变得更好,并在专业和个人方面得到发展。我希望这个小故事能帮助我发现一些使用 SpringBoot 这个神奇工具的新想法。PS GitHub .
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION