1. 项目背景与核心架构解析
这个JavaWeb作业的核心在于实现经典的三层架构设计,并通过IOC/DI机制解耦各层组件。作为企业级开发的黄金标准,三层架构将业务逻辑清晰地划分为:
- 表现层(Controller):处理HTTP请求与响应
- 业务层(Service):封装核心业务逻辑
- 持久层(DAO):负责数据持久化操作
关键设计原则:上层只能调用直接下层,禁止跨层调用。例如Controller只能访问Service,不能直接操作DAO。
2. 三层架构具体实现
2.1 Controller层实现要点
java复制@Controller
@RequestMapping("/user")
public class UserController {
@Autowired // DI注入
private UserService userService;
@GetMapping("/{id}")
public String getUser(@PathVariable Integer id, Model model) {
User user = userService.getById(id);
model.addAttribute("user", user);
return "userDetail";
}
}
- 使用
@Controller注解标记为控制层组件 - 通过
@RequestMapping定义基础路径 - 方法参数支持自动绑定请求参数(
@RequestParam)、路径变量(@PathVariable)等 - 返回字符串默认对应视图名称,也可用
@ResponseBody返回JSON
2.2 Service层最佳实践
java复制@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Transactional // 事务控制
@Override
public User getById(Integer id) {
// 业务逻辑校验
if(id == null || id <= 0) {
throw new IllegalArgumentException("ID不合法");
}
return userDao.selectById(id);
}
}
@Service注解标识业务组件- 建议面向接口编程(Impl实现接口)
- 事务控制应放在Service层(
@Transactional) - 业务异常建议使用自定义异常体系
2.3 DAO层数据访问
java复制@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public User selectById(Integer id) {
String sql = "SELECT * FROM t_user WHERE id=?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
}
}
@Repository注解标识持久层组件- 可使用JdbcTemplate、MyBatis等持久化方案
- SQL建议集中管理(如放在XML配置中)
3. IOC与DI解耦实现
3.1 配置Spring容器
xml复制<!-- applicationContext.xml -->
<context:component-scan base-package="com.example"/>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<!-- 数据源配置 -->
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
3.2 依赖注入方式对比
| 注入方式 | 实现示例 | 适用场景 |
|---|---|---|
| 构造器注入 | @Autowired构造函数 |
强依赖、不可变依赖 |
| Setter注入 | @Autowiredsetter方法 |
可选依赖 |
| 字段注入 | @Autowired字段 |
简单场景(最常用) |
推荐使用构造器注入(结合
@RequiredArgsConstructor),避免循环依赖问题
4. 常见问题解决方案
4.1 组件扫描失效排查
- 检查
@ComponentScan或XML配置的base-package是否正确 - 确认类是否在扫描路径下
- 查看是否有异常被静默处理(可开启DEBUG日志)
4.2 事务不生效场景
- 方法非public修饰
- 同类方法自调用(绕过代理)
- 异常类型非RuntimeException且未配置rollbackFor
- 数据库引擎不支持事务(如MyISAM)
4.3 性能优化建议
- DAO层:
- 使用连接池(HikariCP)
- 批量操作代替循环单条操作
- Service层:
- 避免在事务方法中进行远程调用
- 合理设置事务隔离级别
- Controller层:
- 使用DTO代替直接暴露实体类
- 添加缓存控制(
@Cacheable)
5. 项目结构规范
code复制src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── controller/
│ │ ├── service/
│ │ │ ├── impl/
│ │ ├── dao/
│ │ ├── entity/
│ │ └── config/
│ └── resources/
│ ├── mapper/
│ └── application.properties
└── test/ # 测试代码
- 实体类放在entity包
- 接口与实现分离(service/impl)
- 资源文件按类型分类
- 测试代码保持相同包结构
6. 单元测试要点
6.1 Controller层测试
java复制@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
when(userService.getById(1)).thenReturn(new User(1, "test"));
mockMvc.perform(get("/user/1"))
.andExpect(status().isOk())
.andExpect(view().name("userDetail"));
}
}
6.2 Service层测试
java复制@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserDao userDao;
@InjectMocks
private UserServiceImpl userService;
@Test
void testGetUserWithInvalidId() {
assertThrows(IllegalArgumentException.class,
() -> userService.getById(-1));
}
}
7. 进阶优化方向
- 使用Spring Boot简化配置:
- 自动配置数据源
- 内嵌容器部署
- 引入AOP实现:
- 统一日志处理
- 权限校验
- 性能监控
- 接口文档化:
- Swagger集成
- 生成在线API文档
实际开发中,建议结合Lombok减少样板代码,使用MapStruct简化DTO转换,这些都能显著提升开发效率。三层架构看似简单,但要写出易于维护的代码,关键是要严格遵守各层的职责边界。
