目录
  • Spring早已经成为企业级开发的业界标准,尤其是Spring Boot 2.0、Spring 5发布后,Spring的生态系统引领了技术架构发展的潮流,对于Java开发人员,深入掌握Spring全家桶的各种框架应用及必要的底层原理知识,是一件非常重要的事情。

在这里插入图片描述

Spring的核心是提供了一个容器(container),通常称为Spring应用上下文(Spring application context),它们会创建和管理应用组件。这些组件也可以称为bean,会在Spring应用上下文中装配在一起,从而形成一个完整的应用程序。
在这里插入图片描述

将bean装配在一起的行为是通过一种基于依赖注入(dependency injection,DI)的模式实现的。此时,组件不会再去创建它所依赖的组件并管理它们的生命周期,使用依赖注入的应用依赖于单独的实体(容器)来创建和维护所有的组件,并将其注入到需要它们的bean中。通常,这是通过构造器参数和属性访问方法来实现的。

在这里插入图片描述

在历史上,一般通过两种配置方式为Spring应用上下文提供Bean

  1. 使用一个或多个XML文件描述bean
  2. 使用@Configuration注解会告知Spring这是一个配置类

随着Spring Boot 2.x的引入,Spring自动配置的能力已经大大加强,Spring Boot能够基于类路径中的条目、环境变量和其他因素合理猜测需要配置的组件并将它们装配在一起。Java程序员尽可能多地使用Spring Boot,只有在必要的时候才使用显式配置。

  1. 在IntelliJ IDEA中创建新项目
    在这里插入图片描述
  2. 通过地址为https://start.spring.io/初始化项目;
    在这里插入图片描述
  3. 指定项目通用信息:
    在这里插入图片描述
  4. 选择项目Starter:
    在这里插入图片描述
  5. 生成的项目结构:
    在这里插入图片描述
  6. maven规范
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.1.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>demo</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-test</artifactId>
  27. <scope>test</scope>
  28. <exclusions>
  29. <exclusion>
  30. <groupId>org.junit.vintage</groupId>
  31. <artifactId>junit-vintage-engine</artifactId>
  32. </exclusion>
  33. </exclusions>
  34. </dependency>
  35. </dependencies>
  36. <build>
  37. <plugins>
  38. <plugin>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-maven-plugin</artifactId>
  41. </plugin>
  42. </plugins>
  43. </build>
  44. </project>

可参见本人博客《Maven POM( Project Object Model,项目对象模型 )》《一图说清maven常见要素》这两篇文章。

  1. 入口类
  1. /**
  2. * SpringBoot应用
  3. */
  4. @SpringBootApplication
  5. public class DemoApplication {
  6. public static void main(String[] args) {
  7. // 运行应用
  8. SpringApplication.run(DemoApplication.class, args);
  9. }
  10. }

@SpringBootApplication是一个组合注解,它组合了3个其他的注解。

  • @SpringBootConfiguration:将该类声明为配置类。尽管这个类目前还没有太多的配置,但是后续我们可以按需添加基于Java的Spring框架配置。这个注解实际上是@Configuration注解的特殊形式。
  • @EnableAutoConfiguration:启用Spring Boot的自动配置。我们随后会介绍自动配置的更多功能。就现在来说,我们只需要知道这个注解会告诉SpringBoot自动配置它认为我们会用到的组件。
  • @ComponentScan:启用组件扫描。这样我们能够通过@Component@Controller、@Service这样的注解声明其他类,Spring会自动发现它们并将它们注册为Spring应用上下文中的组件。

在这里插入图片描述
8. 测试类

  1. // SpringBoot测试
  2. @SpringBootTest
  3. class DemoApplicationTests {
  4. // 测试方法
  5. @Test
  6. void contextLoads() {
  7. }
  8. }
  • 程序启动
    在这里插入图片描述
  • 第一个Controller

index()是一个简单的控制器方法。它带有@GetMapping注解,表明如果针对“/”发送HTTP GET请求,那么这个方法将会处理请求。该方法所做的只是返回String类型的index值,该控制器方法中还通过Spring自动注入IndexService服务组件,及调用服务组件方法。

  1. /**
  2. * 第一个SpringMVC程序--Controller层
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-02
  6. */
  7. @Controller
  8. public class IndexController {
  9. // Spring注入服务组件
  10. @Autowired
  11. private IndexService indexService;
  12. @GetMapping("/")
  13. public String index(Model model) {
  14. String index = indexService.getIndex();
  15. model.addAttribute("index", index);
  16. // 返回视图 即index.html
  17. return "index";
  18. }
  19. }
  • 第一个Service

getIndex()是一个简单的服务方法。该方法所做的只是返回String类型的index值,该服务组件供控制层调用。

  1. /**
  2. * 第一个SpringMVC程序--Service层
  3. * * @author zhuhuix
  4. * @date 2020-07-02
  5. */
  6. @Service
  7. public class IndexService {
  8. public String getIndex() {
  9. return "hello world!!!";
  10. }
  11. }
  • 第一个View

— 使用Thymeleaf模板引擎

  1. ### application.properties
  2. ###ThymeLeaf配置
  3. spring:
  4. thymeleaf:
  5. #模板的模式,支持 HTML, XML TEXT JAVASCRIPT
  6. mode: HTML5
  7. #编码 可不用配置
  8. encoding: UTF-8
  9. #内容类别,可不用配置
  10. content-type: text/html
  11. #开发配置为false,避免修改模板还要重启服务器
  12. cache: false
  13. #配置模板路径,默认是templates,可以不用配置
  14. prefix: classpath:/templates

— 定义index.html视图层

  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org" >
  3. <head>
  4. <meta charset="UTF-8"/>
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <p th:text="${index}" />
  9. </body>
  10. </html>
  • 运行测试
    在这里插入图片描述

