使用 Spring 框架构建 MVC 应用程序:初学者教程

已发表: 2022-03-11

人们常说 Java 太复杂,而且构建简单的应用程序花费的时间太长。 尽管如此,Java 提供了一个稳定的平台,周围有一个非常成熟的生态系统,这使其成为开发健壮软件的绝佳选择。

Spring 框架是 Java 生态系统中众多强大的框架之一,它带有一组编程和配置模型,旨在简化 Java 中高性能和可测试应用程序的开发。

春天框架

在本教程中,我们将挑战使用 Spring Framework 和 Java Persistence API (JPA) 构建一个简单的应用程序,该应用程序将充当软件开发人员的数据库。

该应用程序遵循标准的 MVC 架构。 它将有一个控制器(ContractsController 类)、视图(基于 Thymeleaf 模板)和一个模型(一个 Java 地图对象)。 为简单起见,我们将使用 JPA 背后的内存数据库在应用程序运行时持久保存数据。

Spring 框架教程入门

要构建基于 Spring 的应用程序,我们将需要使用以下构建工具之一:

  • 马文
  • 摇篮

在本教程中,我们将使用 Maven。 如果您不熟悉这些工具中的任何一个,一个简单的入门方法是下载 Spring Tool Suite。 该套件专用于 Spring Framework,并带有自己的基于 Eclipse 的 IDE。

在 Spring Tool Suite 中,我们通过从“File > New”菜单下选择“Spring Starter Project”来创建一个新项目。

Spring 框架教程

创建新项目后,我们需要编辑 Maven 配置文件“ pom.xml ”,并添加以下依赖项:

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> </dependency>

这些列出的依赖项将加载 Spring Boot Web、Thymeleaf、JPA 和 H2(将作为我们的内存数据库)。 所有必要的库将被自动拉取。

实体类

为了能够存储有关开发人员及其技能的信息,我们需要定义两个实体类:“ Developer ”和“ Skill ”。

这两者都被定义为带有一些注释的普通 Java 类。 通过在类前添加“@Entity”,我们将它们的实例提供给 JPA。 这将使在需要时从持久数据存储中存储和检索实例变得更加容易。 此外,“@Id”和“@GeneratedValue”注释允许我们指示实体的唯一 ID 字段,并在存储在数据库中时自动生成其值。

由于开发人员可以拥有许多技能,我们可以使用“@ManyToMany”注释定义一个简单的多对多关系。

开发商

@Entity public class Developer { @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id; private String firstName; private String lastName; private String email; @ManyToMany private List<Skill> skills; public Developer() { super(); } public Developer(String firstName, String lastName, String email, List<Skill> skills) { super(); this.firstName = firstName; this.lastName = lastName; this.email = email; this.skills = skills; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List<Skill> getSkills() { return skills; } public void setSkills(List<Skill> skills) { this.skills = skills; } public boolean hasSkill(Skill skill) { for (Skill containedSkill: getSkills()) { if (containedSkill.getId() == skill.getId()) { return true; } } return false; } }

技能

@Entity public class Skill { @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id; private String label; private String description; public Skill() { super(); } public Skill(String label, String description) { super(); this.label = label; this.description = description; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }

存储库

使用 JPA,我们可以定义一个非常有用的 DeveloperRepository 接口和 SkillRepository 接口,它们允许简单的 CRUD 操作。 这些接口将允许我们通过简单的方法调用来访问存储的开发人员和技能,例如:

  • “respository.findAll()”:返回所有开发者
  • “repository.findOne(id)”:返回给定 ID 的开发者

要创建这些接口,我们需要做的就是扩展 CrudRepository 接口。

开发者资料库

public interface DeveloperRepository extends CrudRepository<Developer, Long> { }

技能库

public interface SkillRepository extends CrudRepository<Skill, Long> { public List<Skill> findByLabel(String label); }

此处声明的附加方法“ findByLabel ”的功能将由 JPA 自动提供。

控制器

接下来,我们可以在这个应用程序的控制器上工作。 控制器会将请求 URI 映射到查看模板并在两者之间执行所有必要的处理。

