拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Spring Boot使用具有JSP模板机制

Spring Boot使用具有JSP模板机制

白鹭 - 2021-11-24 655 0 0

1.简介

在构建Web应用程序时, JavaServer Pages(JSP)是一种选项,我们可以将其用作HTML页面的模板机制。另一方面, Spring Boot是一个流行的框架,我们可以用来引导我们的Web应用程序

在本教程中,我们将了解如何将JSP与Spring Boot一起使用来构建Web应用程序。首先,我们将了解如何设置我们的应用程序以在不同的部署方案中工作。然后,我们将继续了解JSP的一些常用用法。最后,在打包应用程序时,我们将探讨各种选项。

这里要注意的一点是, JSP本身就有局限性,与Spring Boot结合使用时有更多局限性。因此,我们应该将Thymeleaf或FreeMarker视为JSP的更好替代方案。

2. Maven依赖

让我们看看使用JSP支持Spring Boot需要哪些依赖项。

我们还将注意到在将我们的应用程序作为独立应用程序运行与在Web容器中运行之间的微妙之处。

2.1 作为独立应用程序运行

首先,让我们包括spring-boot-starter-web依赖项。此依赖关系提供了使Web应用程序连同默认的嵌入式Tomcat Servlet容器一起运行的所有核心要求,这些Web应用程序可与Spring Boot一起运行:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-web</artifactId>

 <version>2.4.4</version>

 </dependency>

请查看我们关于在Spring Boot中比较嵌入式Servlet容器的文章,以获取有关如何配置除Tomcat之外的嵌入式Servlet容器的更多信息。

我们应该特别注意, Undertow在用作嵌入式Servlet容器时不支持JSP

接下来,我们需要包含tomcat-embed-jasper依赖关系,以允许我们的应用程序编译和呈现JSP页面:

<dependency>

 <groupId>org.apache.tomcat.embed</groupId>

 <artifactId>tomcat-embed-jasper</artifactId>

 <version>9.0.44</version>

 </dependency>

虽然可以手动提供以上两个依赖项,但是通常最好让Spring Boot管理这些依赖项版本,而我们仅管理Spring Boot版本。

可以通过使用Spring Boot父POM(如我们的文章Spring Boot教程–引导一个简单的应用程序)中所示的方式来进行版本管理,也可以通过使用我们的文章Spring Boot Dependency Management with Custom Parent中的依赖项管理来完成此版本管理。

最后,我们需要包括jstl库,该库将在我们的JSP页面中提供所需的JSTL标签支持:

<dependency>

 <groupId>javax.servlet</groupId>

 <artifactId>jstl</artifactId>

 <version>1.2</version>

 </dependency>

2.2 在Web容器(Tomcat)中运行

在Tomcat Web容器中运行时,我们仍然需要上述依赖项。

但是,为了避免我们的应用程序提供的依赖关系与Tomcat运行时提供的依赖关系发生冲突,我们需要使用provided作用域设置两个依赖关系:

<dependency>

 <groupId>org.apache.tomcat.embed</groupId>

 <artifactId>tomcat-embed-jasper</artifactId>

 <version>9.0.44</version>

 <scope>provided</scope>

 </dependency>



 <dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-tomcat</artifactId>

 <version>2.4.4</version>

 <scope>provided</scope>

 </dependency>

请注意,我们必须显式定义spring-boot-starter-tomcat并将其标记为provided范围。这是因为它已经是spring-boot-starter-web提供的可传递依赖项。

3.查看解析器配置

按照约定,我们将JSP文件放置在${project.basedir}/main/webapp/WEB-INF/jsp/目录中。 application.properties文件中配置两个属性来让Spring知道在哪里可以找到这些JSP文件:

spring.mvc.view.prefix: /WEB-INF/jsp/

 spring.mvc.view.suffix: .jsp

编译后,Maven将确保生成的WAR文件将上述jsp目录放在WEB-INF目录中,然后由我们的应用程序提供服务。

4.运行我们的应用程序

我们主要的应用程序类别将受到影响,无论是计划作为独立应用程序运行还是在Web容器中运行。

当作为一个独立的应用程序运行时,我们的应用程序类将是一个简单的@SpringBootApplication注释的类以及main方法

@SpringBootApplication(scanBasePackages = "com.baeldung.boot.jsp")

 public class SpringBootJspApplication {



 public static void main(String[] args) {

 SpringApplication.run(SpringBootJspApplication.class);

 }

 }