•代码变更后应用会自动重启;
•当面向浏览器的资源(如模板、JavaScript、样式表)等发生变化时,会自动刷新浏览器

  • pom.xml
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-devtools</artifactId>
  5. <optional>true</optional>
  6. <scope>runtime</scope>
  7. </dependency>
  8. ...
  9. </dependencies>
  10. <build>
  11. <plugins>
  12. <plugin>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-maven-plugin</artifactId>
  15. <configuration>
  16. <!--fork : devtools生效必须设置成true -->
  17. <fork>true</fork>
  18. </configuration>
  19. </plugin>
  20. </plugins>
  21. </build>
  • idea设置
    — 需勾选Build project automaticallty
    在这里插入图片描述
    — ctrl+alt+shift+/ :Registry 中第一项必须打勾
    在这里插入图片描述
  • Spring核心框架:Spring核心框架是Spring领域中一切的基础。它提供了核心容器和依赖注入框架。
  • Spring Boot:Spring Boot构建在Spring之上,通过简化依赖管理、自动配置和运行时洞察,使Spring更加易用;
  • Spring MVC:我们通过SpringBoot初始化生成的框架上加入Controller,Service,View的分层,编写了第一个Spring MVC程序,并成功运行。
  • 使用Idea开发环境,安装Spring Boot DevTools并进行配置,提高了开发效率。

. 在上一小节中创建了第一个DEMO,本章将继续基于SpringMVC框架构建我们的web应用,该应用需要实现用户登记,具体实现步骤如下:

  1. 创建用户的数据模型;
  2. 在服务层编写用户登记的业务逻辑;
  3. 生成为Web浏览器提供用户登记内容的控制器
  4. 在视图层运用模板引擎展示数据及校验表单输入

在这里插入图片描述

  • 创建一个用户类,定义用户id,用户名称,邮箱三个属性;
  • 添加默认构造方法与全属性构造方法;
  • 对用户属性添加对应getter,setter方法;
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. */
  7. public class User implements Serializable {
  8. // 用户id
  9. private Long id;
  10. // 用户名
  11. private String name;
  12. // 邮箱
  13. private String email;
  14. User(){ }
  15. public User(Long id, String name, String email) {
  16. this.id = id;
  17. this.name = name;
  18. this.email = email;
  19. }
  20. public Long getId() {
  21. return id;
  22. }
  23. public void setId(Long id) {
  24. this.id = id;
  25. }
  26. public String getName() {
  27. return name;
  28. }
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32. public String getEmail() {
  33. return email;
  34. }
  35. public void setEmail(String email) {
  36. this.email = email;
  37. }
  38. }
  • 实现返回所有用户数据的列表
  • 实现增加用户的功能
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户服务类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. */
  7. @Service
  8. public class UserService {
  9. public static ArrayList<User> users = new ArrayList<>();
  10. // mock数据
  11. public UserService() {
  12. users.add(new User(1L, "Mike", "mike@gmail.com"));
  13. users.add(new User(2L, "Jack", "jack@gmail.com"));
  14. users.add(new User(3L, "Kate", "kate@gmail.com"));
  15. users.add(new User(4L, "Mary", "mary@gmail.com"));
  16. users.add(new User(5L, "Rose", "rose@gmail.com"));
  17. }
  18. // 返回所有的用户
  19. public List<User> listUsers() {
  20. return users;
  21. }
  22. // 增加用户
  23. public User saveUser(User user) {
  24. user.setId(users.size() + 1L);
  25. users.add(user);
  26. return user;
  27. }
  28. }

在Spring MVC框架中,控制器是重要的参与者。它们的主要职责是处理HTTP请求传递给视图以便于渲染HTML(浏览器展现)。

  • SpirngMVC的请求注解
注解 描述
@RequestMapping 通用的请求
@GetMapping 处理HTTP GET请示
@PostMapping 处理HTTP POST请示
@PutMapping 处理HTTP PUT请示
@DeleteMapping 处理HTTP DELETE请示
  • 用户控制器的处理内容
    — 处理路径为“/user”的HTTP GET请求,向服务层调用返回所有用户数据列表的接口,获取数据后传递给对应的视图模板,并发送给发起请求的Web浏览器。
    — 处理路径为“/user”的HTTP POST请求,向服务层调用增加用户的接口,处理成功后调用路径为“/user”的HTTP GET请求,并发送给发起请求的Web浏览器。
    — 处理路径为“/user/form”的HTTP GET请求,产生一个新用户数据模型,并调用对应的视图模板,发送给发起请求的Web浏览器。
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户控制器
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. */
  7. @RestController
  8. @RequestMapping("/user")
  9. public class UserController {
  10. @Autowired
  11. private UserService userService;
  12. // 保存用户并返回到用户列表页面
  13. @PostMapping
  14. public ModelAndView saveUser(User user) {
  15. userService.saveUser(user);
  16. return new ModelAndView("redirect:/user");//重定向到list页面
  17. }
  18. // 获取创建用户表单页面
  19. @GetMapping("/form")
  20. public ModelAndView createForm(Model model) {
  21. model.addAttribute("user",new User());
  22. return new ModelAndView("register","userModel",model);
  23. }
  24. // 获取用户列表页面
  25. @GetMapping
  26. public ModelAndView list(Model model) {
  27. model.addAttribute("userList", userService.listUsers());
  28. return new ModelAndView("userlist", "userModel", model);
  29. }
  30. }
  • 设计一个用户列表的视图模板
    — Thymeleaf提供了一个属性“th:each”,它会迭代一个元素集合,为集合中的每个条目渲染HTML,我们可以利用这个属性,设计出用户的列表视图
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org"
  3. xmlns:layout="http://www.ultrag.net.nz/thymeleaf/layout"
  4. >
  5. <head>
  6. <meta charset="UTF-8">
  7. </head>
  8. <body>
  9. <h3>用户列表</h3>
  10. <div>
  11. <a th:href="@{/user/form}">创建用户</a>
  12. </div>
  13. <table border="1">
  14. <thead>
  15. <tr>
  16. <td>ID</td>
  17. <td>Email</td>
  18. <td>Name</td>
  19. </tr>
  20. </thead>
  21. <tbody>
  22. <tr th:if="${userModel.userList.size()} eq 0">
  23. <td colspan="3">没有用户信息!</td>
  24. </tr>
  25. <tr th:each="user:${userModel.userList}">
  26. <td th:text="${user.id}"></td>
  27. <td th:text="${user.email}"></td>
  28. <td th:text="${user.name}"></td>
  29. </tr>
  30. </tbody>
  31. </table>
  32. </body>
  33. </html>
  • 设计一个提交新用户的视图模板
    — 用户通过这个视图,录用名称与邮箱地址,提交新用户的信息。
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org"
  3. xmlns:layout="http://www.ultrag.net.nz/thymeleaf/layout"
  4. >
  5. <head>
  6. <meta charset="UTF-8">
  7. </head>
  8. <body>
  9. <h3>登记用户</h3>
  10. <form action="/users" th:action="@{/user}" method="POST" th:object="${userModel.user}">
  11. <input type="hidden" name="id" th:value="*{id}">
  12. 名称:<br>
  13. <input type="text" name="name" th:value="*{name}">
  14. <br>
  15. 邮箱:<br>
  16. <input type="text" name="email" th:value="*{email}">
  17. <input type="submit" value="提交" >
  18. </form>
  19. </body>
  20. </html>

