服务层(常见)
如前所述,服务层主要负责处理业务逻辑。在这个例子中,我们会有一个
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实现的概述,以及与
BookService
和BookController
的集成。
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,都可以与之前定义的BookService
和BookController
集成。**只需在服务层中更换相应的DAO或Mapper即可。**也就是说,原本如果没用到数据库,那一般会在本地写明Service层的业务逻辑和那些insert函数。而Dao层本质就是对数据的操作,所以,在原本服务层上使用Dao层中定义的函数即可!
例如,如果选择使用MyBatis的BookMapper
,BookService
可能看起来像这样:
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在某些情况下可能是可行的,但建议在设计软件时考虑长远和整体,以支持未来的扩展和维护。