Spring 基础 控制反转与依赖注入

本文最后更新于 2025年6月22日 晚上

IoC与DI

IoC(控制反转)和di(依赖注入) 。 即通过注解的方式,将业务流程中对对象的创建和管理的 控制权转移给Spring框架来进行控制。Spring框架将对象创建与业务解耦,使编程人员能够更加专注于业务实现,而不再需要着重考虑使用对象的生命周期。

整体流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
类扫描(@ComponentScan

BeanDefinition注册

Bean实例化(构造器 or 工厂方法)

依赖注入(@Autowired@Value

初始化(@PostConstruct、InitializingBean、initMethod)

AOP代理(若满足切面条件)

应用运行中

销毁(@PreDestroy、DisposableBean、destroyMethod)

Bean

Bean 是 被Spring 管理的对象的 统称

Bean 配置

1.@Component - @Autowire( 类注解 + 自动注入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 数据接入类
@Component
public class UserDao {
public void getUser()(
System.out.println("Hello Spring")
)
}
// 数据服务类
@Component
public class UserService{
@Autowired
UserDao ud ;
public void getUser(){
ud.getUserDao();
}
}

// 测试类

@SpringBootApplication
Test01{
@Test
public void test()
{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
IUserService userService = context.getBean("userService",IUserService.class);
userService.getUser();
}
}

以及Component 衍生注解

@Service

@Repository

@Configuration

2 @Bean

通过方法将对象注册到Spring

必须放置在配置类里面

@Configuration和@Component 注解下 @bean行为的区别

@Configuration 类下的 @Bean 方法调用(代理增强

当一个类被 @Configuration 注解标记时,Spring 会用 CGLIB 动态代理 增强这个类,形成一个代理对象。

  • 这个代理对象在内部维护了 Spring 容器的上下文;
  • 当你在 @Configuration 类内部调用 @Bean 方法时,其实是通过代理对象去调用的;
  • 代理对象会拦截这个调用,先检查 Spring 容器中是否已经存在对应的单例 Bean,如果存在直接返回,否则创建并注册。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java
复制编辑
@Configuration
public class AppConfig {
@Bean
public A a() {
return new A(b());
}

@Bean
public B b() {
return new B();
}
}

a() 方法调用 b() 时,其实走的是 AppConfig 代理对象的 b() 方法,因此它会走 Spring 容器逻辑,而不是直接的方法调用。


@Component 类下的 @Bean 方法调用(无代理增强

当你的类只是用 @Component 标注时,Spring 不会使用 CGLIB 增强它。

此时:

  • 这个类就是一个普通的被 Spring 托管的对象;
  • 其中的 @Bean 注解方法在容器初始化时仍然会被扫描并注册为 Bean(主要取决于你是不是把 @Component 类纳入了 @ComponentScan 范围);
  • 但是:方法之间的调用是普通的 Java 方法调用,没有代理,没有容器拦截
  • 所以在 @Component 下,如果你自己在一个 @Bean 方法中调用另一个 @Bean 方法,其实就是直接调用普通方法 —— 它不会走 Spring 容器逻辑,也就没有 Bean 生命周期、依赖注入、单例复用等特性。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java
复制编辑
@Component
public class MyComponent {

@Bean
public A a() {
return new A(b()); // 直接调用 b() 方法,而不是容器中的 B 实例
}

@Bean
public B b() {
return new B();
}
}

此时 b() 方法就是普通的对象方法,不经过 Spring 容器,你实际上会创建了两个 B 实例:

  • 一个被 Spring 容器注册;
  • 一个直接被 a() 方法内部调用创建。

3 @Import (对类注解)

在当前配置类中手动导入其他 Bean 定义。

1 声明导入配置类或普通类

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@Import(OtherConfig.class)
public class MainConfig {
}

@Configuration
public class OtherConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
  1. 实现ImportSelector接口,传入字符数组导入相关类
1
2
3
4
5
6
7
8
9
10
11
12
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.MyService", "com.example.MyRepository"};
}
}

@Configuration
@Import(MyImportSelector.class)
public class MainConfig {
}

  1. 实现ImportBeanDefinitionRegistrar接口,进行注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {

RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);
registry.registerBeanDefinition("myBean", beanDefinition);
}
}

@Configuration
@Import(MyRegistrar.class)
public class MainConfig {
}

Bean的实例化

Bean 实例化时默认选择 无参构造器进行构造

如果 不存在无参构造器,只存在一个构造器,则使用该构造器构造,并自动注入参数对象

如果 存在多个构造器, 则会报错

@Component 类注册定义模式 只是把类结构信息注册进去,实例化时反射创建实例,依赖注入走标准构造器或属性。

@Bean 工厂方法定义模式 , Spring 注册了 “如何执行这个方法去获得实例” 的元数据,不直接关心类信息,而是把方法当成实例来源。

通过可以bean注解 来指定 具体使用的构造方法

FactoryBean 接口

通过实现FactoryBean 接口 来将一个工厂类注入到Spring中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class OrderService implements FactoryBean {
@Override
public Object getObject() throws Exception{
return new UserService();
// 定义工程实际创建的对象
}

@Override
public Class<?>getObjectType(){
return UserService.class;
// 当使用类型创建bean时指定的类型信息
}
}

//
@SpringBootApplication
public class C3IocApplication {
ConfigurableApplicationContext ioc = SpringApplication.run(C3IocApplication.class)
System.out.println(iot.getBean("&orderService"));
}

依赖注入

@Autowired 自动装配

作用域 字段,构造方法,参数,函数

多个类型注入 ,优先按类型,再按名字

@Primary 同一个类,优先匹配这个注解的实现

@Qualifier 指定别名匹配

@Autowired(required=false)

Autowired 的注入位置

  1. 在 对象声明位置进行自动注入
1
2
@Autowired
UserService userservice;
  1. 在 构造器处进行自动注入,显式指定创建对象使用的构造器
1
2
3
4
5
6
7
8
class AutoService {

@Autowired
AutoService(ProductService productservice){
this.productservice = productservice;
}

}
  1. 在 传入参数时 使用Autowired 进行自动注入,设置required 允许 该参数传入为空
1
2
3
4
5
6
class AutoService {
@Autowired
AutoService(@Autowired(required=false) ProductService productservice){
this.productservice = productservice;
}
}
  1. 在函数定义上增加Autowired , 在 该方法的对象创建后自动执行,Spring不关注方法的返回值
1
2
3
4
5
6
7
8
9
10
11
class AutoService{
@Autowired
AutoService(ProductService productservice){
this.productservice = productservice;
}
@Autowired
public void getAutoService(){
System.out.println("fetch the AutoService");
}

}

其他自动注入注解

@Inject

1
2
3
4
5
6
// 通过包 引用,不能设置required=false
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>2.0.1</version>
</dependency>

@Resource

先根据名字查找,再根据类型查找

值注入

@Value

向声明的变量进行赋值

1
2
@Value("18")
private Integer age ;

@PropertySource() 指定提取属性的位置 (默认从application.properties中查询)

1
2
3
@Value("${person.age}")
@PropertySource("person.property")
private Integer age ;

spel 向 容器赋值

1
2
3
4
@Value("#{{'语文':'90','数学':'88'}}")
private Map<String,Integer> score ;
@Value("#{{'音乐,电影,话剧'}}")
private List<String> habbies ;

@Order

定义注入同一个接口实现下的不同实现类的加载顺序。 值小的先被加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@SpringBootTest(classes = TestOrder.class)
public class TestOrder{
@Bean
@Order(1)
public A a(){
return new A();
}

@Bean
@Order(0)
public B b(){
return new B();
}

@Test
public void test(@Autowired List<I> i ){
System.out.println(i);
}
}
// 或实现 Ordered接口
public class B implements I, Ordered {
public B(){
System.out.println("B");

@Override
public int getOrder(){
return 0 // 获取优先级级别
}
}

}

@Dependson

定义不同类之间的加载先后关系

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootDependsOnTest(classes = TestDependson.class)
public class TestDependson{
@Bean
@DependsOn("d") // d会提前加载
public C c(){
return new C();
}
@Bean
public D d(){
return new D();
}

}

@Lazy

被@Lazy注解的对象直到 被使用时才会被加载

@Scope

定义对象的创建(单例或非单例)

1
2
3
4
5
6
7
8
9
@Bean
@Scope("Singleton")
UserService userservice
// 对象只会创建一次

@Bean
@Scope("prototype")
UserSercice userservice
// 对象在每次调用后都会创建

@Conditional

动态决定某个bean 是否生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SpringBootTest(classes = TestConditional.class)
public class TAestConditional{
@Bean
@Conditional(MyCondition.class)
public ConditionalService conditionalService(){
return new ConditionalService();
}


}

// MyCodition.class 实现了Condition 接口
public class MyCondition implements Condition{
@Override
public boolena matches(CondtionContext context , AnnotatedTypeMetadata metadata){
return false ;
}
}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public interface IDB{
public void connection(){};
}

// 实现类
public class MySqlDB implements IDB{
@Override
public void connection(){
System.out.println("连接MySql");
}
}
// 实现类
public class OracleDB implements IDB{
@Override
public void connection(){
System.out.println("连接Oreacle");
}
}

@SpringBootTest(classes = TestDB.class)
public class TestDB{
@Bean
public IDB mysqlDB(){
return new MySqlDB();
}

@Bean
public IDB oracleDB(){
}
}

public class MysqlCondition implements Condition{
@Override
public boolean matches(ConditionContext context , AnnotatedTypeMetadata metadata){
try {
context.getClassLoader().loadClass("MySQLDBConnection_class_path")
return true ; // 检查MySQL 核心类是否加载
}catch {ClassNotFoundException e}{
return false ;
}
return false ;
}
}

public class MysqlCondition implements Condition{
@Override
public boolean matches(ConditionContext context , AnnotatedTypeMetadata metadata){
try {
context.getClassLoader().loadClass("OracleDBConnection_class_path")
return true ; // 检查Oracle 核心类是否加载
}catch {ClassNotFoundException e}{
return false ;
}
return false ;
}
}

Bean的生命周期

配置编码阶段

对需要注入容器的类进行注解

1
2
3
4
5
6
7
8
@Component 
class BaseService{
// Service_function
@Bean
public UserService getuserservice(){
return new UserService();
}
}

在Springboot 应用中注入依赖

1
2
3
4
5
6
7
8
9
@SpringBootApplication
class Worker{
@Autowired
UserService userservice

public void ExecService(UserService userserive){
userservice.getservice();
}
}

启动Spring 程序

加载Spring 容器

实例化托管对象

根据注解注入对象到业务流程中

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 基于接口的 初始化定义
public class UserService implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化");
}
}