在这里插入图片描述

  • 到目前为止,我们已经开发了User用户模型、UserService用户服务类、UserController控制器、userlist用户列表视图模板、register用户登记视图模板,接下来我们可以尝试运行一下。打开浏览器并访问http://localhost:8080/user。

在这里插入图片描述

  • 点击页面上的创建用户,登记新用户,并提交
    在这里插入图片描述
    在这里插入图片描述
    该web应用一切运行正常。

虽然我们已经实现了用户列表与登记新用户,但视图层还存在漏洞,比如用户名称为空的时候不能保存,邮箱输入格式要符合规则,所以程序要对表单输入的内容进行校验。

  • 引入Validation API与Hibernate的Validation API 依赖
  1. <dependency>
  2. <groupId>org.hibernate.validator</groupId>
  3. <artifactId>hibernate-validator</artifactId>
  4. <version>6.0.13.Final</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>javax.validation</groupId>
  8. <artifactId>validation-api</artifactId>
  9. <version>2.0.1.Final</version>
  10. </dependency>
  • 在要被校验的类上声明校验规则:即在User类上声明校验规则。
  1. public class User implements Serializable {
  2. // 用户id
  3. @NotNull
  4. private Long id;
  5. // 用户名
  6. @NotBlank(message = "用户名称不能为空")
  7. private String name;
  8. // 邮箱
  9. @Pattern(message ="邮箱格式不符", regexp = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")
  10. private String email;
  11. ...
  12. }
  • 在控制器方法中声明要进行校验:即在UserController类的saveUser上增加用户数据的校验规则。
  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4. @Autowired
  5. private UserService userService;
  6. // 保存用户并返回到用户列表页面
  7. @PostMapping
  8. public ModelAndView saveUser(@Valid User user, Errors errors,Model model) {
  9. if (errors.hasErrors()){
  10. model.addAttribute("user",user);
  11. if (errors.getFieldError("name")!=null) {
  12. model.addAttribute("nameError", errors.getFieldError("name").getDefaultMessage());
  13. }
  14. if (errors.getFieldError("email")!=null) {
  15. model.addAttribute("emailError", errors.getFieldError("email").getDefaultMessage());
  16. }
  17. return new ModelAndView("register","userModel",model);
  18. }
  19. userService.saveUser(user);
  20. //重定向到list页面
  21. return new ModelAndView("redirect:/user");
  22. }
  23. ...
  24. }
  • 修改register表单视图以展现校验错误。
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. </head>
  6. <body>
  7. <h3>登记用户</h3>
  8. <form action="/users" th:action="@{/user}" method="POST" th:object="${userModel.user}">
  9. <input type="hidden" name="id" th:value="*{id}">
  10. 名称:<br>
  11. <input type="text" name="name" th:value="*{name}" >
  12. <br>
  13. 邮箱:<br>
  14. <input type="text" name="email" th:value="*{email}">
  15. <br>
  16. <input type="submit" value="提交" >
  17. <div style="color:red" th:text="${nameError}"></div>
  18. <div style="color:red" th:text="${emailError}"></div>
  19. </form>
  20. </body>
  21. </html>

在这里插入图片描述

  • Spring提供了一个强大Spring MVC的Web框架。
  • Spring MVC是基于注解的,通过像@RequestMapping、@GetMapping和@PostMapping这样的注解来启用请求处理方法的声明。
  • 请求处理方法返回一个Thymeleaf模板,同时会带有模型数据。
  • Spring MVC支持表单校验。

. 在上一小节中基于SpringMVC框架构建了我们的web应用,并在视图层运用模板引擎展示数据及校验表单输入,本章将使用JdbcTemplate及Spring Data实现数据持久化的操作。

  • 一说到数据的持久化,首选方案就是关系型数据库,本文将使用互联网领域最常用mysql数据库。

