JavaWeb从0到1-DAY9-三层架构、IOC 与 DI

三层架构、IOC 与 DI 学习笔记

一、这一章在讲什么?

这一章主要讲 Spring Web 项目中为什么要把代码拆成 Controller / Service / Dao 三层。原来所有代码都写在 Controller 中,会导致职责混乱,不利于复用、扩展和维护。拆分三层之后,每一层只负责自己的事情,符合单一职责原则。后面进一步引出 IOC 和 DI,用 Spring 容器管理对象、注入依赖,降低类和类之间的耦合。

二、核心概念

1. 单一职责原则

  • 它是什么?
    一个类或一个方法尽量只负责一类事情。
  • 有什么作用?
    让代码职责清晰,后期修改时不容易牵一发动全身。
  • 原理是什么?
    把不同类型的功能拆到不同层或不同类中,例如请求处理、业务处理、数据访问分开写。
  • 初学者容易混淆的点
    拆分不是为了让代码变多,而是为了让每段代码更清楚、更容易维护。

2. 三层架构

  • 它是什么?
    把 Web 项目代码分成 ControllerServiceDao 三层。
  • 有什么作用?
    提高代码的扩展性、复用性和可维护性。
  • 原理是什么?
    请求先进入 ControllerController 调用 ServiceService 调用 Dao 获取数据,最后返回结果。
  • 初学者容易混淆的点
    Controller 不负责全部代码,Service 不直接等于数据库层,Dao 不处理业务逻辑。

3. Controller

  • 它是什么?
    控制层。
  • 有什么作用?
    接收前端请求,调用业务层,并响应数据。
  • 原理是什么?
    通过 @RequestMapping 等注解接收请求,然后调用 Service 方法拿到结果。
  • 初学者容易混淆的点
    Controller 不是写所有逻辑的地方,它主要负责“接请求、调业务、回数据”。

4. Service

  • 它是什么?
    业务逻辑层。
  • 有什么作用?
    处理具体业务逻辑,例如把字符串数据解析成 User 对象集合。
  • 原理是什么?
    Service 通常调用 Dao 获取原始数据,再进行业务处理。
  • 初学者容易混淆的点
    Service 不是单纯读取数据,它更关注业务规则和处理流程。

5. Dao

  • 它是什么?
    数据访问层,DaoData Access Object 的缩写。
  • 有什么作用?
    负责数据访问操作,比如读取文件、查询数据库、增删改查。
  • 原理是什么?
    把和数据来源有关的代码集中到 Dao 中,其他层不直接关心数据从哪里来。
  • 初学者容易混淆的点
    Dao 只负责拿数据,不负责复杂业务判断。

6. 高内聚、低耦合

  • 它是什么?
    高内聚:一个模块内部功能联系紧密。低耦合:模块之间依赖程度低。
  • 有什么作用?
    让程序更容易扩展和替换实现。
  • 原理是什么?
    每层只依赖自己需要的接口或对象,不把具体实现写死。
  • 初学者容易混淆的点
    三层架构只是第一步,如果代码里还直接 new 具体实现类,耦合仍然比较高。

7. IOC

  • 它是什么?
    IOC 是 Inversion Of Control,叫控制反转。
  • 有什么作用?
    把对象创建和管理交给 Spring IOC 容器,减少类之间的直接依赖。
  • 原理是什么?
    原来对象由程序员在类中 new 出来,现在由容器统一创建、管理和提供。
  • 初学者容易混淆的点
    IOC 不是不创建对象,而是对象不再由当前类自己创建,而是交给容器创建。

8. Bean

  • 它是什么?
    被 IOC 容器创建和管理的对象,称为 Bean。
  • 有什么作用?
    让 Spring 能统一管理对象,并在需要时完成依赖注入。
  • 原理是什么?
    在类上加声明 Bean 的注解,Spring 扫描到后会创建并放入 IOC 容器。
  • 初学者容易混淆的点
    不是所有 Java 对象都叫 Bean,只有被 IOC 容器管理的对象才是 Bean。

9. DI

  • 它是什么?
    DI 是 Dependency Injection,叫依赖注入。
  • 有什么作用?
    当一个类需要另一个对象时,由容器把依赖对象注入进来。
  • 原理是什么?
    例如 Controller 需要 UserService,Spring 就从容器中找到对应 Bean 并赋值给它。
  • 初学者容易混淆的点
    DI 不是“控制注入”,而是“依赖注入”。IOC 负责把对象交给容器管理,DI 负责把容器里的对象注入给需要它的类。

10. 声明 Bean 的注解

  • 它是什么?
    常见有 @Component@Controller@Service@Repository
  • 有什么作用?
    告诉 Spring:这个类需要交给 IOC 容器管理。
  • 原理是什么?
    Spring Boot 默认扫描启动类所在包及其子包,被扫描到的 Bean 注解才会生效。
  • 初学者容易混淆的点
    @Component 最通用;@Controller 用于控制层;@Service 用于业务层;@Repository 用于数据访问层。

三、重难点

1. 为什么要拆三层?

结论:为了让代码职责更清楚,提高扩展性和复用性。
原因:如果所有代码都写在 Controller 中,请求处理、业务逻辑、数据访问会混在一起,后期改一个功能可能影响一大片。
比喻:像餐厅分工,前台接待顾客,厨师做菜,采购负责食材。如果一个人全干,事情一多就乱。

