1. 程式人生 > 實用技巧 >Spring Boot demo系列(二):簡單三層架構Web應用

Spring Boot demo系列(二):簡單三層架構Web應用

1 概述

這是Spring Boot的第二個Demo,一個只有三層架構的極簡Web應用,持久層使用的是MyBatis

2 架構

一個最簡單的Spring Boot Web應用分為三層:

  • Controller層:負責具體業務流程的控制,呼叫Service層來控制業務邏輯
  • Service層:業務邏輯層,真正執行業務的操作,比如獲取使用者資訊等
  • Dao層:負責資料持久化,在這一層中與各種資料庫,比如MySQLOracle等打交道

先來簡單說一下三層所使用到的註解。

2.1 Controller

Controller層也是入口層,一般涉及如下註解:

  • @Controller@Controller
    是經典的Controller層註解,@Controller標識的類代表該類是控制器類
  • @RequestMapping:使用@RequestMapping可以對請求進行對映,可以註解在類上或者方法上,在類上的話表示該類所有的方法都是以該地址作為父地址,在方法上就表示可以對映對應的請求到該方法上
  • @GetMapping/@PostMapping:這兩者實際上是@RequestMapping對應不同方法的簡化版,因為@RequestMapping有一個method屬性,如果該method指定為GET那麼就相當於@GetMapping,如果指定為POST就相當於@PostMapping
  • @ResponseBody
    :作用在方法上,將返回的資料進行可能的轉換(取決於請求頭,轉換為JSONXML等等,預設的情況下比如單純字串就直接返回),比如返回語句為return "success";,如果加上了@ResponseBody就直接返回success,如果不加上就會跳轉到success.jsp頁面
  • @RequestParm:處理Contrent-Typeapplication/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";
    }
}

執行之後,如果出現如下錯誤:

這是因為沒有配置資料來源,可以先把MySQLMyBatis的依賴刪去:

執行之後在瀏覽器輸入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);
}

selectsql直接寫在了上面,剩下的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以及yamlyaml格式更加簡單,這裡使用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 參考