MySQL 最流行的关系型数据库管理系统,MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言,MySQL由于性能高、成本低、可靠性好,已经成为最流行的开源数据库,因此被广泛地应用在互联网领域中。
在这里插入图片描述

  • 根据用户信息模型类,设计用户信息登录表
  1. DROP DATABASE IF EXISTS user_info;
  2. CREATE DATABASE user_info
  3. DEFAULT CHARACTER SET utf8
  4. DEFAULT COLLATE utf8_general_ci;
  5. use user_info;
  6. SET NAMES utf8mb4;
  7. SET FOREIGN_KEY_CHECKS = 0;
  8. -- ----------------------------
  9. -- Table structure for user
  10. -- ----------------------------
  11. DROP TABLE IF EXISTS `user`;
  12. CREATE TABLE `user` (
  13. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  14. `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  15. `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  16. PRIMARY KEY (`id`) USING BTREE
  17. ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  18. SET FOREIGN_KEY_CHECKS = 1;

在这里插入图片描述

  • 增加依赖
  1. <!--Mysql依赖包-->
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <version>5.1.47</version>
  6. <scope>runtime</scope>
  7. </dependency>
  8. <!-- 数据库连接池:druid数据源驱动 -->
  9. <dependency>
  10. <groupId>com.alibaba</groupId>
  11. <artifactId>druid-spring-boot-starter</artifactId>
  12. <version>1.1.10</version>
  13. </dependency>
  • Spring配置
  1. #配置数据源
  2. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  3. spring.datasource.username=root
  4. spring.datasource.password=root
  5. spring.datasource.url=jdbc:mysql://user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true

Spring对JDBC的支持要归功于JdbcTemplate类。JdbcTemplate提供了一种特殊的方式,通过这种方式,开发人员在对关系型数据库执行SQL操作的时候能够避免使用JDBC时常见的繁文缛节和样板式代码。

  • 增加依赖
  1. <!-- JDBC -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-jdbc</artifactId>
  5. </dependency>
  • 修改UserService业务逻辑
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户服务类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. * @date 2020-07-04 增加通过jdbcTemplate处理数据
  7. */
  8. @Service
  9. public class UserService {
  10. @Autowired
  11. private JdbcTemplate jdbcTemplate;
  12. // 返回所有的用户
  13. public List<User> listUsers() {
  14. return jdbcTemplate.query("select id,name,email from user;",
  15. new Object[]{}, new BeanPropertyRowMapper<>(User.class));
  16. }
  17. // 增加用户
  18. public int saveUser(User user) {
  19. return jdbcTemplate.update("insert into user(name,email) values(?,?);"
  20. , user.getName(), user.getEmail());
  21. }
  22. }
  • 我们只修改了UserService用户服务类;控制器、视图模板都未做任何改变,接下来我们可以再次尝试运行一下。打开浏览器并访问http://localhost:8080/user。
    在这里插入图片描述

  • 输入用户信息并提交
    在这里插入图片描述
    在这里插入图片描述

  • 查看数据库用户信息表
    在这里插入图片描述

相对于普通的JDBC,Spring的JdbcTemplate能够极大地简化关系型数据库的使用。但是,你会发现使用JPA会更加简单。接下来我们会继续通过Spring Data框架让数据持久化变得更简单。

在上篇文章中,我们使用mysql数据库与JdbcTemplate简单实现了数据持久化操作,并对web程序进行了测试,本篇文章将继续通过Spring Data框架让数据持久化变得更简单。

  • Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得数据库的访问变得方便快捷,常用的子项目有:

Spring Data JDBC -数据访问组件对JDBC的支持。

Spring Data JPA:-基于关系型数据库进行JPA持久化。

Spring Data MongoDB – 持久化到Mongo文档数据库。

Spring Data Redis-持久化到Redis key-value内存数据库。

Spring Data REST:通过Spring Data数据访问组件导出为RESTful资源。

Spring Data Cassandra:持久化到Cassandra数据库。

  • 本文会基于原JDBC的实现替换为使用SpringData JPA的repository
  1. <!--pom.xml-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-jpa</artifactId>
  5. </dependency>
  • 首先我们给原来的user表增加两个时间字段
  1. ALTER TABLE user ADD COLUMN create_time DATETIME;
  2. ALTER TABLE user ADD COLUMN update_time DATETIME;
  • 修改user.class
    — 给user类添加@Entity注解,声明为JPA实体
    — 给id字段添加@Id注解将其指定为数据库中唯一标识该实体的属性
    — 给id字段添加@GeneratedValue注解,依赖数据库自动生成ID值
    — 给其它字段添加@Column注解,并声明对应user表中的字段名称
    — 增加createTime成员,添加@CreationTimestamp注解,使用该注解可以让Hibernate在插入数据时对注解的属性对应的日期类型创建默认值
    — 增加updateTime成员,添加@UpdateTimestamp注解,使用该注解可以让Hibernate在更新数据时对注解的属性对应的日期类型进行更新
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. * @date 2020-07-07 添加JPA映射注解
  7. */
  8. @Entity
  9. public class User implements Serializable {
  10. // 用户id
  11. @Id
  12. @GeneratedValue(strategy = GenerationType.IDENTITY)
  13. private Long id;
  14. // 用户名
  15. @NotBlank(message = "用户名称不能为空")
  16. @Column(name="name")
  17. private String name;
  18. // 邮箱
  19. @Column(name="email")
  20. @Pattern(message ="邮箱格式不符", regexp = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")
  21. private String email;
  22. // 创建时间
  23. @Column(name = "create_time")
  24. @CreationTimestamp
  25. private Timestamp createTime;
  26. // 更新时间
  27. @Column(name = "update_time")
  28. @UpdateTimestamp
  29. private Timestamp updateTime;
  30. public User(Long id, String name, String email) {
  31. this.id = id;
  32. this.name = name;
  33. this.email = email;
  34. }
  35. public Long getId() {
  36. return id;
  37. }
  38. public void setId(Long id) {
  39. this.id = id;
  40. }
  41. public String getName() {
  42. return name;
  43. }
  44. public void setName(String name) {
  45. this.name = name;
  46. }
  47. public String getEmail() {
  48. return email;
  49. }
  50. public void setEmail(String email) {
  51. this.email = email;
  52. }
  53. }
  • 借助Spring Data JPA,我们可以通过继承CrudRepository接口,快速定义应用的数据层。CrudRepository定义并实现了很多用于CRUD(创建、读取、更新、删除)操作的方法,我们根本就不用编写实现类!当应用启动的时候,Spring DataJPA会在运行期自动生成实现类。这意味着,我们现在就可以在服务层直接设用repository的增删改查操作了。
  1. /**
  2. * 基于SpringMVC框架开发web应用--数据操作层
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-07
  6. */
  7. public interface UserRepository extends CrudRepository<User,Long> {
  8. }
  • 接下来我们在UserService层直接调用UserRepository中由Spring Data JPA提供的各种方法,实现查询及增加用户信息。
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户服务类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. * @date 2020-07-04 增加通过jdbcTemplate处理数据
  7. * @date 2020-07-07 将jdbcTemplate处理数据程序改为Spring Data JPA的处理方式
  8. */
  9. @Service
  10. public class UserService {
  11. @Autowired
  12. private UserRepository userRepository;
  13. // 返回所有的用户
  14. public List<User> listUsers() {
  15. return (List<User>) userRepository.findAll();
  16. }
  17. // 增加用户
  18. public User saveUser(User user) {
  19. return userRepository.save(user);
  20. }
  21. }
  • 我们增加了数据层并修改了UserService服务层;控制器、视图模板都未做任何改变,接下来我们可以再次尝试运行一下。打开浏览器并访问http://localhost:8080/user。
    在这里插入图片描述

  • 输入用户信息并提交
    在这里插入图片描述
    在这里插入图片描述

  • 查看数据库用户信息表
    在这里插入图片描述

  • 测试结论:通过将JdbcTemplate替换成Spring Data JPA,同样实现了用户信息的查询与增加,而且通过JPA CrudRepository接口,我们没有书写一行SQL语句,也实现了数据的增加与查询。

  • 除了CrudRepository提供的基本CRUD操作之外,还需要通过用户名称或邮箱地址查找用户信息,那我们需要添加如下的方法声明到UserRepository中:
  1. public interface UserRepository extends CrudRepository<User,Long> {
  2. // 自定义添加通过用户名称查找用户信息
  3. List<User> findByName(String name);
  4. }

按照Spring Data的规范的规定,查询方法以find | read | get开头(比如 find、findBy、read、readBy、get、getBy),涉及查询条件时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
直接在接口中定义查询方法,如果是符合规范的,可以不用写实现,即不用写SQL。

  1. @Service
  2. public class UserService {
  3. @Autowired
  4. private UserRepository userRepository;
  5. ...
  6. // 根据名称查找用户
  7. public List<User> searchUser(String name){
  8. return userRepository.findByName(name);
  9. }
  10. }
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户控制器
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. * @date 2020-07-07 增加用户查找
  7. */
  8. @RestController
  9. @RequestMapping("/user")
  10. public class UserController {
  11. @Autowired
  12. private UserService userService;
  13. ...
  14. // 查找输入页面
  15. @GetMapping("/index")
  16. public ModelAndView index(Model model) {
  17. model.addAttribute("user", new User());
  18. return new ModelAndView("index", "userModel", model);
  19. }
  20. // 查找提交并跳转用户列表
  21. @PostMapping("/search")
  22. public ModelAndView search(@ModelAttribute User user, Model model) {
  23. model.addAttribute("userList", userService.searchUser(user.getName()));
  24. return new ModelAndView("userlist", "userModel", model);
  25. }
  26. }
  1. <!-- index.html-->
  2. <!DOCTYPE html>
  3. <html xmlns:th="http://www.thymeleaf.org" >
  4. <head>
  5. <meta charset="UTF-8"/>
  6. <title>Title</title>
  7. </head>
  8. <body>
  9. <h3>查找用户</h3>
  10. <form action="/users" th:action="@{/user/search}" method="POST" th:object="${userModel.user}">
  11. 名称:<br>
  12. <input type="text" name="name" th:value="*{name}" >
  13. <br>
  14. <input type="submit" value="查询" >
  15. </form>
  16. </body>
  17. </html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • Spring Data JPA能够极大地简化JPA持久化,我们只需编写repository接口即可;
  • Spirng Data 对于实体类可以通过各种注解进行数据的管理:比如@Id ,@Column,@CreationTimestamp等等
  • 只需要符合JPA的接口命名规则,我们可以自定义JPA repository各种对于数据的操作方法:比如findBy… ,findAllBy…,findBy..And..等。
  • 在前四篇文章中已经实现了一个非常简单的用户邮箱登记的web应用,并将数据保存到mysql数据库中。但这个web应用涉及的数据展示、查询及操作都是开放的,如果部署在互联网上,可以访问应用的人员都可随意增加及查询数据,这显然是不符合安全要求的。
    作为软件开发人员,我们必须采取措施来保护应用程序中的信息。

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的标准。

本文将通过Spring Security配置实现web应用的如下功能:

  • 实现页面的WEB安全保护
  • 实现管理员的帐户注册
  • 实现登录验证的完整流程
  • 在pom.xml文件中添加依赖
  1. <!-- Spring Security-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-security </artifactId>
  5. </dependency>

通过将security starter添加到项目的构建文件中,我们得到了如下的安全特性:

  • 所有的HTTP请求路径都需要认证;
  • 不需要特定的角色和权限;
  • 没有登录页面;
  • 认证过程是通过HTTP basic认证对话框实现的;
  • 系统只有一个用户,用户名为user。

我们试着启动一下应用,并访问用户列表页面,发现程序竟然跳转到了一个login登录页面,并要求输入用户和密码。
在这里插入图片描述
这是一个HTTP basic认证对话框,提示进行认证。要想通过这个认证,需要一个用户名和密码。用户名为user,而密码则是随机生成的,可以在日志监控界面上查到:
在这里插入图片描述
我们试着在登录界面上输入用户名user与图中的密码,进行认证登录:
在这里插入图片描述
在这里插入图片描述
程序顺利通过了密码认证,并进入到用户列表界面。但显然以上的方式是不符合需求的,我们需要的是:

  • 通过自定义的登录页面来提示管理员用户进行认证;
  • 提供一个注册页面,新管理员用户能够注册;
  • 对不同的请求路径,执行不同的安全规则。比如注册页面不需要进行认证,其他页面需要进行认证。
  1. -- ----------------------------
  2. -- Table structure for admin
  3. -- ----------------------------
  4. DROP TABLE IF EXISTS `admin`;
  5. CREATE TABLE `admin` (
  6. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  7. `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  8. `user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  9. PRIMARY KEY (`id`) USING BTREE
  10. ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  11. SET FOREIGN_KEY_CHECKS = 1;
  1. /**
  2. * 基于SpringMVC框架开发web应用--管理员用户类,用于登录认证
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. */
  7. @Entity
  8. public class Admin implements UserDetails {
  9. // 管理员id
  10. @Id
  11. @GeneratedValue(strategy = GenerationType.IDENTITY)
  12. private Long id;
  13. @Column(name="user_name")
  14. private String userName;
  15. @Column(name="user_password")
  16. private String userPassword;
  17. @Override
  18. public Collection<? extends GrantedAuthority> getAuthorities() {
  19. return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
  20. }
  21. @Override
  22. public String getPassword() {
  23. return userPassword;
  24. }
  25. @Override
  26. public String getUsername() {
  27. return userName;
  28. }
  29. @Override
  30. public boolean isAccountNonExpired() {
  31. return true;
  32. }
  33. @Override
  34. public boolean isAccountNonLocked() {
  35. return true;
  36. }
  37. @Override
  38. public boolean isCredentialsNonExpired() {
  39. return true;
  40. }
  41. @Override
  42. public boolean isEnabled() {
  43. return true;
  44. }
  45. public Long getId() {
  46. return id;
  47. }
  48. public void setId(Long id) {
  49. this.id = id;
  50. }
  51. public void setUserName(String userName) {
  52. this.userName = userName;
  53. }
  54. public void setUserPassword(String userPassword) {
  55. this.userPassword = userPassword;
  56. }
  57. }
  • 管理员类实现了Spring Security的UserDetails接口。通过实现UserDetails接口,我们能够提供更多信息给框架,比如用户都被授予了哪些权限以及用户的账号是否可用。
  • getAuthorities()方法返回用户被授予权限的一个集合。各种is…Expired()方法要返回一个boolean值,表明用户的账号是否可用或过期。
  1. /**
  2. * 基于SpringMVC框架开发web应用--管理员数据操作层
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. */
  7. public interface AdminRepository extends CrudRepository<Admin,Long> {
  8. // 根据用户名查找管理员
  9. Admin findByUserName(String username);
  10. }
  1. /**
  2. * 基于SpringMVC框架开发web应用--管理员服务层
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. */
  7. @Service
  8. public class AdminService implements UserDetailsService {
  9. private final Logger logger = LoggerFactory.getLogger(Logger.class);
  10. @Autowired
  11. private AdminRepository adminRepository;
  12. @Override
  13. public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  14. Admin admin = adminRepository.findByUserName(s);
  15. if (admin == null) {
  16. logger.error("管理员" + s + "未找到");
  17. throw new UsernameNotFoundException("User " + s + "not found");
  18. }
  19. return admin;
  20. }
  21. // 保存管理员
  22. public Admin save(Admin admin){
  23. return adminRepository.save(admin);
  24. }
  25. }
  1. /**
  2. * Spring Security配置类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. */
  7. @Configuration
  8. @EnableWebSecurity
  9. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  10. @Autowired
  11. private AdminService adminService;
  12. @Override
  13. protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
  14. authenticationManagerBuilder
  15. .userDetailsService(adminService)
  16. .passwordEncoder(new BCryptPasswordEncoder());
  17. }
  18. }