2. IOC 中“控制反转”到底反转了什么?

结论:反转的是对象创建和管理的控制权。
原因:以前类中自己 new 对象,现在交给 Spring IOC 容器创建和管理。
比喻:以前你自己买菜做饭,现在把做饭交给食堂,你需要饭时直接去取。

3. IOC 和 DI 的关系

结论:IOC 是思想,DI 是实现这种思想的方式之一。
原因:IOC 让容器管理对象,DI 让容器把对象注入给需要它的类。
例子:UserController 不自己创建 UserServiceImpl,而是通过 @Autowired 让容器注入 UserService

4. 多个同类型 Bean 为什么会报错?

结论:因为 @Autowired 默认按照类型注入,同类型 Bean 有多个时,Spring 不知道选哪个。
原因:比如 UserServiceUserServiceImplUserServiceImpl2 两个实现类,类型都匹配。
比喻:你说“给我一个杯子”,桌上有两个杯子,别人不知道你要哪一个,所以需要你指定。

四、代码理解

1. 三层调用关系

@RestController
public class UserController {
    @Autowired
    private UserService userService; // 注入业务层对象

    @RequestMapping("/list")
    public List<User> list() {
        // 调用 service 查询用户信息
        List<User> userList = userService.list();
        return userList; // 响应数据
    }
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao; // 注入数据访问层对象

    @Override
    public List<User> list() {
        // 调用 dao 获取原始数据
        List<String> lines = userDao.list();

        // 把字符串数据解析成 User 对象集合
        return lines.stream().map(line -> {
            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts[0]);
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(
                    parts[5],
                    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
            );
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
    }
}
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public List<String> list() {
        // 读取 user.txt 文件
        InputStream in = this.getClass()
                .getClassLoader()
                .getResourceAsStream("user.txt");

        return IOUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
    }
}

语法规则总结:

  • @RestController:声明控制层 Bean,同时返回数据。
  • @Service:声明业务层 Bean。
  • @Repository:声明数据访问层 Bean。
  • @Autowired:从 IOC 容器中找到需要的 Bean,并注入进来。
  • Controller -> Service -> Dao:请求处理的基本调用顺序。

2. 依赖注入的三种方式

@Autowired
private UserService userService;

属性注入:写法简单,但依赖关系不够明显。

private final UserService userService;

@Autowired
public UserController(UserService userService) {
    this.userService = userService;
}

构造函数注入:依赖关系清晰,安全性更高。如果只有一个构造函数,@Autowired 可以省略。

private UserService userService;

@Autowired
public void setUserService(UserService userService) {
    this.userService = userService;
}

Setter 注入:依赖关系清晰,但需要多写 setter 方法。

3. 多 Bean 冲突的解决方式

@Primary
@Service
public class UserServiceImpl implements UserService {
}

@Primary:多个同类型 Bean 中,优先选择这个。

@Autowired
@Qualifier("userServiceImpl")
private UserService userService;

@Qualifier:配合 @Autowired,按名称指定 Bean。

@Resource(name = "userServiceImpl")
private UserService userService;

@Resource:按名称指定 Bean。它是 Java EE 提供的注解,不是 Spring 提供的。

五、易错点

  1. 以为三层架构只是把代码拆开,忘了核心目的是单一职责和降低维护成本。
  2. Controller 当成万能层,在里面写业务逻辑和数据访问代码。
  3. 把 IOC 理解成“不创建对象”,其实是对象由容器创建和管理。
  4. 把 DI 说成“控制注入”,正确说法是“依赖注入”。
  5. 忘记 @Autowired 默认按类型注入,同类型 Bean 有多个时会报错。

六、记忆口诀 / 通俗比喻

  • Controller 管请求,Service 管业务,Dao 管数据。
  • 三层架构治“职责混乱”,IOC / DI 治“对象写死”。
  • IOC:对象交给容器管。DI:容器把对象送过来。
  • @Autowired 默认按类型找,多个 Bean 要指定。
  • @Primary 定优先级,@Qualifier 指名字,@Resource 默认按名称。

七、应用

在实际 Spring Boot 项目中,通常会把控制器、业务逻辑、数据访问分开放在不同包中。例如用户列表查询功能:UserController 接收 /list 请求,调用 UserService 获取用户列表,UserServiceImpl 处理业务逻辑,UserDaoImpl 负责读取文件或数据库。

这样做的好处是,如果以后数据来源从 user.txt 换成数据库,通常只需要改 Dao 层;如果业务规则变了,主要改 Service 层;如果接口路径或响应格式变了,主要改 Controller 层。每一层各管各的,项目就更容易维护。

八、最终总结

本章核心是从三层架构理解 Spring 项目的分工:Controller 接收请求并响应数据,Service 处理业务逻辑,Dao 负责数据访问。三层架构体现了单一职责原则,也让代码更容易扩展和复用。IOC 把对象创建和管理交给容器,DI 让容器把依赖对象注入进来。@Autowired 默认按类型注入,如果同类型 Bean 有多个,可以用 @Primary@Qualifier@Resource 解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值