什么是分层解耦,什么是三层架构,一篇文章告诉你!

发布时间:2024-10-18 07:09:46

分层解耦(重点)

1. 分层解耦是什么

定义:分层解耦指的是将系统拆分成多个层次,每一层都有特定职责,并彼此隔离。这种方法能让各层独立演变,降低模块间的耦合,提升系统的可维护性。

内聚:软件中各个功能模块内部的功能联系。 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。  软件设计原则:高内聚低耦合。

常见分层(三层架构):

表现层(Controller 层):负责处理用户请求和返回数据,通常使用 Spring MVC 的 @Controller。 业务层(Service 层):包含核心业务逻辑,通常用 @Service 注解标识。 数据访问层(DAO 层):负责数据库交互(持久层),通常用 @Repository 注解标识。

解耦目的

直接实例化对象会导致类与类之间的耦合,尤其是在多层结构的代码中(例如Controller、Service、DAO层),更换实现类需要修改代码,降低了代码的灵活性和可维护性。 使用IOC容器管理对象,将依赖注入到需要的类中,解耦依赖关系,减少因为实现类更换导致的代码修改。

如何解耦呢?

在三个层中Controller层需要访问Service层获取处理的数据,Service层有需要访问DAO层获取数据,假设Service层的实现类访问了DAO层的实现类,就必须创建DAO层的实现类

private EmpDao empDao = new EmpDaoA();//面向接口编程(多态)

当DAO层的实现类名改变的时候如EmpDaoB,new 的类名也自然也要改如new EmpDaoB(),这就是耦合,如何解决耦合呢?

以前是我需要什么对象就new什么对象,现在是将所有的对象交给了外部(IOC)容器来管理,这样就可以解决new对象耦合的问题。

控制反转(IOC) 是一种将对象创建和管理职责交由外部容器(如Spring)处理的思想,避免在代码中直接创建实例的耦合关系。通过IOC,我们可以从外部容器获取我们需要的对象,控制权被“反转”给容器。 依赖注入(DI) 是实现IOC的一种方式。容器会在运行时自动将一个对象的依赖注入到该对象中,这样对象不需要显式地创建依赖对象。 Bean对象:loc容器中创建、管理的对象,称之为bean。

这是 Spring 框架中常见的注解,用于指定类的功能角色,使得 Spring 容器可以自动扫描、管理并注入依赖。以下是详细说明:

| 注解          | 说明                                                         | 位置与用法                                                   | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | @Component  | 声明一个 Bean 的基础注解,它会被 Spring 扫描并注册到容器中。适用于不属于以下三类的 Bean。 | 标注在任意类上,当无法确定具体功能时使用。                   | | @Controller | @Component 的衍生注解,专门用于控制器类(例如处理 HTTP 请求的类),通常用于 MVC 模式的控制层。 | 标注在控制器类(处理用户请求的类)上。                       | | @Service    | @Component 的衍生注解,标识业务逻辑类。通常用于封装业务服务的实现逻辑。 | 标注在业务类上,表示业务层的服务。                           | | @Repository | @Component 的衍生注解,专用于数据访问层,用于标识数据库操作类(DAO)。这个注解会处理持久化异常,使得数据访问异常可以被统一转换。 | 标注在数据访问类上(例如操作数据库的类)。不过,结合 MyBatis 时较少使用。 |

补充说明

扫描机制:前面声明bean的四大注解,要想生效,还需要被组件扫描注解@componentScan扫描。@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。 ```java   @ComponentScan(("dao","com.itheima")//手动指定扫描的包,注意,使用此注解后,默认扫描失效!   @SpringBootApplication//默认扫描当前包及其子包   public class SpringbootWebReqRespApplication   public static void main(String[] args){

}   ```

衍生作用:@Controller、@Service、@Repository 是 @Component 的衍生注解,具备 @Component 的所有功能,但更加语义化,有助于代码的分层和管理。

示例

@Component // 通用组件
public class GeneralBean {}

@Controller // 控制器
public class UserController {}

@Service // 业务服务
public class UserService {}

@Repository // 数据访问层
public class UserRepository {}

注意事项

使用衍生注解(@Controller、@Service、@Repository)更能体现类的实际职责,便于维护和管理。 Spring 自动处理 @Repository 中的数据访问异常,但需要根据项目框架使用(例如 MyBatis 中可以不标注 @Repository )。

所以我们在编程时,只需要把Service层的实现类和DAO层的实现类交给IOC容器管理为Bean对象

@Component
public class EmpServiceA implements EmpService{//将这个实现类给IOC容器

}

在需要使用这个对象的地方创建对应类的接口类型数据上加入@Autowired注解就行了

@Autowired//自动在IOC里获取基于EmpService接口实现的类注入
private EmpService empService;//EmpService这个就是Service层实现类的接口,基于这个接口实现的类我们都可以获取到。

代码示例

DAO层和Service层定义

假设有以下接口和实现类:

// DAO接口
public interface EmpDao {
    // 一些数据访问方法
}

// DAO实现类A
@Repository // 告诉Spring将此类托管为Bean
public class EmpDaoA implements EmpDao {
    // 数据访问方法实现
}

// DAO实现类B
@Repository // 另一种实现类
public class EmpDaoB implements EmpDao {
    // 数据访问方法实现
}

// Service接口
public interface EmpService {
    // 一些业务逻辑方法
}

// Service实现类
@Service // 将此类托管为Bean
public class EmpServiceA implements EmpService {

    private final EmpDao empDao;