以上通过建立管理员信息表,及通过JPA定义数据处理层,编写获取管理员信息的服务实现,最后配置Spring Security Web安全类,实现了自定义的登录验证方法,下面具体来测试一下:
在这里插入图片描述
在这里插入图片描述
web应用程序已经实现了自定义的用户登录验证。

  • 以上虽然完成了管理员的登录验证,但没有涉及管理员的添加,以下需要完成管理员注册流程。
  1. /**
  2. * 基于SpringMVC框架开发web应用--管理员注册控制器
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. */
  7. @RestController
  8. @RequestMapping("/admin")
  9. public class AdminController {
  10. @Autowired
  11. private AdminService adminService;
  12. // 管理注册页
  13. @GetMapping
  14. public ModelAndView registerForm(Model model) {
  15. model.addAttribute("admin", new Admin());
  16. return new ModelAndView("registration", "adminModel", model);
  17. }
  18. // 提交注册信息
  19. @PostMapping("/register")
  20. public ModelAndView save(Admin admin) {
  21. BCryptPasswordEncoder bCryptPasswordEncoder =new BCryptPasswordEncoder();
  22. admin.setUserPassword(bCryptPasswordEncoder.encode(admin.getPassword()));
  23. if (adminService.save(admin) != null) {
  24. return new ModelAndView("redirect:/login ");
  25. } else {
  26. return null;
  27. }
  28. }
  29. }
  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. </head>
  6. <body>
  7. <h3>管理员注册</h3>
  8. <form action="/admin" th:action="@{/admin/register}" method="POST" th:object="${adminModel.admin}">
  9. 名称:<br>
  10. <input type="text" name="userName" th:value="*{userName}" >
  11. <br>
  12. 输入密码:<br>
  13. <input type="password" name="userPassword" th:value="*{userPassword}">
  14. <br>
  15. <input type="submit" value="注册" >
  16. </form>
  17. </body>
  18. </html>
  • 以上完成了管理员注册的业务逻辑、控制层、与页面视图,但如果我们直接访问管理员注册页面的时候,web应用程序仍然会跳转到login页面,要求输入用户名和密码,这显然不符合需求,我们只需要对需要认证的页面进行登录验证保护,对于注册页面是开放的,所以我们需要配置保护web请求的安全性规则:
  1. /**
  2. * Spring Security配置类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. */
  7. @Configuration
  8. @EnableWebSecurity
  9. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  10. @Autowired
  11. private AdminService adminService;
  12. // 自定义用户验证
  13. @Override
  14. protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
  15. authenticationManagerBuilder
  16. .userDetailsService(adminService)
  17. .passwordEncoder(new BCryptPasswordEncoder());
  18. }
  19. // 保护web请求的安全性规则
  20. @Override
  21. protected void configure(HttpSecurity http) throws Exception{
  22. http.authorizeRequests()
  23. .antMatchers("/user/**").access("hasRole('ROLE_USER')")
  24. .antMatchers("/admin/**").permitAll()
  25. .and().formLogin().defaultSuccessUrl("/user")
  26. .and().httpBasic();
  27. }
  28. }
  • 访问管理员注册页面进行注册
    在这里插入图片描述
    在这里插入图片描述
  • 使用注册的管理员帐户进行登录验证
    在这里插入图片描述
    在这里插入图片描述
  • Spring Security的自动配置是实现基本安全性功能的好办法,但是大多数的应用都需要自定义安全规则,这样才能满足特定的安全需求。
  • 认证用户详情信息可以通过自定义用户存储机制进行管理,它的后端可以是关系型数据库。
  • 注意:以上实现功能有不完全处:如管理员注册时,需在页面进行密码的两次确认,服务层需判断帐户名是否存在等,请大家借鉴使用。
  • 本小节将了解Spring的配置属性及完成自定义的配置