// 基于注解的 初始化定义
public class UserService {
@PostConstruct
public void afterPropertiesSet() throws Exception {
System.out.println("初始化afterPropertiesSet");
}
}
// 基于initMethod属性 初始化定义
public class UserService {
public void init() throws Exception {
System.out.println("init方法初始化");
}
}
// 服务端指定初始化方法
@Bean(initMethod="init")
public UserService userServie() {return new UserService();}
// 执行顺序
// 注解
// 注入时方法指定
// 实现接口

放入 Map<beanName,bean对象>

spring容器.getBean(”beanName”)

spring容器关闭 , bean 销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class UserService implements DisposableBean{
@Override
public void destory() throws Exception {
System.out.println("接口实现销毁");
}
}

// 基于注解的 初始化定义
public class UserService {
@PreDestory
public void afterPropertiesSet() throws Exception {
System.out.println("PreDestory销毁");
}
}
// 基于initMethod属性 初始化定义
public class UserService {
public void destory() throws Exception {
System.out.println("destory销毁");
}
}
}
// 服务端指定初始化方法
@Bean(destoryMethod="destory")
public UserService userService() {return new UserService();}
// 执行顺序
// 注解
// 实现接口
// 注入时方法指定

循环依赖

在Spring中可能存在编码时A和B俩个bean 需要相互调用的问题。而在Springboot中这种循环依赖的对象创建会导致直接报错

  1. 打开允许循环依赖对象创建设置