但是,如果需要在Web容器中进行部署,则需要扩展SpringBootServletInitializer.这会将我们的应用程序的ServletFilter,ServletContextInitializer绑定到运行时服务器,这对于我们的应用程序运行是必需的:

@SpringBootApplication(scanBasePackages = "com.baeldung.boot.jsp")

 public class SpringBootJspApplication extends SpringBootServletInitializer {



 @Override

 protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {

 return builder.sources(SpringBootJspApplication.class);

 }



 public static void main(String[] args) {

 SpringApplication.run(SpringBootJspApplication.class);

 }

 }

5.提供一个简单的网页

JSP页面依靠JavaServer Pages标准标记库(JSTL)提供常见的模板功能,例如分支,迭代和格式化,甚至还提供了一组预定义的功能。

让我们创建一个简单的网页,其中显示了保存在应用程序中的书籍列表。

假设我们有一个BookService ,可以帮助我们查找所有Book对象:

public class Book {

 private String isbn;

 private String name;

 private String author;



 //getters, setters, constructors and toString

 }



 public interface BookService {

 Collection<Book> getBooks();

 Book addBook(Book book);

 }

我们可以编写一个Spring MVC Controller来将其公开为网页:

@Controller

 @RequestMapping("/book")

 public class BookController {



 private final BookService bookService;



 public BookController(BookService bookService) {

 this.bookService = bookService;

 }



 @GetMapping("/viewBooks")

 public String viewBooks(Model model) {

 model.addAttribute("books", bookService.getBooks());

 return "view-books";

 }

 }

请注意,上面的BookController将返回一个名为view-books.根据我们之前在application.properties,配置,Spring MVC将在/WEB-INF/jsp/目录中view-books.jsp

我们需要在该位置创建此文件:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

 <html>

 <head>

 <title>View Books</title>

 <link href="<c:url value="/css/common.css"/>" rel="stylesheet" type="text/css">

 </head>

 <body>

 <table>

 <thead>

 <tr>

 <th>ISBN</th>

 <th>Name</th>

 <th>Author</th>

 </tr>

 </thead>

 <tbody>

 <c:forEach items="${books}" var="book">

 <tr>

 <td>${book.isbn}</td>

 <td>${book.name}</td>

 <td>${book.author}</td>

 </tr>

 </c:forEach>

 </tbody>

 </table>

 </body>

 </html>

上面的示例向我们展示了如何使用JSTL <c:url>标记链接到外部资源,例如JavaScript和CSS。我们通常将它们放在${project.basedir}/main/resources/static/目录下。

我们还可以看到JSTL <c:forEach>标记如何可用于遍历BookController books model属性。

6.处理表格提交

现在让我们看看如何使用JSP处理表单提交。我们的BookController将需要提供MVC端点来服务于表单以添加书籍并处理表单提交:

public class BookController {



 //already existing code



 @GetMapping("/addBook")

 public String addBookView(Model model) {

 model.addAttribute("book", new Book());

 return "add-book";

 }



 @PostMapping("/addBook")

 public RedirectView addBook(@ModelAttribute("book") Book book, RedirectAttributes redirectAttributes) {

 final RedirectView redirectView = new RedirectView("/book/addBook", true);

 Book savedBook = bookService.addBook(book);

 redirectAttributes.addFlashAttribute("savedBook", savedBook);

 redirectAttributes.addFlashAttribute("addBookSuccess", true);

 return redirectView;

 }

 }

我们将创建以下add-book.jsp文件(请记住将其放置在正确的目录中):

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 <%@ page contentType="text/html;charset=UTF-8" language="java" %>

 <html>

 <head>

 <title>Add Book</title>

 </head>

 <body>

 <c:if test="${addBookSuccess}">

 <div>Successfully added Book with ISBN: ${savedBook.isbn}</div>

 </c:if>



 <c:url var="add_book_url" value="/book/addBook"/>

 <form:form action="${add_book_url}" method="post" modelAttribute="book">

 <form:label path="isbn">ISBN: </form:label> <form:input type="text" path="isbn"/>

 <form:label path="name">Book Name: </form:label> <form:input type="text" path="name"/>

 <form:label path="author">Author Name: </form:label> <form:input path="author"/>

 <input type="submit" value="submit"/>

 </form:form>

 </body>

 </html>