Spring的环境抽象是各种配置属性的一站式服务。它抽取了原始的属性,这样需要这些属性的bean就可以从Spring本身中获取了。Spring环境会拉取多个属性源,包括:

  • JVM系统属性;
  • 操作系统环境变量;
  • 命令行参数;
  • 应用属性配置文件。

在这里插入图片描述

  • 我们回顾下原web应用中的application.properties文件:
  1. ###数据源配置
  2. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  3. spring.datasource.username=root
  4. spring.datasource.password=root
  5. spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
  6. spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
  7. #thymelea模板配置
  8. spring.thymeleaf.prefix=classpath:/templates/
  9. spring.thymeleaf.suffix=.html
  10. spring.thymeleaf.mode=HTML5
  11. spring.thymeleaf.encoding=UTF-8
  12. spring.thymeleaf.content-type=text/html
  13. spring.thymeleaf.cache=false
  14. spring.resources.chain.strategy.content.enabled=true
  15. spring.resources.chain.strategy.content.paths=/**
  • Spring Boot引入了具有层次关系的yml格式的配置文件:
  1. spring:
  2. port:8080

显然具有层次关系的配置文件更易于理解与书写,接下来我们将使用application.yml取代application.properties完成各种属性配置。

  1. ###数据源配置
  2. spring:
  3. datasource:
  4. type: com.alibaba.druid.pool.DruidDataSource
  5. username: root
  6. password: root
  7. driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
  8. url: jdbc:log4jdbc:mysql://localhost:3306/user_info?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
  1. #thymelea模板配置
  2. thymeleaf:
  3. prefix: classpath:/templates/
  4. suffix: .html
  5. mode: HTML5
  6. encoding: UTF-8
  7. content-type: text/html
  8. cache: false
  • 默认情况下,Spring Boot通过Logback配置日志,日志会以INFO级别写入到控制台中,我们希望重新配置显示日志的格式;
  • 同时我们希望通过监控sql日志输出到控制台,并将输出的信息进行筛选打印;
  • 首先需引入log4jdbc依赖
  1. <dependency>
  2. <groupId>org.bgee.log4jdbc-log4j2</groupId>
  3. <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
  4. <version>1.16</version>
  5. </dependency>
  • 其次生成配置文件log4jdbc.log4j2.properties:
  1. log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
  2. log4jdbc.auto.load.popular.drivers=false
  3. log4jdbc.drivers=com.mysql.cj.jdbc.Driver
  • 最后创建一个logback.xml文件完成配置:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration scan="true" scanPeriod="30 seconds" debug="false">
  3. <contextName>web demo</contextName>
  4. <property name="log.charset" value="utf-8" />
  5. <property name="log.pattern" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}) - %gray(%msg%n)" />
  6. <!--输出到控制台-->
  7. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  8. <encoder>
  9. <pattern>${log.pattern}</pattern>
  10. <charset>${log.charset}</charset>
  11. </encoder>
  12. </appender>
  13. <!--普通日志输出到控制台-->
  14. <root level="info">
  15. <appender-ref ref="console" />
  16. </root>
  17. <!--监控sql日志输出 -->
  18. <logger name="jdbc.sqlonly" level="INFO" additivity="false">
  19. <appender-ref ref="console" />
  20. </logger>
  21. <logger name="jdbc.resultset" level="ERROR" additivity="false">
  22. <appender-ref ref="console" />
  23. </logger>
  24. <!-- 如想看到表格数据,将OFF改为INFO -->
  25. <logger name="jdbc.resultsettable" level="OFF" additivity="false">
  26. <appender-ref ref="console" />
  27. </logger>
  28. <logger name="jdbc.connection" level="OFF" additivity="false">
  29. <appender-ref ref="console" />
  30. </logger>
  31. <logger name="jdbc.sqltiming" level="OFF" additivity="false">
  32. <appender-ref ref="console" />
  33. </logger>
  34. <logger name="jdbc.audit" level="OFF" additivity="false">
  35. <appender-ref ref="console" />
  36. </logger>
  37. </configuration>
  • 输出格式如下:
    在这里插入图片描述

为了支持配置属性的注入,Spring Boot提供了@ConfigurationProperties注解。将它放到Spring 应用上下文的 bean之后,它就会为该bean中那些能够根据Spring环境注入值的属性赋值。

  • 我们希望给管理员注册时,添加一个应用的默认密码,假设用户注册时,不输入密码,web应用就以这个默认密码进行注册,并写入数据库。
  1. #管理员默认密码
  2. admin:
  3. password: 123456
  • 使用 @ConfigurationProperties 配置模块
  • 过添加 @Component 注解让 Component Scan 扫描到
  1. /**
  2. * 基于SpringMVC框架开发web应用--管理员用户默认密钥
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-09
  6. */
  7. @Component
  8. @ConfigurationProperties(prefix = "admin")
  9. public class DefaultPasswordProperties {
  10. private String password;
  11. public String getPassword() {
  12. return password;
  13. }
  14. public void setPassword(String password) {
  15. this.password = password;
  16. }
  17. }
  1. /**
  2. * 基于SpringMVC框架开发web应用--管理员注册控制器
  3. * * @author zhuhuix
  4. * @date 2020-07-08
  5. * @date 2020-07-09 注册密码为空时使用自定义的默认密码属性
  6. */
  7. @RestController
  8. @RequestMapping("/admin")
  9. public class AdminController {
  10. @Autowired
  11. private AdminService adminService;
  12. @Autowired
  13. private DefaultPasswordProperties defaultPasswordProperties;
  14. // 管理注册页
  15. @GetMapping
  16. public ModelAndView registerForm(Model model) {
  17. model.addAttribute("admin", new Admin());
  18. return new ModelAndView("registration", "adminModel", model);
  19. }
  20. // 提交注册信息
  21. @PostMapping("/register")
  22. public ModelAndView save(Admin admin) {
  23. // 如果注册密码为空,则赋值默认密码
  24. if (StringUtils.isEmpty(admin.getPassword())) {
  25. admin.setUserPassword(defaultPasswordProperties.getPassword());
  26. }
  27. BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
  28. admin.setUserPassword(bCryptPasswordEncoder.encode(admin.getPassword()));
  29. if (adminService.save(admin) != null) {
  30. return new ModelAndView("redirect:/login ");
  31. } else {
  32. return null;
  33. }
  34. }
  35. }
  • 注册admin2管理员,密码保持为空,并提交
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 用admin2管理员,密码为123456进行登录
    在这里插入图片描述
  • 登录成功
    在这里插入图片描述
  • Spring的配置属性可以通过命令行参数、环境变量、JVM系统属性、属性文件或YAML文件等方式进行设置。
  • Spring的配置属性可以用来覆盖自动配置相关的设置,包括指定数据源URL和日志级别。
  • Spring的配置属性可以添加@ConfigurationProperties注解,这样就能够从多个属性源中选取一个来注入它的值。