1
spring.main.allow-circular-references=true
  1. 代码设计上进行控制
1
2
a. 把依赖的方法写在本类中
b. 使用组合,将bean A 和 bean B 都由bean C 进行调用
  1. 延迟注入
1
2
a. 添加需要依赖的构造函数参数
b. 添加lazy注解

AOP(面向切面编程)

如果说 AOP 是 数据与业务分离,那么AOP的设计则是”运维代码(日志统计,观测,测试)” 与业务的分离。通过定义AOP类向目标业务类中注入切面类,来完成处理过程中对业务流程执行的拦截,获取想要的数据以及进行请求/响应改造过程。

定义业务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class UserService{
public void add(){
System.out.println("增加");
}
public void delete(){
System.out.println("删除");
}
public void update(){
System.out.println("修改")
}
public void query(){
System.out.println("查询")
}
}

定义切面类

1
2
3
4
5
6
7
8
@Around("execution(* class_path.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object result = pjp.proceed();
long end = System.currentTimeMillis();
System.out.println("用时:"+(end-begin)+"ms");
return result;
}

@EnableAspectJAutoProxy 使用SpingBoot 自动添加 使能

术语

目标对象 :要增强的对象

切面 :要增强的代码放入那个类叫做切面类 , @Aspect

通知 :用来增强目标对象的函数,叫做通知

切点 :通知的目标方法,使用切点表达式指定

连接点 :通知和目标方的桥梁

顾问 :存储切入类对象的对象

织入 : 切入类中的方法 组合到目标对象的过程

通知

@Before

定义在目标方法之前执行的方法

1
2
3
4
5
6
7
8
9
10
11
@Aspect
@Component
public class LogAspect{
@Before("execution(*.com.example.*(..))")
public void before(JoinPoint joinpoint){
String methodName = joinPoint.getSignature().getName();

Object[] args = joinPoint.getArgs();
System.out.println("当前方法是"+methodName+"参数"+Array.toString(Args));
}
}

@After

定义在目标方法之后执行的方法

@AfterReturning

定义在目标方法返回前执行的方法

@AfterThrowing

定义在目标方法异常时(Catch中)会执行的方法

前置—执行方法—返回通知—后置通知

抽取切点表达式

通过注解 @Pointcut 创建一个函数,后续都可以使用该函数作为切点表达式

1
2
3
4
5
@Pointcut("execution( public int (target_method_path))")
public void myPoint(){};

后续 切面方法可以使用
@After("myPoint") 进行注解

通配符规则

1
2
3
4
5
"execution(* public void target_method_path)"

"within( package_name.class_name")

"@annotation(annotation_name)"

Spring 基础 控制反转与依赖注入
http://gadoid.io/2025/06/22/Spring-基础-控制反转与依赖注入/
作者
Codfish
发布于
2025年6月22日
许可协议