java开发三层架构下的分层解耦
引例:
我们都听说过软件开发设计原则高内聚低耦合。
高内聚:指的是一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 "高内聚"。
低耦合:指的是软件中各个层、模块之间的依赖关联程序越低越好。
回顾一下之前提过javaweb开发的三层架构:
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
Service:业务逻辑层。处理具体的业务逻辑。
Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
看一下在如下这个例子中:
问:上面这个案例中控制层调用业务逻辑层是通过new一个业务逻辑层对象.思考一下,这样写会有什么问题?
解:如果说我们需要更换实现类,比如由于业务的变更,UserServiceImpl 不能满足现有的业务需求,我们需要切换为 UserServiceImpl2 这套实现,就需要修改Contorller的代码,需要创建 UserServiceImpl2 的实现new UserServiceImpl2()
.我们想做的是让三层架构分别工作,修改业务逻辑层的代码并不需要改动控制层的代码,现在这样明显是不行的,也就是说目前层与层之间是存在耦合的
为了解决上面的问题,我们的最终目标,是做到层与层之间,尽可能的降低耦合,甚至解除耦合。
解耦思路:
上个案例我们在编写代码时,需要什么对象,就直接new一个就可以了。 这种做法呢,层与层之间代码就耦合了,当service层的实现变了之后, 我们还需要修改controller层的代码。
那应该怎么解耦呢?
首先不能在EmpController中使用new对象。代码如下:
此时,就存在另一个问题了,不能new,就意味着没有业务层对象(程序运行就报错),怎么办呢?
我们的解决思路是:
提供一个容器,容器中存储一些对象(例:UserService对象)
Controller程序从容器中获取UserService类型的对象
1:将要用到的对象交给一个容器管理。
2应用程序中用到这个对象,就直接从容器中获取
我们想要实现上述解耦操作,就涉及到Spring中的两个核心概念:
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
程序运行时需要某个资源,此时容器就为其提供这个资源。
例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。
bean对象:IOC容器中创建、管理的对象,称之为:bean对象。
IOC&DI基础
将Service及Dao层的实现类,交给IOC容器管理
1:只需要在实现类加上 @Component
注解,就代表把当前类产生的对象交给IOC容器管理。
例如:
2. 为Controller 及 Service通过@Autowired注解注入运行时所依赖的对象
例如:
IOC详解
前面我们提到IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。
在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
那么此时,我们就可以使用 @Service
注解声明Service层的bean。 使用 @Repository
注解声明Dao层的bean。
例如:
注意1:声明bean的时候,可以通过注解的value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
注意2:使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
问题:使用的这四个注解声明的bean,一定会生效吗?
答案:不一定。(原因:bean想要生效,还需要被组件扫描)
声明bean的四大注解,要想生效,还需要被组件扫描注解 @ComponentScan
扫描。
该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解
可以看到@SpringBootApplication的定义里包含了
@ComponentScan
所以,我们在项目开发中,只需要按照如上项目结构,将项目中的所有的业务类,都放在启动类所在包的子包中,就无需考虑组件扫描问题
DI详解
依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
之前我们使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。
@Autowired用法有三种
在项目开发中,基于@Autowired进行依赖注入时,基本都是第一种和第二种方式。(官方推荐第二种方式,因为会更加规范)但是在企业项目开发中,很多的项目中,也会选择第一种方式因为更加简洁、高效(在规范性方面进行了妥协)。
问题:如果在IOC容器中,存在多个相同类型的bean对象,DI注入时候会注入哪一个bean对象呢?
可以看到,正常情况有多个bean时候,DI自动装配会报错,不知道自动装配哪一个bean
解决办法:
@Primary:为要注入的bean添加优先级
@Qualifier:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。//bean的名称默认为类名首字母小写
@Qualifier注解不能单独使用,必须配合@Autowired使用。
@Resource:按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
@Autowird 与 @Resource的区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入