本节将进入到新的单元:Spring与应用的集成,今天先实现集成REST API服务。

微服务架构,前后端分离目前已成为互联网项目开发的业界标准,其核心思想就是前端(APP、小程序、H5页面等)通过调用后端的API接口,提交及返回JSON数据进行交互。

  • 移动设备、平板电脑、智能设备已经非常常见,许多应用程序采用了一种通用的设计,那就是将用户界面推到更接近客户端的地方,而让服务器公开API,通过这种API,各种客户端都能与后端功能进行交互。
  • REST API(REpresentation State Transfer Application Programming Interface):通过URL定位资源,用HTTP动词(GET,POST,DELETE,PUSH等)描述操作,与后端服务进行交互。
  • 在前几篇文章中我们用了模板引擎开发了多页应用(MultiPage Application,MPA),我们将在原有基础上按以下步骤实现集成API服务:

  • 创建用户管理的Restful Api(UserRestfulApi.java)的接口,实现增加、删除、修改、查找用户信息的API交互服务;

  • 集成Swagger2,对上述API接口进行测试。

  • Controller
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户restful api
  3. * 增加、删除、修改、查找用户信息的API交互服务
  4. *
  5. * @author zhuhuix
  6. * @date 2020-07-10
  7. */
  8. @RestController
  9. @RequestMapping("/user/api")
  10. public class UserRestfulApi {
  11. @Autowired
  12. private UserService userService;
  13. // 增加用户信息
  14. @PostMapping
  15. public ResponseEntity<User> addUser(User user) {
  16. return ResponseEntity.ok(userService.saveUser(user));
  17. }
  18. // 根据id删除用户
  19. @DeleteMapping("/{id}")
  20. public ResponseEntity deleteUser(@PathVariable Long id) {
  21. userService.deleteUser(id);
  22. return ResponseEntity.ok(HttpStatus.OK);
  23. }
  24. // 根据id修改用户
  25. @PutMapping("/{id}")
  26. public ResponseEntity<User> updateUser(@RequestBody User user) {
  27. return ResponseEntity.ok(userService.saveUser(user));
  28. }
  29. // 根据id查找用户
  30. @GetMapping("/{id}")
  31. public ResponseEntity<User> findUser(@PathVariable Long id) {
  32. return ResponseEntity.ok(userService.findUser(id));
  33. }
  34. }
  • Service
  1. /**
  2. * 基于SpringMVC框架开发web应用--用户服务类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-03
  6. * @date 2020-07-04 增加通过jdbcTemplate处理数据
  7. * @date 2020-07-07 将jdbcTemplate处理数据程序改为Spring Data JPA的处理方式
  8. * @date 2020-07-10 增加
  9. */
  10. @Service
  11. public class UserService {
  12. @Autowired
  13. private UserRepository userRepository;
  14. // 返回所有的用户
  15. public List<User> listUsers() {
  16. return (List<User>) userRepository.findAll();
  17. }
  18. // 保存用户
  19. public User saveUser(User user) {
  20. return userRepository.save(user);
  21. }
  22. // 删除用户
  23. public void deleteUser(Long id){
  24. userRepository.deleteById(id);
  25. }
  26. // 查找用户
  27. public User findUser(Long id){
  28. return userRepository.findById(id).get();
  29. }
  30. // 根据名称查找用户
  31. public List<User> searchUser(String name){
  32. return userRepository.findByName(name);
  33. }
  34. }
  • Repository
  1. /**
  2. * 基于SpringMVC框架开发web应用--数据操作层
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-07
  6. */
  7. public interface UserRepository extends CrudRepository<User,Long> {
  8. // 自定义添加通过用户名称查找用户信息
  9. List<User> findByName(String name);
  10. }

