三层架构

上一篇文章的案例出现问题,进行程序设计以及程序开发时,应该满足单一职责原则,一个类或一个方法,就只做一件事情,只管一块功能,但是上一篇文章却是一个类做了三件事情

上一篇文章的处理逻辑,从组成上看可以分为三个部分:

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。

  • Service:业务逻辑层。处理具体的业务逻辑。

  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

具体流程为:

  • 前端发起请求,由Controller层接收(Controller响应数据给前端)

  • Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)

  • Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)

  • Dao层操作文件中的数据(Dao拿到的原始数据会返回给Service层)

定义业务逻辑层接口

public interface EmpService {
    //获取员工列表
    public List<Emp> listEmp();
}

定义数据访问层接口

public interface EmpDao {
    //获取员工列表数据
    public List<Emp> listEmp();
}

  • private EmpService empService = new EmpServiceA() ;是具体定义的EmpServiceA()类

  • private EmpDao empDao = new EmpDaoA();是具体定义的EmpDaoA类

分层解耦

高内聚:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 "高内聚"

低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好

软件开发中应该满足高内聚,低耦合的特点,但是上述分层的做法却是高耦合的,例如把业务类变为EmpServiceB时,需要修改controller层中的代码

解决办法

引入控制反转与依赖注入

控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转,IOC容器中创建、管理的对象,称之为:bean对象

依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入

例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象

实现步骤:

  • 删除Controller层、Service层中new对象的代码

  • Service层及Dao层的实现类,交给IOC容器管理,使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理

  • 为Controller及Service注入运行时依赖的对象,使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象

IOC细节:

  • 类交给IOC容器管理,使用@Component太宽泛,无法确定bean对象到底归属于哪一层,又提供了@Component的衍生注解(无法确定属于哪一类,例如工具类才用Component):

    • @Controller (标注在控制层类上)

    • @Service (标注在业务层类上)

    • @Repository (标注在数据访问层类上)

  • 但是@RestController = @Controller + @ResponseBody,所以控制层不用再单独声明@Repository 了

  • 在IOC容器中,每一个Bean都有一个属于自己的名字,可以通过注解的value属性指定bean的名字。如果没有指定,默认为类名首字母小写。

  • 使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描,@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,(默认扫描的范围是SpringBoot启动类所在包及其子包),如果定义的类不在SpringBoot启动类所在包及其子包,手动添加@ComponentScan注解,指定要扫描的包

DI细节:

如果在IOC容器中,存在多个相同类型的bean对象,会报错(不知道该注入哪个对象)解决方案:

  • 使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现

  • 使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称,@Qualifier注解不能单独使用,必须配合@Autowired使用

  • 使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称

@Autowird 与 @Resource的区别:

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入