目录
4.1.2. 服务接口层 (Service Interface)
4.1.3. 服务层(Service Implementation)
为了从零开始实现一个高效的API接口,下面将通过Spring Cloud构建一个简单的用户服务作为示例。这个服务将包括获取用户详情的API。将逐步介绍控制层、服务层、服务接口层、数据访问层等层面的具体实现方式及作用,同时利用JUnit进行单元测试。
1. 添加POM依赖
首先,在`pom.xml`中添加必要的Spring Boot和Spring Cloud等依赖。下面是一个基本的示例:
<dependencies>
<!-- Spring Cloud 相关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- Spring Boot 和 Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot 测试相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Data JPA启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Lombok库,减少样板代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot 测试启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 构建目录结构
创建以下目录结构来组织代码:
src/
|- main/
|- java/
|- com/
|- example/
|- api/
|- Application.java
|- controller/ // 控制层
|- service/ // 服务层
|- service/impl/ // 服务接口实现层
|- repository/ // 数据访问层(Mapper或Repository)
|- entity/ // 实体类
|- dto/ // 数据传输对象
|- resources/
|- application.yml
|- mapper/ // MyBatis的Mapper XML文件存放目录
|- test/
|- java/
|- com/
|- example/
|- api/
|- controller/
|- service/
|- mapper/
3. 构建文件
在`application.yml`中配置应用程序参数,比如服务端口、数据库连接和自定义参数。
server:
port: 8080
spring:
application:
name: spring-cloud-api-service
datasource:
url: jdbc:mysql://localhost:3306/db_example?serverTimezone=UTC&useSSL=false
username: db_user
password: db_password
driverClassName: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:/mybatis/*.xml
type-aliases-package: com.example.api.entity
jpa:
show-sql: true
hibernate:
ddl-auto: update
4. 实现API接口
现在我们来具体实现API接口,分层介绍每一部分。
- 控制层 (Controller): 处理外部HTTP请求,将请求转发给服务层。
- 服务接口层 (Service Interface): 定义服务层的接口。
- 服务层 (Service Implementation): 实现服务接口层定义的接口,包含业务逻辑。
- 数据访问层 (Mapper & Mapper XML): 与数据库交互,执行CRUD操作。这里可使用MyBatis或Spring Data JPA等框架来实现。如果是使用MyBatis,你会有Mapper接口和对应的XML文件。
4.1. MyBatis
4.1.1. 控制层(Controller)
控制层负责处理外部请求,并将请求转发到服务层。
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable("id") Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
}
4.1.2. 服务接口层 (Service Interface)
服务接口层定义了服务层需要实现的方法。它是一种契约,规定了可供控制层调用的业务逻辑方法。
public interface UserService {
User getUserById(Long id);
}
4.1.3. 服务层(Service Implementation)
服务层实现了服务接口层定义的方法。这里包含了具体的业务逻辑,如调用Mapper层与数据库交互。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Long id) {
return userMapper.selectByPrimaryKey(id);
}
}
4.1.4. 实体类(Entity)
数据传输对象用于服务层和控制层之间的数据传递。
public class User {
private Long id;
private String name;
// getter和setter省略
}
4.1.5. 数据访问层(Mapper)
数据访问层使用MyBatis Mapper接口与数据库进行交互。它定义了一些用于数据库操作的方法,这些方法的实现由MyBatis框架在运行时自动完成。
@Mapper
public interface UserMapper {
User selectByPrimaryKey(Long id);
}
MyBatis的Mapper XML文件定义了Mapper接口方法的SQL语句。对应的`UserMapper.xml`:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.api.mapper.UserMapper">
<select id="selectByPrimaryKey" resultType="com.example.api.entity.User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
4.1.6. JUnit单元测试
Controller层测试
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void getUserByIdTest() throws Exception {
UserDTO userDTO = new UserDTO();
userDTO.setId(1L);
userDTO.setName("John Doe");
given(userService.getUserById(1L)).willReturn(userDTO);
mockMvc.perform(MockMvcRequestBuilders.get("/users/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John Doe"));
}
}
Mapper层测试
@DataJpaTest
@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void selectByPrimaryKeyTest() {
// 这里需要添加测试数据,或者使用一个内存数据库如H2
User user = userMapper.selectByPrimaryKey(1L);
assertNotNull(user);
assertEquals("John Doe", user.getName());
}
}
4.2. Spring Data JPA
4.2.1. 控制层(Controller)
控制层负责处理外部请求,并将请求转发到服务层。
@RestController
@RequestMapping("/users")
@AllArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
}
4.2.2. 服务层(Service)
服务层是业务逻辑的中心层,它从控制器接收命令,并利用数据访问层来实现这些命令。
@Service
@AllArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
}
}
4.2.3. 实体类(Entity)
实体类是数据库表的映射。
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 其他字段...
}
4.2.4. 数据访问层(Repository)
Spring Data JPA的Repository负责数据持久化操作。
public interface UserRepository extends JpaRepository<User, Long> {
}
4.2.5. JUnit单元测试
Controller层测试
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void getUserByIdShouldReturnUser() throws Exception {
User user = new User(1L, "John Doe");
Mockito.when(userService.findById(1L)).thenReturn(user);
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value(user.getName()));
}
}
Service层测试
@ExtendWith(SpringExtension.class)
@DataJpaTest
public class UserServiceTest {
@Autowired
private UserRepository userRepository;
private UserService userService;
@BeforeEach
public void setUp() {
userService = new UserService(userRepository);
}
@Test
public void findByIdShouldReturnUser() {
User savedUser = userRepository.save(new User(1L, "John Doe"));
User foundUser = userService.findById(savedUser.getId());
assertThat(foundUser).isEqualTo(savedUser);
}
}
5. 总结
通过这种分层架构,可以清晰地组织代码,实现关注点分离,提高代码的可维护性和可扩展性。每一层的作用已经被清晰定义,并且示例代码为您提供了具体的实施方案。Junit测试确保了代码的正确性和稳定性。在实际的开发环境中,这个基础架构需要根据具体的需求进行适当的扩展和调整。还需要考虑错误处理、日志记录、安全性和其他高级特性。