    // 构造函数注入DAO
    @Autowired
    public EmpServiceA(EmpDao empDao) {
        this.empDao = empDao;
    }

    // 使用empDao进行数据处理
}

Controller层使用Service

在Controller中使用Service时,通过 @Autowired 注解自动注入 EmpService 的实现类:

@RestController
public class EmpController {

    @Autowired // 自动注入EmpService接口实现类
    private EmpService empService;

    // 控制器方法,可以使用empService进行服务调用
}

依赖注入的多种方式

Spring提供了多种依赖注入方式,根据不同需求可以选择不同的注入方式:

构造函数注入:通过构造函数注入依赖对象,是推荐的方式。 Setter方法注入:通过Setter方法注入依赖,适合可选依赖。 字段注入:直接在字段上使用@Autowired,简单但不利于单元测试。

总结

将对象交由Spring的IOC容器管理,通过DI来解耦依赖,提升代码的灵活性和可维护性。我们通过注解如 @Component、@Autowired 等实现自动化注入,不再手动创建依赖对象,从而减少代码耦合度。

2. 三层架构

三层架构是一种常见的分层模式,其目的是将系统功能分为表现、业务和数据访问三层,以便实现模块化开发。

层次职责

表现层:与前端交互,接收 HTTP 请求并返回响应,通常用 Spring MVC 的 @Controller 注解来标识,例如 UserController。 业务层:处理业务逻辑,负责调用 DAO 层获取数据或处理逻辑。使用 @Service 注解来标识。 数据访问层:直接与数据库交互,通过 SQL 查询、ORM 等方式管理数据,通常使用 @Repository 注解。

此分层模式在上述的示例代码中已展示。通过分离每层职责,可以有效地隔离系统逻辑,降低代码耦合度。

注意事项:

单向调用:表现层只能调用业务层,业务层只能调用数据访问层。 避免跨层:不要在表现层直接调用 DAO 层,违反了三层架构的职责分工。

IOC 详解

Spring 容器在项目启动时会扫描注解并自动创建 Bean。IOC 通过注解或 XML 配置来控制对象的生命周期和依赖关系。

IOC 注解方式和 XML 配置方式示例

注解方式:

@Service
public class OrderService {
    // Spring 容器会扫描注解并自动创建实例
}

XML 配置方式:

<beans>
    <bean id="orderService" class="com.example.OrderService" />
</beans>

注意事项:

单例和多例:默认情况下,Spring 容器中的 Bean 是单例的,可以通过 @Scope("prototype") 注解来设为多例。 Bean 生命周期:Spring 支持 @PostConstruct 和 @PreDestroy 注解来定义 Bean 的初始化和销毁方法。

DI 详解

依赖注入支持构造器、Setter 和接口三种方式,Spring 会自动管理 Bean 的创建和注入。对于复杂依赖关系,可以通过 @Qualifier 注解精确指定 Bean。

依赖注入示例

构造器注入:

public class OrderProcessor {
    private final OrderDAO orderDAO;

    public OrderProcessor(OrderDAO orderDAO) {
        this.orderDAO = orderDAO;
    }
}

Setter 注入:

public class OrderProcessor {
    private OrderDAO orderDAO;

    public void setOrderDAO(OrderDAO orderDAO) {
        this.orderDAO = orderDAO;
    }
}

注意事项:

使用 @Qualifier 注解可以在有多个同类型 Bean 时指定注入。 构造器注入适合必须的依赖,Setter 注入则用于可选依赖。

在 Spring 中,默认情况下,当有两个相同类型的实现类同时被 @Service 注解标注,容器在注入时无法确定应该选择哪个实现类,从而导致冲突,进而抛出错误。可以通过以下方法解决:

方法一:指定 @Primary 注解

使用 @Primary 注解在一个实现类上,告诉 Spring 容器该实现类是主要候选,这样注入时会优先选择这个实现类。

@Service
public class UserServiceImpl1 implements UserService {
    // ...
}

@Primary
@Service
public class UserServiceImpl2 implements UserService {
    // 将优先注入这个类
}

方法二:使用 @Qualifier 指定具体实现

在注入时通过 @Qualifier 注解明确指明需要注入哪个实现类。

@Service("userServiceImpl1")
public class UserServiceImpl1 implements UserService {
    // ...
}

@Service("userServiceImpl2")
public class UserServiceImpl2 implements UserService {
    // ...
}

// 在注入时指定名称
@Autowired
@Qualifier("userServiceImpl1")
private UserService userService;

方法三:将实现类分组到不同的 @Configuration 中

可以使用 @Configuration 配置类,将相同接口的不同实现分组在不同的配置类中,以便在不同的场景中灵活选择实现类。

@Configuration
public class Config1 {
    @Bean
    public UserService userServiceImpl1() {
        return new UserServiceImpl1();
    }
}

@Configuration
public class Config2 {
    @Bean
    public UserService userServiceImpl2() {
        return new UserServiceImpl2();
    }
}

注意事项

不必要的实现类:在单一场景中一般避免多个相同接口的实现类,以免引入复杂性。 @Primary 和 @Qualifier 配合使用:当 @Primary 标记的类和 @Qualifier 同时使用时,以 @Qualifier 为优先。 

总结

分层解耦:将系统分成表现、业务和数据访问层,以降低耦合。 三层架构:表现层处理请求,业务层实现逻辑,数据层负责数据操作。 IOC 和 DI:由 Spring 容器管理对象和依赖,解耦代码结构,提高灵活性和可测试性。