 @Controller public class DevelopersController { @Autowired DeveloperRepository repository; @Autowired SkillRepository skillRepository; @RequestMapping("/developer/{id}") public String developer(@PathVariable Long id, Model model) { model.addAttribute("developer", repository.findOne(id)); model.addAttribute("skills", skillRepository.findAll()); return "developer"; } @RequestMapping(value="/developers",method=RequestMethod.GET) public String developersList(Model model) { model.addAttribute("developers", repository.findAll()); return "developers"; } @RequestMapping(value="/developers",method=RequestMethod.POST) public String developersAdd(@RequestParam String email, @RequestParam String firstName, @RequestParam String lastName, Model model) { Developer newDeveloper = new Developer(); newDeveloper.setEmail(email); newDeveloper.setFirstName(firstName); newDeveloper.setLastName(lastName); repository.save(newDeveloper); model.addAttribute("developer", newDeveloper); model.addAttribute("skills", skillRepository.findAll()); return "redirect:/developer/" + newDeveloper.getId(); } @RequestMapping(value="/developer/{id}/skills", method=RequestMethod.POST) public String developersAddSkill(@PathVariable Long id, @RequestParam Long skillId, Model model) { Skill skill = skillRepository.findOne(skillId); Developer developer = repository.findOne(id); if (developer != null) { if (!developer.hasSkill(skill)) { developer.getSkills().add(skill); } repository.save(developer); model.addAttribute("developer", repository.findOne(id)); model.addAttribute("skills", skillRepository.findAll()); return "redirect:/developer/" + developer.getId(); } model.addAttribute("developers", repository.findAll()); return "redirect:/developers"; } }

URI 到方法的映射是通过简单的“@RequestMapping”注释完成的。 在这种情况下,控制器的每个方法都映射到一个 URI。

这些方法的模型参数允许将数据传递给视图。 本质上,这些是键到值的简单映射。

每个控制器方法要么返回要用作视图的 Thymeleaf 模板的名称,要么返回特定模式的 URL(“redirect: ”)重定向到。 例如,方法“developer”和“_developersList_”返回模板的名称,而“developersAdd”和“developersAddSkill”返回要重定向到的 URL。

在控制器中,“@Autowired”注释会自动在相应字段中分配我们定义的存储库的有效实例。 这允许从控制器内部访问相关数据,而无需处理大量样板代码。

意见

最后,我们需要为要生成的视图定义一些模板。 为此,我们使用 Thymeleaf,一个简单的模板引擎。 我们在控制器方法中使用的模型可以直接在模板中使用,即当我们在模型中的“ contract ”键中输入合约时,我们将能够从模板中以“contract.name”的形式访问名称字段。

Thymeleaf 包含一些控制 HTML 生成的特殊元素和属性。 它们非常直观和直接。 例如,要使用技能名称填充 span 元素的内容,您需要做的就是定义以下属性(假设模型中定义了键“技能”):

 <span th:text="${skill.label}"></span>

与设置锚元素的“ href ”属性类似,可以使用特殊属性“ th:href ”。

在我们的应用程序中,我们将需要两个简单的模板。 为清楚起见,我们将跳过嵌入模板代码中的所有样式和类属性(即 Bootstrap 属性)。

开发者列表

 <!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Developers database</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Developers</h1> <table> <tr> <th>Name</th> <th>Skills</th> <th></th> </tr> <tr th:each="developer : ${developers}"> <td th:text="${developer.firstName + ' ' + developer.lastName}"></td> <td> <span th:each="skill,iterStat : ${developer.skills}"> <span th:text="${skill.label}"/><th:block th:if="${!iterStat.last}">,</th:block> </span> </td> <td> <a th:href="@{/developer/{id}(id=${developer.id})}">view</a> </td> </tr> </table> <hr/> <form th:action="@{/developers}" method="post" enctype="multipart/form-data"> <div> First name: <input name="firstName" /> </div> <div> Last name: <input name="lastName" /> </div> <div> Email: <input name="email" /> </div> <div> <input type="submit" value="Create developer" name="button"/> </div> </form> </body> </html>

开发者详情

 <!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Developer</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Developer</h1> Name: <b th:text="${developer.firstName}" /> <b th:text="${developer.lastName}" /><br/> Email: <span th:text="${developer.email}" /><br/> Skills: <span th:each="skill : ${developer.skills}"> <br/>&nbsp;&nbsp;<span th:text="${skill.label}" /> - <span th:text="${skill.description}" /> </span> <form th:action="@{/developer/{id}/skills(id=${developer.id})}" method="post" enctype="multipart/form-data" > <select name="skillId"> <option th:each="skill : ${skills}" th:value="${skill.id}" th:text="${skill.description}">Skill</option> </select> <input type="submit" value="Add skill"/> </form> </body> </html>

运行服务器

Spring 包含一个引导模块。 这允许我们从命令行轻松地启动服务器作为命令行 Java 应用程序:

 @SpringBootApplication public class Application implements CommandLineRunner { @Autowired DeveloperRepository developerRepository; @Autowired SkillRepository skillRepository; public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

由于我们使用的是内存数据库,因此在启动时使用一些预定义的数据引导数据库是有意义的。 这样,当服务器启动并运行时,我们将在数据库中至少有一些数据。

 @Override public void run(String... args) throws Exception { Skill javascript = new Skill("javascript", "Javascript language skill"); Skill ruby = new Skill("ruby", "Ruby language skill"); Skill emberjs = new Skill("emberjs", "Emberjs framework"); Skill angularjs = new Skill("angularjs", "Angularjs framework"); skillRepository.save(javascript); skillRepository.save(ruby); skillRepository.save(emberjs); skillRepository.save(angularjs); List<Developer> developers = new LinkedList<Developer>(); developers.add(new Developer("John", "Smith", "[email protected]", Arrays.asList(new Skill[] { javascript, ruby }))); developers.add(new Developer("Mark", "Johnson", "[email protected]", Arrays.asList(new Skill[] { emberjs, ruby }))); developers.add(new Developer("Michael", "Williams", "[email protected]", Arrays.asList(new Skill[] { angularjs, ruby }))); developers.add(new Developer("Fred", "Miller", "[email protected]", Arrays.asList(new Skill[] { emberjs, angularjs, javascript }))); developers.add(new Developer("Bob", "Brown", "[email protected]", Arrays.asList(new Skill[] { emberjs }))); developerRepository.save(developers); }

结论

Spring 是一个通用框架,允许构建 MVC 应用程序。 使用 Spring 构建一个简单的应用程序既快速又透明。 该应用程序还可以使用 JPA 轻松地与数据库集成。

整个项目的源代码可在 GitHub 上找到。