本文主要是介绍Spring Boot、Spring MVC、Spring Security、JSP、JPA 和 MySQL 作为数据库来开发 Todo待办Web应用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Spring Boot Web Application with JSP CRUD Example Tutorial
在本教程中,我们将学习如何使用
Spring Boot、
Spring MVC、
Spring Security、
JSP、
JPA 和
MySQL 作为数据库来开发 Spring Boot
Todo Management Web 应用程序 。
这个小项目主要是为初学者学习如何使用 Spring Boot 一步步开发 Web 应用。
在这个迷你待办事项管理项目中实现了以下功能:
- 创建待办事项
- 更新待办事项
- 删除待办事项
- 列出待办事项
- 通过 id 获取 Todo。
- 简单的 Spring 安全性
- 用于管理客户端依赖项(CSS 和 JS)的 Webjar。
- JSP 作为视图和通用页眉、页脚和导航栏。
- 自定义错误页面映射
我们将建造什么
我们将使用Spring Boot、
Spring MVC、
Spring Security、
JSP、
JPA 和
MySQL 作为数据库 逐步开发 Spring MVC Todo 管理 Web 应用程序 。
使用的工具和技术
- Spring Boot - 2.0.4.RELEASE
- JDK - 1.8 或更高版本
- Spring 框架 - 5.0.8 发布
- 休眠 - 5.2.17.Final
- Maven - 3.2+
- Spring Data JPA - 2.0.10 发布
- IDE - Eclipse 或 Spring Tool Suite (STS)
- MYSQL - 5.1.47
- Spring Security - 5.0.7 发布
- JSP
创建和导入项目
有很多方法可以创建 Spring Boot 应用程序。最简单的方法是在 http://start.spring.io/使用 Spring Initializr ,它是一个在线 Spring Boot 应用程序生成器。
看上图,我们指定了以下细节:
- 生成:Maven 项目
- Java 版本:1.8(默认)
- 春季启动:2.0.4
- 组:net.guides.todo
- 神器:todo-management-spring-boot
- 名称:待办事项管理弹簧启动
- 描述:todo-management-spring-boot
- 包名:net.guides.springboot.todomanagement
- 包装:jar(这是默认值)
- 依赖项:Web、JPA、MySQL、DevTools、安全性
输入所有详细信息后,单击 Generate Project 按钮将生成一个 Spring Boot 项目并下载它。接下来,解压缩下载的 zip 文件并将其导入您喜欢的 IDE。
包装结构
以下是包装结构供您参考-
pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.guides.springboot</groupId>
<artifactId>todo-management-spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>todo-management-spring-boot</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap-datepicker</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
请注意,我们使用 Spring Data JPA starter 与 MySQL 数据库通信。
创建 JPA 实体 - Todo.java
package net.guides.springboot.todomanagement.model;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;
/**
* @author Ramesh Fadatare
*
*/
@Entity
@Table(name = "todos")
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String userName;
@Size(min = 10, message = "Enter at least 10 Characters...")
private String description;
private Date targetDate;
public Todo() {
super();
}
public Todo(String user, String desc, Date targetDate, boolean isDone) {
super();
this.userName = user;
this.description = desc;
this.targetDate = targetDate;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getTargetDate() {
return targetDate;
}
public void setTargetDate(Date targetDate) {
this.targetDate = targetDate;
}
}
接下来,为 Todo 实体创建 Spring Data JPA 存储库。
Spring Data JPA 存储库接口 - TodoRepository.java
package net.guides.springboot.todomanagement.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import net.guides.springboot.todomanagement.model.Todo;
public interface TodoRepository extends JpaRepository < Todo, Long > {
List < Todo > findByUserName(String user);
}
Spring 安全配置 - SecurityConfiguration.java
package net.guides.springboot.todomanagement.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("admin").password("admin")
.roles("USER", "ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login", "/h2-console/**").permitAll()
.antMatchers("/", "/*todo*/**").access("hasRole('USER')").and()
.formLogin();
http.csrf().disable();
http.headers().frameOptions().disable();
}
}
这个类扩展
了 WebSecurityConfigurerAdapter 并覆盖了它的几个方法来设置 Web 安全配置的一些细节。
configure(HttpSecurity) 方法 定义了哪些 URL 路径应该被保护,哪些不应该。具体来说,“/”和“/login”路径配置为不需要任何身份验证。所有其他路径都必须经过身份验证。
至于
userDetailsService() 方法,它使用单个用户建立一个内存用户存储。该用户的用户名是“admin”,密码是“admin”,角色是“ADMIN”。
控制器层 - TodoController.java
此类包含用于创建、更新、删除和列出 Todos 的请求处理方法。
package net.guides.springboot.todomanagement.controller;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import net.guides.springboot.todomanagement.model.Todo;
import net.guides.springboot.todomanagement.service.ITodoService;
@Controller
public class TodoController {
@Autowired
private ITodoService todoService;
@InitBinder
public void initBinder(WebDataBinder binder) {
// Date - dd/MM/yyyy
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
@RequestMapping(value = "/list-todos", method = RequestMethod.GET)
public String showTodos(ModelMap model) {
String name = getLoggedInUserName(model);
model.put("todos", todoService.getTodosByUser(name));
// model.put("todos", service.retrieveTodos(name));
return "list-todos";
}
private String getLoggedInUserName(ModelMap model) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
}
return principal.toString();
}
@RequestMapping(value = "/add-todo", method = RequestMethod.GET)
public String showAddTodoPage(ModelMap model) {
model.addAttribute("todo", new Todo());
return "todo";
}
@RequestMapping(value = "/delete-todo", method = RequestMethod.GET)
public String deleteTodo(@RequestParam long id) {
todoService.deleteTodo(id);
// service.deleteTodo(id);
return "redirect:/list-todos";
}
@RequestMapping(value = "/update-todo", method = RequestMethod.GET)
public String showUpdateTodoPage(@RequestParam long id, ModelMap model) {
Todo todo = todoService.getTodoById(id).get();
model.put("todo", todo);
return "todo";
}
@RequestMapping(value = "/update-todo", method = RequestMethod.POST)
public String updateTodo(ModelMap model, @Valid Todo todo, BindingResult result) {
if (result.hasErrors()) {
return "todo";
}
todo.setUserName(getLoggedInUserName(model));
todoService.updateTodo(todo);
return "redirect:/list-todos";
}
@RequestMapping(value = "/add-todo", method = RequestMethod.POST)
public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) {
if (result.hasErrors()) {
return "todo";
}
todo.setUserName(getLoggedInUserName(model));
todoService.saveTodo(todo);
return "redirect:/list-todos";
}
}
控制器层 - WelcomeController.java
用户成功通过身份验证后,将导航到欢迎页面,此请求由该 WelcomeController 类处理。
package net.guides.springboot.todomanagement.controller;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class WelcomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String showWelcomePage(ModelMap model) {
model.put("name", getLoggedinUserName());
return "welcome";
}
private String getLoggedinUserName() {
Object principal = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
}
return principal.toString();
}
}
控制器层 - ErrorController.java
ErrorController 用于映射自定义错误页面。
package net.guides.springboot.todomanagement.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@Controller("error")
public class ErrorController {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(HttpServletRequest request, Exception ex) {
ModelAndView mv = new ModelAndView();
mv.addObject("exception", ex.getLocalizedMessage());
mv.addObject("url", request.getRequestURL());
mv.setViewName("error");
return mv;
}
}
控制器层 - LogoutController.java
此类处理,在成功注销后,然后导航到带有正确消息的主页。
package net.guides.springboot.todomanagement.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LogoutController {
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication != null) {
new SecurityContextLogoutHandler().logout(request, response,
authentication);
}
return "redirect:/";
}
}
服务层 - ITodoService.java
让我们探索在此界面中处理 Todo 功能所需的所有方法。
package net.guides.springboot.todomanagement.service;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import net.guides.springboot.todomanagement.model.Todo;
public interface ITodoService {
List < Todo > getTodosByUser(String user);
Optional < Todo > getTodoById(long id);
void updateTodo(Todo todo);
void addTodo(String name, String desc, Date targetDate, boolean isDone);
void deleteTodo(long id);
void saveTodo(Todo todo);
}
服务层 - TodoService.java
package net.guides.springboot.todomanagement.service;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.guides.springboot.todomanagement.model.Todo;
import net.guides.springboot.todomanagement.repository.TodoRepository;
@Service
public class TodoService implements ITodoService {
@Autowired
private TodoRepository todoRepository;
@Override
public List < Todo > getTodosByUser(String user) {
return todoRepository.findByUserName(user);
}
@Override
public Optional < Todo > getTodoById(long id) {
return todoRepository.findById(id);
}
@Override
public void updateTodo(Todo todo) {
todoRepository.save(todo);
}
@Override
public void addTodo(String name, String desc, Date targetDate, boolean isDone) {
todoRepository.save(new Todo(name, desc, targetDate, isDone));
}
@Override
public void deleteTodo(long id) {
Optional < Todo > todo = todoRepository.findById(id);
if (todo.isPresent()) {
todoRepository.delete(todo.get());
}
}
@Override
public void saveTodo(Todo todo) {
todoRepository.save(todo);
}
}
配置 MySQL 数据库和 JSP 视图解析器
配置 application.properties 以连接到您的 MySQL 数据库。让我们打开一个 application.properties 文件并向其中添加以下数据库配置。
## Spring view resolver set up
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/users_database?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
常见的 JSP 页面片段 - footer.jspf、header.jspf 和 navigation.jspf
footer.jspf
<script src="webjars/jquery/1.9.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="webjars/bootstrap-datepicker/1.0.1/js/bootstrap-datepicker.js"></script>
<script>
$('#targetDate').datepicker({
format: 'dd/mm/yyyy'
});
</script>
<div class="footer">
Fixed Footer
<h1>
<a href="http://www.javaguides.net/p/spring-boot-tutorial.html">
Spring Boot Tutorial</a>
</h1>
</div>
</body>
</html>
header.jspf
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>
<head>
<title>Todo Management</title>
<link href="webjars/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet">
<style>
.footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: black;
color: white;
height: 100px;
text-align: center;
}
</style>
</head>
<body>
navigation.jspf
<nav role="navigation" class="navbar navbar-default">
<div class="">
<a href="http://www.javaguides.net" class="navbar-brand">Java Guides</a>
</div>
<div class="navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="/">Home</a></li>
<li><a href="/list-todos">Todos</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="/logout">Logout</a></li>
</ul>
</div>
</nav>
现在,我们将把上面的页面片段包含到即将到来的 JSP 页面中。
welcome.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">Home Page</div>
<div class="panel-body">
Welcome ${name}!! <a href="/list-todos">Click here</a> to manage your
todo's.
</div>
</div>
</div>
<%@ include file="common/footer.jspf"%>
todo.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3 ">
<div class="panel panel-primary">
<div class="panel-heading">Add TODO</div>
<div class="panel-body">
<form:form method="post" modelAttribute="todo">
<form:hidden path="id" />
<fieldset class="form-group">
<form:label path="description">Description</form:label>
<form:input path="description" type="text" class="form-control"
required="required" />
<form:errors path="description" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:label path="targetDate">Target Date</form:label>
<form:input path="targetDate" type="text" class="form-control"
required="required" />
<form:errors path="targetDate" cssClass="text-warning" />
</fieldset>
<button type="submit" class="btn btn-success">Save</button>
</form:form>
</div>
</div>
</div>
</div>
</div>
<%@ include file="common/footer.jspf"%>
list-todos.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
<div>
<a type="button" class="btn btn-primary btn-md" href="/add-todo">Add Todo</a>
</div>
<br>
<div class="panel panel-primary">
<div class="panel-heading">
<h3>List of TODO's</h3>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
<th width="40%">Description</th>
<th width="40%">Target Date</th>
<th width="20%"></th>
</tr>
</thead>
<tbody>
<c:forEach items="${todos}" var="todo">
<tr>
<td>${todo.description}</td>
<td><fmt:formatDate value="${todo.targetDate}"
pattern="dd/MM/yyyy" /></td>
<td><a type="button" class="btn btn-success"
href="/update-todo?id=${todo.id}">Update</a>
<a type="button" class="btn btn-warning"
href="/delete-todo?id=${todo.id}">Delete</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
<%@ include file="common/footer.jspf"%>
error.jsp
<%@ include file="common/header.jspf"%>
<%@ include file="common/navigation.jspf"%>
<div class="container">
An exception occurred! Please contact Support!
</div>
<%@ include file="common/footer.jspf"%>
运行应用程序
我们已经成功开发了迷你待办事项管理网络应用程序。现在是时候在 servlet 容器(嵌入式 tomcat)中部署我们的应用程序了。我们可以通过两种方式启动独立的 Spring Boot 应用程序。
- 从应用程序的根目录并键入以下命令来运行它 -
$ mvn spring-boot:run
- 在您的 IDE 中,将 TodoManagementSpringBoot2Application.main() 方法作为独立的 Java 类运行,它将在端口 8080 上启动嵌入式 Tomcat 服务器,并将浏览器指向 http://localhost:8080/。
演示
1.登录页面
网址: http://localhost:8080/login
用户名:admin
密码:admin
这是 Spring Security 提供的简单登录页面。您还可以创建自定义登录页面。
2.首页
成功用户登录后导航到主页。
3.列出待办事项
网址: http://localhost:8080/list-todos
4. 创建待办事项
网址: http://localhost:8080/add-todo
5.更新待办事项
网址: http://localhost:8080/update-todo?id=28
6.登出页面
网址: http://localhost:8080/login?logout
我已经完成了使用带有 JSP 的 Spring boot 开发迷你 Todo Management Spring MVC Web 应用程序。
如果您对本文有任何建议或反馈,请发表评论,如果您需要我的帮助,请告诉我。
本文的源代码可在我的 Github 存储库中找到,地址为
GitHub - RameshMF/todo-management-spring-boot: Mini Todo Management Project using Spring Boot Spring MVC Spring Security JSP MySQL
这篇关于Spring Boot、Spring MVC、Spring Security、JSP、JPA 和 MySQL 作为数据库来开发 Todo待办Web应用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!