分层解耦
上一次介绍了JavaWeb中请求响应相关的内容,这次来了解一下JavaWeb中分层解耦的思想及其实现,重点在于理解控制反转(IOC)和依赖注入(DI)。
一、三层架构
1、概述
1.1 controller(接收请求、响应数据)
控制层,接收前端发送的请求,对请求进行处理,并响应数据。
1.2 service(逻辑处理)
业务逻辑层,处理具体的业务逻辑。
1.3 dao(数据访问)
数据访问层(Data Access Object)(持久层),负责数据访问操作,包括数据的增、删、改、查。
1.4 对比
以前的方式与三层架构方式对比。
二、分层解耦
1、 基本概念
1)内聚:软件中各个功能模块内部的功能联系。
2)耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
2、问题导入
探讨传统的MVC模式,三层架构代码书写存在的问题。我们先看一个代码示例,再对问题进行剖析。
注:MVC模式是软件工程中常见的一种软件架构模式,该模式把软件系统(项目)分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
1)Dao层(dao包下接口和对应实现类)
1.1)BookDao接口(com.itweb.dao.BookDao)
1 | package com.itweb.dao; |
1.2)BookDaoImpl实现类(com.itweb.dao.impl.BookDaoImpl)
1 | package com.itweb.dao.impl; |
2)Service层(service包下接口和对应实现类)
2.1)BookService接口(com.itweb.service.BookService)
1 | package com.itweb.service; |
2.2)BookServiceImpl实现类(com.itweb.service.impl.BookServiceImpl)
1 | package com.itweb.service.impl; |
3)存在问题分析
3.1)在业务层(Service层)的接口实现类中调用数据层(Dao层)的接口实现类时,存在较高耦合性(即数据层实现类如果修改,则在业务层中调用的实现类也要修改,导致需要重新编译、测试、部署…),究其原因,是调用的实现类导致高耦合(凡是new出来的对象的调用都存在一定的耦合性)
3.2)解决办法
思路1:将new出来的实现类去掉,直接写接口,即将 private BookDao bookDao=new BookDaoImpl(); 改为 private BookDao bookDao; 但这样也是存在问题的,对象从哪里来呢?(IOC–>解耦)
思路2:在BookServiceImpl实现类中,业务层实现依赖 dao对象运行(因为在Service层中调用了dao对象的save方法),那么这种依赖关系该如何绑定呢?(依赖注入–>绑定)
综上,由此引出了两个重要话题:IOC(控制反转)和 DI(依赖注入)
3、IOC & DI概述
3.1 控制反转(IOC)
Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
Spring对 IOC思想进行了实现:Spring提供了一个容器,称为IOC容器(即Spring架构中的 Core Container),用来充当IOC思想中的外部。IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为 Bean(Spring中对象都称为Bean)
3.2 依赖注入(DI)
Dependency Injection,简称DI。容器为应用程序提供运行时所依赖的资源(即在容器中建立bean与bean之间的依赖关系的整个过程)称为依赖注入。
3.3 Bean对象
IOC容器中创建、管理的对象,称为bean。
3.4 小结:(Spring实现充分解耦过程)
- 1.使用 IOC容器管理 bean对象 (IOC)
- 2.在IOC容器内将有依赖关系的 bean进行关系绑定 (DI)
- 3.最终效果
- 使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
4、IOC & DI入门程序
同样以我们问题导入那个案例作为入门程序,在原基础上做一些修改即可。具体为:
1、在对应的实现类上方添加 @Component注解实现控制反转
2、在删掉new对象的上方添加 @Autowired注解实现依赖注入
1)Dao层(dao包下接口和对应实现类)
1.1)BookDao接口(com.itweb.dao.BookDao)
1 | package com.itweb.dao; |
1.2)BookDaoImpl实现类(com.itweb.dao.impl.BookDaoImpl)
1 | package com.itweb.dao.impl; |
2)Service层(service包下接口和对应实现类)
2.1)BookService接口(com.itweb.service.BookService)
1 | package com.itweb.service; |
2.2)BookServiceImpl实现类(com.itweb.service.impl.BookServiceImpl)
1 | package com.itweb.service.impl; |
5、IOC & DI详解
5.1 Bean的声明
要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一
注:Controller(请求处理、响应数据),Service(逻辑处理),Dao(数据访问)
声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
5.2 Bean组件扫描
前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。
注:@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。
@SpringBootApplication具有包扫描作用,默认扫描当前包及其子包
5.3 Bean注入
@Autowired注解,即自动装配,默认是按照类型进行,如果存在多个相同类型的bean,将会报出如下错误
通过以下几种方案来解决
1)**@Primary**
见名知意,该注解的作用即设置bean的优先级
1 |
|
2)**@Qualifier**
配合@Autowired一起使用
1 |
|
3)**@Resource**
指定名称,按名称注入
1 |
|
4)小结
1.依赖注入的注解
- @Autowired: 默认按照类型自动装配。
- 如果同类型的bean存在多个:
- @Primary
- @Autowired +@Qualifier(“bean的名称”)
- @Resource(name=”bean的名称”)
2.@Resource 与@Autowired区别(重点–面试题)
- @Autowired 是spring框架提供的注解而@Resource是JDK提供的注解。
- @Autowired默认是按照类型注入,而 @Resource默认是按照名称注入。