我们使用modelAttribute通过所提供的参数<form:form>标签的结合book中所添加的属性addBookView()在方法BookController到的形式,其反过来,将提交表单时被填充。

使用此标记的结果是,我们需要单独定义表单操作URL(因为我们不能将标记放在标记内)。我们还使用在<form:input> path属性,将每个输入字段绑定到Book对像中的一个属性。

有关如何处理表单提交的更多详细信息,请参阅我们的文章“ Spring MVC中的表单入门”。

7.处理错误

由于将Spring Boot与JSP结合使用的现有限制,我们无法提供一个custom error.html来定制默认的/error映射。相反,我们需要创建自定义错误页面来处理不同的错误。

7.1 静态错误页面

如果我们要显示针对不同HTTP错误的自定义错误页面,则可以提供一个静态错误页面。

假设我们需要为应用程序引发的所有4xx错误提供一个错误页面。我们可以简单地在${project.basedir}/main/resources/static/error/目录4xx.html

如果我们的应用程序抛出4xx HTTP错误,那么Spring将解决该错误并返回提供的4xx.html页面。

7.2 动态错误页面

我们可以通过多种方式处理异常,以提供自定义的错误页面以及上下文相关的信息。让我们看看Spring MVC如何使用@ControllerAdvice@ExceptionHandler批注为我们提供这种支持。

假设我们的应用程序定义了DuplicateBookException

public class DuplicateBookException extends RuntimeException {

 private final Book book;



 public DuplicateBookException(Book book) {

 this.book = book;

 }



 // getter methods

 }

另外,假设我们尝试添加具有相同ISBN的两本书,则BookServiceImpl类将抛出上面的DuplicateBookException

@Service

 public class BookServiceImpl implements BookService {



 private final BookRepository bookRepository;



 // constructors, other override methods



 @Override

 public Book addBook(Book book) {

 final Optional<BookData> existingBook = bookRepository.findById(book.getIsbn());

 if (existingBook.isPresent()) {

 throw new DuplicateBookException(book);

 }



 final BookData savedBook = bookRepository.add(convertBook(book));

 return convertBookData(savedBook);

 }



 // conversion logic

 }

然后,我们的LibraryControllerAdvice类将定义我们要处理的错误以及如何处理每个错误:

@ControllerAdvice

 public class LibraryControllerAdvice {



 @ExceptionHandler(value = DuplicateBookException.class)

 public ModelAndView duplicateBookException(DuplicateBookException e) {

 final ModelAndView modelAndView = new ModelAndView();

 modelAndView.addObject("ref", e.getBook().getIsbn());

 modelAndView.addObject("object", e.getBook());

 modelAndView.addObject("message", "Cannot add an already existing book");

 modelAndView.setViewName("error-book");

 return modelAndView;

 }

 }

我们需要定义error-book.jsp文件,以便在此处解决上述错误。确保将其放置在${project.basedir}/main/webapp/WEB-INF/jsp/目录下,因为它不再是静态HTML,而是需要编译的JSP模板。

8.创建一个可执行文件

如果我们打算将应用程序部署在诸如Tomcat之类的Web容器中,那么选择war打包来实现这一目标。

但是,请注意,如果将JSP和Spring Boot与Embedded Servlet Container一起jar因此,如果作为独立应用程序运行,我们唯一的选择是war

无论哪种情况,我们的pom.xml都需要将其包装指令设置为war

<packaging>war</packaging>

如果我们不使用Spring Boot父POM来管理依赖项,则需要包含spring-boot-maven-plugin以确保生成的war文件能够作为独立应用程序运行

现在,我们可以使用嵌入式Servlet容器运行我们的独立应用程序,或者将生成的war文件放入Tomcat中并让它为我们的应用程序服务。

9.结论

我们在本教程中涉及了各种主题。让我们回顾一些重要的注意事项:

  • JSP包含一些固有的局限性。考虑改用Thymeleaf或FreeMarker
  • 如果在Web容器上部署, provided必要依赖项标记为已提供
  • 如果用作嵌入式Servlet容器,则Undertow将不支持JSP
  • 如果部署在Web容器中,则@SpringBootApplication注释的类应扩展SpringBootServletInitializer并提供必要的配置选项
  • 我们无法使用JSP覆盖默认的/error相反,我们需要提供自定义错误页面
  • 如果我们将JSP与Spring Boot结合使用,则对我们而言,JAR打包不是一个选择
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *