2021.2.24 更新

1 概述

这是Spring Boot的第二个Demo,一个只有三层架构的极简Web应用,持久层使用的是MyBatis。

2 架构

一个最简单的Spring Boot Web应用分为三层:

  • Controller层:负责具体业务流程的控制,调用Service层来控制业务逻辑
  • Service层:业务逻辑层,真正执行业务的操作,比如获取用户信息等
  • Dao层:负责数据持久化,在这一层中与各种数据库,比如MySQL、Oracle等打交道

先来简单说一下三层所使用到的注解。

2.1 Controller层

Controller层也是入口层,一般涉及如下注解:

  • @Controller:@Controller是经典的Controller层注解,@Controller标识的类代表该类是控制器类
  • @RequestMapping:使用@RequestMapping可以对请求进行映射,可以注解在类上或者方法上,在类上的话表示该类所有的方法都是以该地址作为父地址,在方法上就表示可以映射对应的请求到该方法上
  • @GetMapping/@PostMapping:这两者实际上是@RequestMapping对应不同方法的简化版,因为@RequestMapping有一个method属性,如果该method指定为GET那么就相当于@GetMapping,如果指定为POST就相当于@PostMapping
  • @ResponseBody:作用在方法上,将返回的数据进行可能的转换(取决于请求头,转换为JSON或XML等等,默认的情况下比如单纯字符串就直接返回),比如返回语句为return "success";,如果加上了@ResponseBody就直接返回success,如果不加上就会跳转到success.jsp页面
  • @RequestParm:处理Contrent-Type为application/x-www-form-urlencoded的内容,可以接受简单属性类型或者对象,支持GET+POST
  • @RequestBody:处理Content-Type不为application/x-www-form-urlencoded的内容(也就是需要指定Content-Type),不支持GET,只支持POST
  • @PathVariable:可以将占位符的参数传入方法参数,比如/path/1,可以将1传入方法参数中
  • @PathParm:与@RequestParm一样,一般使用@RequestParm
  • @RestController:相当于@Controller+@ResponseBody

2.2 Service层

Service层用于执行主要的业务逻辑,主要就是下面这个注解:

  • @Serice:是一个增强型的@Component,@Component表示一个最普通的组件,可以被注入到Spring容器进行管理,而@Service是专门用于处理业务逻辑的注解,@Controller类似,也是一个增强型的@Component,专门用于Controller层的处理

2.3 Dao层

Dao是数据持久层,这里进行数据持久化的操作,一般加上@Repository即可:

  • @Repository:也是一个增强型的@Component,注解在持久层中,具有将具体数据库抛出的异常转为Spring持久层异常的功能

讲完注解了下面就开始实践一下。

3 实践

3.1 新建项目

选择如下依赖:

Lombok能简化代码,推荐使用,并且需要IDEA安装插件。ORM框架这里选择MyBatis。

3.2 新建包

新建如下四个包:

  • controller
  • dao
  • entity
  • service
  • config

3.3 Controller层

3.3.1 简单Controller

在controller包下新建Controller.java:

@RestController
@RequestMapping("/")
public class Controller {
@GetMapping("test")
public String testMethod()
{
return "test controller";
}
}

运行之后,如果出现如下错误:

这是因为没有配置数据源,可以先把MySQL和MyBatis的依赖删去:

运行之后在浏览器输入localhost:8080/test会返回test controller:

这样一个最简单的Controller就完成了。

3.3.2 @RequestParm

然后下一步是添加参数,可以考虑使用@RequestParm添加:

@GetMapping("withParm")
public String withParm(@RequestParam String id)
{
return "id:"+id;
}

这样直接访问localhost:8080/withParm是不行的,因为没有携带id参数:

加入参数即可,也就是localhost:8080/withParm?id=1:

3.3.3 @PathVariable

另一种添加参数的方式是使用@PathVariable:

@GetMapping("path1/{id}")
public String path1(@PathVariable("id") String id)
{
return "id:"+id;
}

这样不是加入?id=xx,而是直接加入占位符,比如localhost:8080/path1/1:

3.3.4 完整CURD

这里是一个完整的CRUD示例:

@RestController
@RequestMapping("/")
@CrossOrigin("http://localhost:3000")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CRUDController {
private final Service service; @PostMapping("save")
public boolean save(@RequestBody User user)
{
return service.save(user);
} @GetMapping("delete")
public boolean delete(@RequestParam String id)
{
return service.delete(id);
} @GetMapping("select")
public User select(@RequestParam String id)
{
return service.select(id);
} @GetMapping("selectAll")
public List<User> selectAll()
{
return service.selectAll();
} }

