软件工程第六周之服务层与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在某些情况下可能是可行的,但建议在设计软件时考虑长远和整体,以支持未来的扩展和维护。

相关推荐
奋斗的小花生10 分钟前
c++ 多态性
开发语言·c++
魔道不误砍柴功12 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_23412 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨15 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程43 分钟前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