分层解耦
三层架构
- controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据
- service:业务逻辑层,处理具体的业务逻辑
- dao:数据访问层(Data Access Object)(持久层)。负责数据访问操作,包括数据的增删改查
代码拆分的优势:
分层解耦概念
- 耦合:衡量软件中各个层/各个模块的依赖关联程度
- 内聚:软件中各个模块内部的功能联系
- 软件设计原则:**高内聚低耦合**

控制反转:Inversion Of Control,简称IOC。对象创建控制权由程序自身转移到外部(容器),这种思想称为控制反转
依赖注入:Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入
Bean对象:IOC容器中创建、管理的对象,称之为Bean
只有被标明了是Bean的类,才会在容器中创建对象
Spring 容器本质上是一个中间件,用于解耦类与类之间的依赖关系
实现分层解耦,就是将项目中的类交给IOC容器管理
当某个类需要对应的类的对象时,直接依赖容器为其提供
IOC详解
将类交给 Spring IOC 容器管理有以下几种主要方式:
- 组件扫描注解(最常用):
1 2 3 4 5 6
| @Component @Service @Repository @Controller @RestController @Configuration
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Component public class UserService { }
@Service public class OrderService { }
@Repository public class UserRepository { }
|
总结:
| 注解 |
说明 |
位置 |
| @Component |
声明bean的基础注解 |
不属于以下三类时,用此注解 |
| @Controller |
@Component的衍生注解 |
标注在控制层类上 |
| @RestController |
@Controller的衍生注解 |
标注在控制层类上,适用于Restful API风格 |
| @Service |
@Component的衍生注解 |
标注在业务层类上 |
| @Repository |
@Component的衍生注解 |
标注在数据访问层类上(由于与mybatis整合,用的较少) |
| @Mapper |
mybatis提供的注解 |
标注在数据访问层类上 |
| @Configuration |
@Component的衍生注解 |
标注在配置类上 |
------以下方式了解即可------
- Java 配置类中的 @Bean:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration public class AppConfig { @Bean public UserService userService() { return new UserService(); } @Bean public OrderService orderService() { return new OrderService(userService()); } }
|
- 包扫描配置:
1 2 3 4 5 6 7
| @SpringBootApplication @ComponentScan(basePackages = {"com.norlcyan.service", "com.norlcyan.repository"}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
1 2 3 4 5 6
| package com.norlcyan.service;
@Service public class BusinessService { }
|
- XML 配置(较少使用):
1 2 3 4 5
| <!-- applicationContext.xml --> <beans> <bean id="userService" class="com.norlcyan.service.UserService"/> <bean id="orderService" class="com.norlcyan.service.OrderService"/> </beans
|
- 条件化 Bean 创建:
1 2 3 4 5 6 7 8 9 10 11
| @Component @ConditionalOnProperty(name = "feature.enabled", havingValue = "true") public class FeatureService { }
@Component @Profile("dev") public class DevService { }
|
- Import 注解:
1 2 3 4 5
| @Configuration @Import({UserService.class, OrderService.class}) public class ServiceConfig { }
|
最常用的两种方式:
@Component 及其衍生注解 - 适用于自开发的类
@Bean 注解 - 适用于第三方类或需要复杂配置的 Bean
查看容器中的所有Bean
图片演示:


如果在Actuator中将Bean展示为自定义的名称,可以通过@Component("自定义名称")的方式,其他衍生注解同理
IOC组件扫描
- 前面声明bean的注解(@Component及其衍生注解),想要生效,还需要被组件扫描注解**@ComponentScan**扫描
- 该注解虽然没有显式配置,但是实际上已经包含了启动类声明注解@SpringBootApplication中,默认扫描范围是启动类所在包及其子包
DI详解
依赖注入的方式有很多种,但是大部分都基于@Autowired这个注解
- 构造函数注入(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } }
@Service public class UserService { private UserRepository userRepository = new UserRepository(); }
|
优点:
- 对象创建后状态不可变(final 字段)
- 保证依赖不为 null
- 便于单元测试和 Mock
当Spring框架启动时,会自动调用注入的构造方法为其需要的对象赋值
如果类中只有一个注入构造函数,可以省略@Autowired。但存在多个构造函数时,注入构造函数上的@Autowird是不能省略的
为了保持代码的一致性和可读性,建议所有注入构造函数上都加上@Autowired
- Setter 方法注入(了解)
1 2 3 4 5 6 7 8 9 10
| @Service public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } }
|
- 字段注入(最简单,适用于快速开发小型项目)
1 2 3 4 5 6
| @Service public class UserService { @Autowired private UserRepository userRepository; }
|
- 方法注入
1 2 3 4 5 6 7 8 9 10
| @Service public class UserService { private UserRepository userRepository; @Autowired public void configure(UserRepository userRepository) { this.userRepository = userRepository; } }
|
- @Value 注入简单值
1 2 3 4 5 6 7 8 9 10 11
| @Component public class ConfigService { @Value("${app.name}") private String appName; @Value("${app.version:1.0}") private String version; @Value("#{systemProperties['java.home']}") private String javaHome; }
|
- 集合注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Service public class BusinessService { @Autowired private List<StringService> stringServices; @Autowired private Map<String, StringService> stringServiceMap; @Autowired private StringService[] serviceArray; }
|
- 条件化注入
1 2 3 4 5 6 7 8 9 10 11 12
| @Service public class UserService { @Autowired(required = false) private OptionalService optionalService; @Autowired @Qualifier("primaryUserRepository") private UserRepository userRepository; }
|
- Java 配置中的注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class AppConfig { @Bean public UserService userService(UserRepository userRepository) { return new UserService(userRepository); } @Bean public UserRepository userRepository() { return new DatabaseUserRepository(); } }
|
总结
| 注入方式 |
推荐程度 |
优点 |
缺点 |
| 构造函数注入 |
⭐⭐⭐⭐⭐ |
不可变、必需依赖、利于测试 |
循环依赖问题 |
| Setter 注入 |
⭐⭐⭐⭐ |
灵活、可解决循环依赖 |
可能为 null |
| 字段注入 |
⭐⭐ |
简单、适用于快速开发小型项目 |
不利于测试、不可变性差 |
注入冲突问题
- @Autowired注解,默认是按照类型进行注入的
但是,如果出现以下情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public interface A { String getUsers(); }
@Service public class A1TestImpl implements A { ... }
@Service public class A2TestImpl implements A { ... }
@Component public class Test { @Autowired private A a; }
|
这时候,无法知道到底是注入A1TestImpl呢,还是注入A2TestImpl呢
所以会出现以下报错:

