软件工程第六周之服务层与API调用

服务层(常见)

如前所述,服务层主要负责处理业务逻辑。在这个例子中,我们会有一个BookService来处理与图书相关的操作。

java 复制代码
// BookService.java
public class BookService {
    private BookDAO bookDAO;  // Assume this DAO uses JDBC or MyBatis

    public BookService(BookDAO bookDAO) {
        this.bookDAO = bookDAO;
    }

    public void addBook(Book book) {
        bookDAO.insert(book);
    }

    public Book getBookById(int id) {
        return bookDAO.findById(id);
    }

    public List<Book> getAllBooks() {
        return bookDAO.findAll();
    }
}

API调用(常见)

API调用通常在一个控制器或端点中进行 。例如,假设我们使用一个简单的Spring Boot应用程序,我们可以有一个BookController,该控制器暴露出API端点供前端或其他服务调用。

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @PostMapping
    public void addBook(@RequestBody Book book) {
        bookService.addBook(book);
    }

    @GetMapping("/{id}")
    public Book getBookById(@PathVariable int id) {
        return bookService.getBookById(id);
    }

    @GetMapping
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }
}

(看来这个@Autowired不仅可以让springboot自动注入mapper代理对象,还可以自动注入其他类对象,比如服务层实例对象)

在上述例子中,BookController提供了3个API端点

  • POST /books 添加新书。
  • GET /books/{id} 通过ID获取图书信息。
  • GET /books 获取所有图书的列表。

这个BookController通过Springboot的依赖注入将BookService注入进来,因此当API被调用时,它会利用BookService去执行相应的操作。

原来如此!服务层(在本例中为BookService)处理业务逻辑和数据操作,而API层(在本例中为BookController)负责与外部交互,接收请求并返回响应。


Dao层基础上的服务层和API层

当使用JDBC或MyBatis这种Dao层时,核心的差异主要体现在数据访问对象(Dao)或映射器(Mapper)的实现上。以下是如何使用JDBC和MyBatis实现的概述,以及与BookServiceBookController的集成。

1. 使用JDBC

首先,我们需要创建一个基于JDBC的BookDAO

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class JdbcBookDAO {

    private String url = "your_database_url";
    private String username = "your_username";
    private String password = "your_password";

    public void insert(Book book) {
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            PreparedStatement stmt = connection.prepareStatement("INSERT INTO books (title, author) VALUES (?, ?)");
            stmt.setString(1, book.getTitle());
            stmt.setString(2, book.getAuthor());
            stmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Book findById(int id) {
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            PreparedStatement stmt = connection.prepareStatement("SELECT id, title, author FROM books WHERE id = ?");
            stmt.setInt(1, id);
            ResultSet rs = stmt.executeQuery();
            if (rs.next()) {
                return new Book(rs.getInt("id"), rs.getString("title"), rs.getString("author"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<Book> findAll() {
        List<Book> books = new ArrayList<>();
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            PreparedStatement stmt = connection.prepareStatement("SELECT id, title, author FROM books");
            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                books.add(new Book(rs.getInt("id"), rs.getString("title"), rs.getString("author")));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return books;
    }
}

2. 使用MyBatis

首先,定义BookMapper接口:

java 复制代码
public interface BookMapper {
    void insert(Book book);
    Book findById(@Param("id") int id);
    List<Book> findAll();
}

然后,创建BookMapper.xml映射文件:

java 复制代码
<mapper namespace="com.example.BookMapper">
    <insert id="insert" parameterType="Book">
        INSERT INTO books (title, author) VALUES (#{title}, #{author})
    </insert>

    <select id="findById" resultType="Book" parameterType="int">
        SELECT id, title, author FROM books WHERE id = #{id}
    </select>

    <select id="findAll" resultType="Book">
        SELECT id, title, author FROM books
    </select>
</mapper>

3. Dao层与服务层和API层集成

不论选择JDBC还是MyBatis,都可以与之前定义的BookServiceBookController集成。**只需在服务层中更换相应的DAO或Mapper即可。**也就是说,原本如果没用到数据库,那一般会在本地写明Service层的业务逻辑和那些insert函数。而Dao层本质就是对数据的操作,所以,在原本服务层上使用Dao层中定义的函数即可!

例如,如果选择使用MyBatis的BookMapperBookService可能看起来像这样:

java 复制代码
@Service
public class BookService {
    @Autowired
    private BookMapper bookMapper;

    public void addBook(Book book) {
        bookMapper.insert(book);
    }

    public Book getBookById(int id) {
        return bookMapper.findById(id);
    }

    public List<Book> getAllBooks() {
        return bookMapper.findAll();
    }
}

至于BookController,它保持不变。它与BookService进行交互,而不关心背后的数据访问细节是基于JDBC还是MyBatis的。

作者之前直接就是定义mapper接口之后直接在API层也就是controller中使用方法了,所以有一个疑问:简单系统的Service层有必要吗?

其实上面两段都涉及到Service层的好处!我们来细细研究一下:

有许多简单的应用或项目可能会直接在Controller层使用Mapper,特别是当业务逻辑相对简单时。但Service层的存在并不是无的放矢,它是基于软件工程中的一些核心原则和实践。

以下是在图书管理系统或任何应用中使用Service层的几个主要原因:

1. 分层和关注点分离 (Separation of Concerns):

Service层和Controller层服务于不同的目的。Controller主要负责处理HTTP请求和响应,而Service层主要处理业务逻辑。当你将这两者分开,代码更加模块化,各层只关注自己的职责,这使得代码更加清晰、可维护。

2. 复用性:

当业务逻辑被封装在Service层中,它可以被多个Controller或其他服务重用,而不是被固定在特定的Controller中。只不过对于图书管理系统来说,其业务逻辑不复杂,所以才看似复用只是调用insert/update函数而已。

3. 灵活性:

随着项目的增长,可能会有更复杂的业务逻辑需求,例如事务处理、缓存、安全等。有了Service层,可以更容易地在一个集中的地方添加这些逻辑

4. 测试性:

业务逻辑封装在Service层中使得单元测试更为简单。你可以针对Service层进行独立的测试,而不需要涉及HTTP层。这样可以更容易地进行模拟和测试各种业务场景。

5. 解耦:

如果在未来决定更换数据访问层(例如,从MyBatis迁移到JPA),有了Service层,更改的影响范围会小得多,因为Controller并不直接依赖于具体的数据访问技术。

6. 业务逻辑的集中化:

随着时间的推移,一个简单的图书管理系统可能会添加更多的功能,例如推荐系统、库存管理、通知等。所有这些复杂的逻辑可以在Service层中处理,而Controller层保持简单和清晰。

从软件工程的角度来看,分层的架构模式,如MVC或三层架构,被设计出来是为了支持可维护性、扩展性和可重用性。虽然在小型或简单的项目中,我们可能觉得Service层是多余的,但随着项目规模的扩大或需求的变更,Service层的价值将逐渐显现。

总之,虽然直接在Controller中使用Mapper或DAO在某些情况下可能是可行的,但建议在设计软件时考虑长远和整体,以支持未来的扩展和维护。

相关推荐
葫芦和十三2 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp3 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑4 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯4 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan7 小时前
多Agent之间的区别
后端
青石路8 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充9 小时前
1.面向对象设计思想
后端
IT_陈寒9 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro9 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗10 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端