注解基本上都在上面说过了,除了下面两个:

  • @RequiredArgsConstrutcor:这个是Lombok的注解,用来消除直接使用@Autowired出现的警告
  • @CrossOrgin:跨域注解,由于笔者使用Postwoman测试,默认运行端口为3000,因此需要加上该注解,使用Postman测试则不需要

3.4 Service层

@org.springframework.stereotype.Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class Service {
private final UserMapper mapper; public boolean save(User user)
{
String id = user.getId();
User currentUser = select(id);
if(currentUser != null)
return mapper.update(user) == 1;
return mapper.insert(user) == 1;
} public boolean delete(String id)
{
return mapper.deleteById(id) == 1;
} public User select(String id)
{
return mapper.selectById(id);
} public List<User> selectAll()
{
return mapper.selectAll();
}
}

简单的CRUD,调用持久层的方法。

3.5 Dao层

由于使用MyBatis,这里的Dao层只有一个Mapper:

@Mapper
@Component
public interface UserMapper{
@Select("select * from user where id=#{id}")
User selectById(@Param("id") String id); @Select("select * from user")
List<User> selectAll(); int insert(@Param("user") User user); int deleteById(@Param("id") String id); int update(@Param("user") User user);
}

select的sql直接写在了上面,剩下的sql语句写在了xml配置文件,另外@Mapper注解表示在编译后生成对应的接口实现类。

3.6 实体类

@Data
@AllArgsConstructor
public class User {
private String id;
private String username;
private String password;
@Override
public String toString()
{
return "id:"+id+"\n"+"username"+username+"\npassword"+password+"\n";
}
}

3.7 配置类

@Configuration
@MapperScan("com.example.demo.dao")
public class MyBatisConfig {
}
  • @Configuration:定义为配置类
  • @MapperScan:@Mapper的扫描路径

3.8 配置文件

配置文件常用的有properties以及yaml,yaml格式更加简单,这里使用yaml格式:

spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test
username: test
password: test mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:mappers/*.xml

分别指定数据库链接,数据库用户名以及密码,还有下划线转驼峰命名以及mapper文件的位置。

另外还需要创建UserMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.UserMapper">
<insert id="insert">
INSERT INTO `user` (`id`,`username`,`password`)
VALUES (#{user.id},#{user.username},#{user.password})
</insert> <update id="update">
UPDATE `user` set `username`=#{user.username} , `password`=#{user.password} where id=#{user.id}
</update> <delete id="deleteById">
DELETE FROM `user` WHERE `id` = #{id}
</delete>
</mapper>

就单纯的sql语句。

另外需要准备建表以及建用户的sql:

CREATE DATABASE IF NOT EXISTS test;

CREATE USER IF NOT EXISTS 'test'@'localhost' IDENTIFIED BY 'test';

GRANT ALL ON test.* to 'test'@'localhost';

USE test;

CREATE TABLE user
(
id char(10) primary key ,
username varchar (30) not null,
password varchar (30) not null
);

测试数据:

USE test;
INSERT INTO user(id,username,password) values ('1','username1','password1'),('2','username2','password2');

最终配置文件如下:

4 其他准备

4.1 建库建表建用户

直接执行上面的脚本即可。

4.2 开启服务

使用相应命令开启数据库服务。

5 测试

5.1 单元测试

修改一下自带的测试类即可:

@SpringBootTest
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class DemoApplicationTests { private final Service service; @Test
void contextLoads() {
} @Test
void select()
{
System.out.println(service.select("1"));
} @Test
void selectAll()
{
service.selectAll().forEach(System.out::println);
} // @Test
// void delete()
// {
// service.delete("3");
// } @Test
void save()
{
service.save(new User("3","username3","password3"));
}
}

直接点击左边的按钮即可运行,测试通过图如下:

5.2 浏览器测试

由于没有做前端,这里就使用Postwoman模拟前端测试:

6 源码

Java版:

Kotlin版:

7 参考

Spring Boot demo系列(二):简单三层架构Web应用的更多相关文章

  1. Spring Boot进阶系列二

    上一篇文章,主要分析了怎么建立一个Restful web service,系列二主要创建一个H5静态页面使用ajax请求数据,功能主要有添加一本书,请求所有书并且按照Id降序排列,以及查看,删除一本书 ...

  2. Spring Boot demo系列(五):Docker部署

    2021.2.24 更新 1 概述 本文讲述了如何使用Docker部署Spring Boot应用,首先介绍了Docker的安装过程,接着介绍了Docker的一些基础知识,最后讲述了Dockerfile ...

  3. Spring Boot demo系列(四):Spring Web+Validation

    2021.2.24 更新 1 概述 本文主要讲述了如何使用Hibernate Validator以及@Valid/@Validate注解. 2 校验 对于一个普通的Spring Boot应用,经常可以 ...

  4. Spring Boot demo系列(十):Redis缓存

    1 概述 本文演示了如何在Spring Boot中将Redis作为缓存使用,具体的内容包括: 环境搭建 项目搭建 测试 2 环境 Redis MySQL MyBatis Plus 3 Redis安装 ...

  5. Spring Boot demo系列(九):Jasypt

    2021.2.24 更新 1 概述 Jasypt是一个加密库,Github上有一个集成了Jasypt的Spring Boot库,叫jasypt-spring-boot,本文演示了如何使用该库对配置文件 ...

  6. Spring Boot demo系列(六):HTTPS

    2021.2.24 更新 1 概述 本文演示了如何给Spring Boot应用加上HTTPS的过程. 2 证书 虽然证书能自己生成,使用JDK自带的keytool即可,但是生产环境是不可能使用自己生成 ...

  7. Spring Boot demo系列(一):Hello World

    2021.2.24 更新 1 新建工程 打开IDEA选择新建工程并选择Spring Initializer: 可以在Project JDK处选择JDK版本,下一步是选择包名,语言,构建工具以及打包工具 ...

  8. Spring Boot教程(二十一)开发Web应用(2)

    在完成配置之后,举一个简单的例子,在快速入门工程的基础上,举一个简单的示例来通过Thymeleaf渲染一个页面. @Controller public class HelloController { ...

  9. Spring Boot教程(二十四)Web应用的统一异常处理

    我们在做Web应用的时候,请求处理过程中发生错误是非常常见的情况.Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来 ...

随机推荐

  1. JVM性能调优经验总结

    本文转载自JVM性能调优经验总结 说明 调优是一个循序渐进的过程,必然需要经历多次迭代,最终才能换取一个较好的折中方案. 在JVM调优这个领域,没有任何一种调优方案是适用于所有应用场景的,同时,切勿极 ...

  2. css故障文字动画

    免费分享95套java实战项目,不仅有源码还有对应的开发视频,关注公众号『勾玉技术』回复"95"即可获取 首先给内容上hover和before, .glitch:hover:bef ...

  3. 解决异常: Execution default-cli of goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.7:generate failed: Cannot instantiate object of type tk.mybatis.mapper.generator.MapperPlugin -> [Help 1]

    mybatis-generator整合通用mapper使用generator插件生成model.mapper时报错: 产生以下错误:↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 解决办法: ...

  4. Spring @Transactional注解和ReentrantLock同步锁同时使用不能同步的问题

    结论:如果在service层的方法上同时使用事务和同步锁无法保证数据同步. 1 @Service 2 public class ServiceImpl{ 3 4 private static Lock ...

  5. Java基础语法:变量与常量

    一.命名规范 所有变量.常量.方法.类 都使用英文单词 命名,要见名知意. 所有变量.方法 的命名都使用小驼峰法 :首字母小写的驼峰命名法.例如:sampleText 类 的命名都使用大驼峰法 :首字 ...

  6. java的基础知识

    编写一个hello,world public  class  Hello{    public static void main(String[] arge){        System.out.p ...

  7. 聊一聊JVM

    JVM 什么是JVM? ​ JVM是java虚拟机的缩写,本质上是一个程序,能识别.class字节码文件(.java文件编译后产生的二进制代码),并且能够解析它的指令,最终调用操作系统上的函数,完成我 ...

  8. 【MaixPy3文档】写好 Python 代码!

    本文是给有一点 Python 基础但还想进一步深入的同学,有经验的开发者建议跳过. 前言 上文讲述了如何认识开源项目和一些编程方法的介绍,这节主要来说说 Python 代码怎么写的一些演化过程和可以如 ...

  9. mysql 使用sleep操作 update 来停止一段时间执行语句 [骚操作]

    update mytestTable inner join(select '2' as id, sleep(5)) a on mytestTable.id=a.id set mytestTable.n ...

  10. Springboot 轻量替代框架 Solon 1.3.10 发布

    Solon 是一个微型的Java开发框架.项目从2018年启动以来,参考过大量前人作品:历时两年,4000多次的commit:内核保持0.1m的身材,超高的跑分,良好的使用体验.支持:RPC.REST ...