解决方案一(**@Primary**):
1 2 3
| @Primary @Service public class A1TestImpl implements A { ... }
|
@Primary主要用于将当前类的Bean提升优先级
解决方案二(**@Qulifier**):
1 2 3 4 5 6
| @Component public class Test { @Autowired @Qualifier("a2TestImpl") private A a; }
|
解决方案三(**@Resource**):
1 2 3 4 5
| @Component public class Test { @Resource("a2TestImpl") private A a; }
|
Bean默认命名规则:类名首字母小写
循环注入问题
- 循环注入(Circular Injection/Circular Dependency)是指两个或多个Bean之间相互依赖,形成一个循环依赖关系
- 想要了解项目是否存在循环注入问题,可以通过日志信息、Actuator监控等方式
循环注入的类型:
- 直接循环依赖
1 2 3 4 5 6 7 8 9 10 11
| @Service public class ServiceA { @Autowired private ServiceB serviceB; }
@Service public class ServiceB { @Autowired private ServiceA serviceA; }
|
- 间接循环依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Service public class ServiceA { @Autowired private ServiceB serviceB; }
@Service public class ServiceB { @Autowired private ServiceC serviceC; }
@Service public class ServiceC { @Autowired private ServiceA serviceA; }
|
解决方案:
- 重新设计架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Service public class CommonService { }
@Service public class ServiceA { @Autowired private CommonService commonService; }
@Service public class ServiceB { @Autowired private CommonService commonService; }
|
- 使用Setter注入
1 2 3 4 5 6 7 8 9
| @Service public class ServiceA { private ServiceB serviceB; @Autowired public void setServiceB(ServiceB serviceB) { this.serviceB = serviceB; } }
|
- 使用@Lazy注解
1 2 3 4 5 6
| @Service public class ServiceA { @Autowired @Lazy private ServiceB serviceB; }
|