Swagger2 作为一个规范和完整的框架,可以用于生成、描述、调用和可视化 RESTful 风格的 Web 服务:
1、 接口文档在线自动生成,文档随接口变动实时更新,节省维护成本
2、 支持在线接口测试,不依赖第三方工具

  • 添加maven依赖
  1. <!-- RESTful APIs swagger2 -->
  2. <dependency>
  3. <groupId>io.springfox</groupId>
  4. <artifactId>springfox-swagger2</artifactId>
  5. <version>2.9.2</version>
  6. <exclusions>
  7. <exclusion>
  8. <groupId>io.swagger</groupId>
  9. <artifactId>swagger-annotations</artifactId>
  10. </exclusion>
  11. <exclusion>
  12. <groupId>io.swagger</groupId>
  13. <artifactId>swagger-models</artifactId>
  14. </exclusion>
  15. </exclusions>
  16. </dependency>
  17. <dependency>
  18. <groupId>io.springfox</groupId>
  19. <artifactId>springfox-swagger-ui</artifactId>
  20. <version>2.9.2</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>io.swagger</groupId>
  24. <artifactId>swagger-annotations</artifactId>
  25. <version>1.5.21</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>io.swagger</groupId>
  29. <artifactId>swagger-models</artifactId>
  30. <version>1.5.21</version>
  31. </dependency>
  • 创建 Swagger2配置类
  1. /**
  2. * Swagger2配置类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-10
  6. */
  7. @Configuration
  8. @EnableSwagger2
  9. public class SwaggerConfig {
  10. @Bean
  11. @SuppressWarnings("all")
  12. public Docket createRestApi() {
  13. return new Docket(DocumentationType.SWAGGER_2)
  14. .enable(true)
  15. .apiInfo(apiInfo())
  16. .select()
  17. .paths(Predicates.not(PathSelectors.regex("/error.*")))
  18. .build();
  19. }
  20. private ApiInfo apiInfo() {
  21. return new ApiInfoBuilder()
  22. .title("Restful Api接口测试")
  23. .version("1.0")
  24. .build();
  25. }
  26. }

在这里插入图片描述

  • 在测试之前,为防止post、put、delete请求出现 403 Forbidden ,需要禁用跨域请求的安全验证
  1. /**
  2. * Spring Security配置类
  3. *
  4. * @author zhuhuix
  5. * @date 2020-07-08
  6. * @date 2020-07-10 禁用跨域请求的安全验证
  7. */
  8. @Configuration
  9. @EnableWebSecurity
  10. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  11. @Autowired
  12. private AdminService adminService;
  13. // 自定义用户验证
  14. @Override
  15. protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
  16. authenticationManagerBuilder
  17. .userDetailsService(adminService)
  18. .passwordEncoder(new BCryptPasswordEncoder());
  19. }
  20. // 保护web请求的安全性规则
  21. @Override
  22. protected void configure(HttpSecurity http) throws Exception{
  23. http.authorizeRequests()
  24. .antMatchers("/user/**").access("hasRole('ROLE_USER')")
  25. .antMatchers("/admin/**").permitAll()
  26. .and().formLogin().defaultSuccessUrl("/user")
  27. .and().httpBasic()
  28. // 禁用 CSRF
  29. .and().csrf().disable();
  30. }
  31. }
增加用户(HTTP POST请求)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查找用户(HTTP GET请求)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改用户(HTTP PUT请求)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

删除用户(HTTP DELETE请求)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

至此我们完成了Spring集成restful api服务,并通过集成Swagger2,简单直观地对http的各种请求进行了完整地测试,下面做个总结:

  • Rest端点可以通过Spring MVC来创建,这里的控制器与面向浏览器的控制器遵循相同的编程模型。
  • @RestController注解简化了REST控制器,使用它的话,处理器方法中就不需要添加@ResponseBody注解了。
  • Restful Api一般会添加JWT认证机制进行安全验证,具体可参见《SpringBoot整合SpringSecurity实现JWT认证》。

版权声明:本文为zhuhuix原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhuhuix/p/13280080.html