再次理解 Spring 中的 IOC、DI、AOP 与多态
目录
引言
1. IOC(控制反转)
1.1 什么是 IOC?
1.2 IOC 的核心思想
1.3 IOC 的实现
2. DI(依赖注入)
2.1 什么是 DI?
2.2 DI 的实现方式
2.3 DI 的核心作用
3. AOP(面向切面编程)
3.1 什么是 AOP?
3.2 AOP 的核心概念
3.3 AOP 的实现方式
4. 多态与 Spring 的关系
4.1 什么是多态?
4.2 多态在 Spring 中的应用
接口和实现类的依赖注入
具体分析
4.3 多态的优势
5. IOC、DI、AOP 与多态的关系
5.1 IOC 和 DI
5.2 DI 和多态
5.3 AOP 和多态
5.4 综合关系
6. 代码示例
6.1 未使用 AOP 的示例
6.2 使用 AOP 的示例
6.3 测试类
6.4 运行结果
7.Spring 容器的选择
常用实现类
8. Spring Bean 的概念
8.1 什么是 Bean?
8.2 Bean 的特点
8.3 Bean 的配置方式
8.4 Bean 的创建流程
1 Bean 的定义
2 Bean 的实例化
3 属性赋值
4 初始化
5 使用
6 销毁
8.5 Bean 的生命周期
1 Bean 的创建
2 Bean 的初始化
3 Bean 的使用
4 Bean 的销毁
8.6 Bean 的作用域
9. @Autowired 注解
9.1 什么是 @Autowired?
9.2 @Autowired 的使用场景
9.3 @Autowired 的工作原理
@Autowired 的匹配规则
10.一些常用注解的联系和区别
1. @Component、@Service、@Repository 的作用
1.1 @Component
1.2 @Service
1.3 @Repository
总结
2. @Autowired 的作用
2.1 使用场景
2.2 工作原理
2.3 总结
3. @Component、@Service、@Repository 和 @Autowired 的区别
结合以上我们做一个底层分析:
2. Spring 容器的运行流程
2.1 Spring 容器启动
2.2 Bean 的创建和依赖注入
2.3 Bean 的初始化
2.4 Bean 的使用
3. 底层原理
3.1 反射机制
3.2 动态代理
3.3 依赖查找
4. 代码示例
4.1 定义 Bean
4.2 测试类
4.3 运行结果
5. 总结
引言
Spring 框架是 Java 开发中最流行的框架之一,其核心特性包括 IOC(控制反转)、DI(依赖注入) 和 AOP(面向切面编程)。这些特性不仅简化了开发流程,还提高了代码的可维护性和扩展性。与此同时,Spring 的实现也深度依赖于 Java 的 多态 特性。本文将详细探讨 Spring IOC、DI、AOP 与多态之间的关系,并通过代码示例帮助读者深入理解。
首先要知道Spring的实现是基于以下原理:
-
IOC 是 Spring 的核心设计原则,DI 是其实现方式。
-
AOP 通过动态代理增强目标方法,依赖于多态实现代理对象。
-
Bean 的生命周期 包括实例化、属性赋值、初始化、使用和销毁。
-
Spring 的底层实现依赖于 反射机制 和 动态代理。
1. IOC(控制反转)
1.1 什么是 IOC?
IOC(Inversion of Control)是一种设计原则,它将对象的创建和依赖关系的管理从应用程序代码中转移到框架或容器中。在 Spring 中,IOC 容器负责创建和管理 Bean 的生命周期。
将对象的创建权力反转给Spring框架!
1.2 IOC 的核心思想
-
传统方式:开发者手动创建对象并管理依赖关系。
-
IOC 方式:Spring 容器负责创建对象并注入依赖关系,开发者只需关注业务逻辑。
1.3 IOC 的实现
Spring 通过 BeanFactory 和 ApplicationContext 实现 IOC 容器。开发者只需通过配置文件(XML 或注解)定义 Bean,Spring 容器会自动创建和管理这些 Bean。
2. DI(依赖注入)
2.1 什么是 DI?
DI(Dependency Injection)是 IOC 的一种实现方式,它通过将依赖关系注入到对象中,而不是由对象自己创建依赖。
2.2 DI 的实现方式
Spring 支持以下三种依赖注入方式:
字段注入:
@Autowired
private UserRepository userRepository;
构造器注入:
@Autowired
public UserService(UserRepository userRepository) {this.userRepository = userRepository;
}
Setter 方法注入:
@Autowired
public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;
}
2.3 DI 的核心作用
-
解耦:将对象的创建和依赖关系分离,降低代码的耦合度。
-
灵活性:通过配置文件或注解动态注入依赖,便于扩展和维护。
3. AOP(面向切面编程)
3.1 什么是 AOP?
AOP(Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(如日志、事务、安全等)与核心业务逻辑分离,提高代码的模块化。
3.2 AOP 的核心概念
-
切面(Aspect):横切关注点的模块化,如日志切面、事务切面。
-
通知(Advice):切面在目标方法执行前后执行的动作。
-
切入点(Pointcut):定义哪些方法需要被增强。
-
目标对象(Target):被增强的对象。
-
代理对象(Proxy):Spring 通过动态代理生成的对象,用于拦截目标方法的调用。
3.3 AOP 的实现方式
Spring AOP 支持两种动态代理方式:
-
JDK 动态代理:适用于目标类实现了接口的情况。
-
CGLIB 动态代理:适用于目标类没有实现接口的情况。
4. 多态与 Spring 的关系
4.1 什么是多态?
多态是面向对象编程的三大特性之一,它允许父类或接口引用指向子类或实现类的对象,并在运行时调用实际对象的方法。
4.2 多态在 Spring 中的应用
-
依赖注入:Spring 通过多态将接口与实现类关联起来。例如:
@Autowired
private UserRepository userRepository; // 实际注入的是 UserRepositoryImpl 的实例
-
虽然
userRepository的类型是UserRepository(接口),但实际调用的是UserRepositoryImpl的save方法。 -
AOP 代理:Spring AOP 通过动态代理生成代理对象,代理对象实现了目标接口或继承了目标类,并在运行时拦截方法调用。
举个例子:
接口和实现类的依赖注入
最常见的情况是,通过接口注入实现类。例如,你有一个UserService接口和它的实现类UserServiceImpl。你可以在Spring配置中声明一个UserService类型的bean,但实际上注入的是UserServiceImpl的实例。
@Service
public class UserServiceImpl implements UserService {// 实现方法
}
在另一个需要UserService的类中,你可以这样注入:
@Service
public class UserController {@Autowiredprivate UserService userService; // 这里注入的是UserServiceImpl的实例
}
这种方式利用了多态:UserService接口可以被任何实现了该接口的类实例化,而具体的实现(如UserServiceImpl)则在运行时确定。
具体分析
(1)Spring 容器管理 Bean
-
在 Spring 容器中,所有的 Bean 都是通过类型(接口或类)来管理的。
-
当你使用
@Autowired注解时,Spring 会根据字段的类型(UserRepository)从容器中查找匹配的 Bean。
(2)注入实现类的实例
-
在你的代码中,
UserRepositoryImpl是UserRepository接口的实现类,并且被标记为@Repository。 -
Spring 容器会创建
UserRepositoryImpl的实例,并将其注册为一个 Bean。 -
当你使用
@Autowired注入UserRepository时,Spring 会找到UserRepositoryImpl的实例,并将其注入到userRepository字段中。
(3)多态的体现
-
虽然
userRepository字段的类型是UserRepository(接口),但实际注入的是UserRepositoryImpl的实例。 -
当你调用
userRepository.save()时,实际调用的是UserRepositoryImpl的save方法。
为什么没有显式的多态代码
-
Spring 的依赖注入机制:Spring 自动完成了 Bean 的创建和注入,你不需要手动编写类似
UserRepository userRepository = new UserRepositoryImpl();的代码。 -
多态的隐式使用:虽然你没有显式地写出多态的代码,但 Spring 在底层通过多态机制将接口和实现类关联起来。
4.3 多态的优势
-
解耦:通过接口调用方法,降低代码的耦合度。
-
扩展性:可以轻松替换实现类,而无需修改调用代码。
5. IOC、DI、AOP 与多态的关系
5.1 IOC 和 DI
-
IOC 是设计原则,DI 是其实现方式。
-
Spring 通过 IOC 容器管理 Bean 的生命周期,并通过 DI 将依赖注入到目标对象中。
5.2 DI 和多态
-
DI 依赖于多态,通过接口注入实现类的实例。
-
例如:
@Autowired
private UserRepository userRepository; // 实际注入的是 UserRepositoryImpl 的实例
5.3 AOP 和多态
-
AOP 通过动态代理生成代理对象,代理对象实现了目标接口或继承了目标类。
-
代理对象在运行时拦截方法调用,并执行增强逻辑。
5.4 综合关系
-
IOC 容器 管理 Bean 的生命周期。
-
DI 通过多态将接口与实现类关联起来。
-
AOP 通过动态代理增强目标方法,依赖于多态实现代理对象。
6. 代码示例
6.1 未使用 AOP 的示例
// 用户仓库接口
public interface UserRepository {void save();
}// 用户仓库实现类
@Repository
public class UserRepositoryImpl implements UserRepository {@Overridepublic void save() {System.out.println("保存用户到数据库...");}
}// 用户服务类
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public void saveUser() {userRepository.save();}
}
6.2 使用 AOP 的示例
// 切面类
@Component
@Aspect
public class LoggingAspect {@Pointcut("execution(* com.example.UserService.saveUser(..))")public void saveUserPointcut() {}@Before("saveUserPointcut()")public void beforeSaveUser() {System.out.println("前置通知:准备保存用户...");}@After("saveUserPointcut()")public void afterSaveUser() {System.out.println("后置通知:用户保存完成。");}
}
6.3 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {@Autowiredprivate UserService userService;@Testpublic void testSaveUser() {userService.saveUser();}
}
6.4 运行结果
前置通知:准备保存用户...
保存用户到数据库...
后置通知:用户保存完成。
7.Spring 容器的选择
Spring 提供了两种主要的 IOC 容器:
-
BeanFactory:
-
是 Spring 最基础的容器接口。
-
提供了 Bean 的创建、配置和管理功能。
-
适合资源受限的环境,延迟加载 Bean。
-
-
ApplicationContext:
-
是
BeanFactory的子接口,提供了更多企业级功能。 -
支持国际化、事件传播、AOP 等功能。
-
默认立即加载 Bean。
-
常用实现类
-
ClassPathXmlApplicationContext:从类路径加载 XML 配置文件。 -
AnnotationConfigApplicationContext:基于注解配置的容器。 -
FileSystemXmlApplicationContext:从文件系统加载 XML 配置文件。
8. Spring Bean 的概念
8.1 什么是 Bean?
Spring Bean是Spring框架中的核心概念之一,代表由Spring容器管理的对象。Spring Bean是指在Spring容器中实例化、组装和管理的对象,通常是应用程序中业务逻辑、数据访问、服务等功能的具体实现。通过定义Bean,开发者可以利用Spring提供的依赖注入(Dependency Injection)和面向切面编程(Aspect-Oriented Programming)等特性,简化应用程序的开发和维护12。
8.2 Bean 的特点
-
生命周期管理:Spring 容器负责 Bean 的创建、初始化和销毁。
-
依赖注入:Spring 容器自动将 Bean 的依赖注入到目标对象中。
-
作用域:Bean 可以有不同的作用域,例如单例(Singleton)、原型(Prototype)等。
8.3 Bean 的配置方式
Spring 支持以下几种方式配置 Bean:
1.基于XML的配置:通过在XML配置文件中声明Bean。例如:
<bean id="myBean" class="com.example.MyClass"><property name="propertyName" value="propertyValue"/>
</bean>
2.基于注解的配置:使用注解如@Component、@Service、@Repository等来标识Bean,并在配置类中启用注解扫描。例如:
@Component
public class MyBean {// ...
}
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {// ...
}
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;
}@Repository
public class UserRepositoryImpl implements UserRepository {// 实现代码
}
3.基于Java配置:使用@Configuration和@Bean注解在配置类中显式声明Bean。例如:
@Configuration
public class AppConfig {@Beanpublic MyBean myBean() {return new MyBean();}
}
8.4 Bean 的创建流程
Spring Bean 的创建过程涉及多个接口和回调方法。以下是详细的流程:
1 Bean 的定义
Spring 容器通过 BeanDefinition 对象来描述 Bean 的元数据,包括:
-
Bean 的类名。
-
Bean 的作用域(Singleton、Prototype 等)。
-
Bean 的属性值。
-
Bean 的初始化方法和销毁方法。
BeanDefinition 的来源可以是 XML 配置文件、注解或 Java 配置类。
2 Bean 的实例化
Spring 容器通过反射机制创建 Bean 的实例。具体步骤如下:
-
加载 Bean 的类:
-
根据
BeanDefinition中的类名,使用ClassLoader加载类。
-
-
创建实例:
-
使用反射调用类的默认构造器创建实例。
-
如果 Bean 实现了
FactoryBean接口,则调用getObject()方法创建实例。
-
3 属性赋值
Spring 容器通过依赖注入将 Bean 的属性值设置到实例中。具体步骤如下:
-
解析依赖:
-
根据
@Autowired或 XML 配置,找到需要注入的 Bean。
-
-
注入依赖:
-
通过反射或 Setter 方法将依赖注入到目标 Bean 中。
-
4 初始化
在 Bean 的属性赋值完成后,Spring 容器会调用初始化方法。具体步骤如下:
-
调用
BeanNameAware和BeanFactoryAware:-
如果 Bean 实现了
BeanNameAware接口,Spring 会调用setBeanName()方法。 -
如果 Bean 实现了
BeanFactoryAware接口,Spring 会调用setBeanFactory()方法。
-
-
调用
ApplicationContextAware:-
如果 Bean 实现了
ApplicationContextAware接口,Spring 会调用setApplicationContext()方法。
-
-
调用
BeanPostProcessor的前置方法:-
Spring 会调用所有
BeanPostProcessor的postProcessBeforeInitialization()方法。
-
-
调用初始化方法:
-
如果 Bean 实现了
InitializingBean接口,Spring 会调用afterPropertiesSet()方法。 -
如果 Bean 配置了
init-method,Spring 会调用指定的初始化方法。
-
-
调用
BeanPostProcessor的后置方法:-
Spring 会调用所有
BeanPostProcessor的postProcessAfterInitialization()方法。
-
5 使用
初始化完成后,Bean 可以被应用程序使用。例如,通过 @Autowired 注入到其他 Bean 中。
6 销毁
当 Spring 容器关闭时,会调用 Bean 的销毁方法。具体步骤如下:
-
调用
DisposableBean接口:-
如果 Bean 实现了
DisposableBean接口,Spring 会调用destroy()方法。
-
-
调用销毁方法:
-
如果 Bean 配置了
destroy-method,Spring 会调用指定的销毁方法。
-
8.5 Bean 的生命周期
1 Bean 的创建
Spring 容器根据配置(XML 或注解)创建 Bean 的实例。
2 Bean 的初始化
-
如果 Bean 实现了
InitializingBean接口,Spring 会调用afterPropertiesSet()方法。 -
如果 Bean 配置了
init-method,Spring 会调用指定的初始化方法。
3 Bean 的使用
-
Bean 可以被应用程序使用,例如通过
@Autowired注入到其他 Bean 中。
4 Bean 的销毁
-
如果 Bean 实现了
DisposableBean接口,Spring 会调用destroy()方法。 -
如果 Bean 配置了
destroy-method,Spring 会调用指定的销毁方法。
8.6 Bean 的作用域
Spring 支持以下 Bean 作用域:
-
Singleton:
-
默认作用域,每个 Spring 容器中只有一个 Bean 实例。
-
-
Prototype:
-
每次请求时都会创建一个新的 Bean 实例。
-
-
Request:
-
每个 HTTP 请求都会创建一个新的 Bean 实例。
-
-
Session:
-
每个 HTTP 会话都会创建一个新的 Bean 实例。
-
-
Global Session:
-
用于 Portlet 应用,每个全局 HTTP 会话都会创建一个新的 Bean 实例。
-
9. @Autowired 注解
9.1 什么是 @Autowired?
@Autowired 是 Spring 提供的注解,用于实现 依赖注入(Dependency Injection, DI)。它的作用是将 Spring 容器中的 Bean 自动注入到目标字段、构造器或方法中。
9.2 @Autowired 的使用场景
@Autowired 可以用在以下位置:
字段注入:
@Autowired
private UserRepository userRepository;
构造器注入:
@Autowired
public UserService(UserRepository userRepository) {this.userRepository = userRepository;
}
Setter 方法注入:
@Autowired
public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;
}
9.3 @Autowired 的工作原理
-
扫描 Bean:
-
Spring 容器启动时,会扫描所有被
@Component、@Service、@Repository等注解标记的类,并将它们注册为 Bean。
-
-
解析依赖:
-
当 Spring 容器遇到
@Autowired注解时,会根据类型(如UserRepository)查找匹配的 Bean。 -
如果找到多个匹配的 Bean,Spring 会根据
@Qualifier注解或 Bean 的名称进一步筛选。
-
-
注入依赖:
-
Spring 会将找到的 Bean 注入到目标字段、构造器参数或 Setter 方法中。
-
@Autowired 的匹配规则
-
按类型匹配:
-
如果容器中只有一个匹配类型的 Bean,则直接注入。
-
@Autowired
private UserRepository userRepository;
- 按名称匹配:
-
如果容器中有多个匹配类型的 Bean,可以通过
@Qualifier注解指定 Bean 的名称。
@Autowired
@Qualifier("userRepositoryImpl")
private UserRepository userRepository;
强制注入:
-
如果找不到匹配的 Bean,Spring 会抛出
NoSuchBeanDefinitionException。 -
可以通过
required = false设置非强制注入:
@Autowired(required = false)
private UserRepository userRepository;
10.一些常用注解的联系和区别
1. @Component、@Service、@Repository 的作用
这些注解的作用是 标识一个类为 Spring Bean,并将它们注册到 Spring 容器中。它们的区别主要在于语义和用途。
1.1 @Component
-
作用:通用的注解,用于标识一个类为 Spring Bean。
-
使用场景:适用于任何需要被 Spring 管理的组件。
-
示例:
@Component
public class MyComponent {// 业务逻辑
}
1.2 @Service
-
作用:标识一个类为服务层(Service Layer)的 Bean。
-
使用场景:用于业务逻辑层,通常包含复杂的业务规则和事务管理。
-
示例:
@Service
public class UserService {// 业务逻辑
}
1.3 @Repository
-
作用:标识一个类为数据访问层(DAO Layer)的 Bean。
-
使用场景:用于数据访问层,通常与数据库交互。
-
额外功能:
@Repository注解会自动将数据访问层的异常转换为 Spring 的统一异常体系(如DataAccessException)。 -
示例:
@Repository
public class UserRepositoryImpl implements UserRepository {// 数据访问逻辑
}
总结
-
@Component是通用的注解,适用于任何组件。 -
@Service和@Repository是@Component的特化形式,分别用于服务层和数据访问层。 -
它们的本质都是将类注册为 Spring Bean,区别在于语义和用途。
2. @Autowired 的作用
@Autowired 是 Spring 提供的注解,用于实现 依赖注入(Dependency Injection, DI)。它的作用是将 Spring 容器中的 Bean 自动注入到目标字段、构造器或方法中。
2.1 使用场景
-
字段注入:
@Autowired
private UserRepository userRepository;
-
构造器注入:
@Autowired
public UserService(UserRepository userRepository) {this.userRepository = userRepository;
}
-
Setter 方法注入:
@Autowired
public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;
}
2.2 工作原理
-
Spring 容器启动时,会扫描所有被
@Component、@Service、@Repository等注解标记的类,并将它们注册为 Bean。 -
当 Spring 容器遇到
@Autowired注解时,会根据类型(如UserRepository)查找匹配的 Bean。 -
如果找到匹配的 Bean,Spring 会将其注入到目标字段、构造器参数或 Setter 方法中。
2.3 总结
-
@Autowired用于将 Spring 容器中的 Bean 注入到目标对象中。 -
它依赖于
@Component、@Service、@Repository等注解注册的 Bean。
3. @Component、@Service、@Repository 和 @Autowired 的区别
| 特性 | @Component、@Service、@Repository | @Autowired |
|---|---|---|
| 作用 | 标识一个类为 Spring Bean,并注册到容器中。 | 将 Spring 容器中的 Bean 注入到目标对象中。 |
| 使用场景 | 定义 Bean 的类别(通用、服务层、数据访问层)。 | 实现依赖注入。 |
| 依赖关系 | 被 @Autowired 依赖,用于注入其他 Bean。 | 依赖于 @Component、@Service、@Repository 注册的 Bean。 |
| 示例 | @Component、@Service、@Repository。 | @Autowired。 |
-
@Component、@Service、@Repository:用于标识一个类为 Spring Bean,并注册到容器中。它们的区别在于语义和用途。 -
@Autowired:用于将 Spring 容器中的 Bean 注入到目标对象中,实现依赖注入。 -
关系:
-
@Component、@Service、@Repository注册的 Bean 可以被@Autowired注入。 -
@Autowired依赖于@Component、@Service、@Repository注册的 Bean。
-
结合以上我们做一个底层分析:
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public void saveUser() {userRepository.save();}
}
-
@Service:标识UserService类为一个 Spring Bean,并将其注册到 Spring 容器中。 -
@Autowired:告诉 Spring 容器将UserRepository类型的 Bean 注入到userRepository字段中。 -
userRepository.save():调用UserRepository的save方法。
2. Spring 容器的运行流程
以下是 Spring 容器在运行时的详细流程:
2.1 Spring 容器启动
-
扫描 Bean:
-
Spring 容器启动时,会扫描所有被
@Component、@Service、@Repository等注解标记的类。 -
在你的代码中,
UserService类被@Service注解标记,因此 Spring 会将其注册为一个 Bean。
-
-
创建 BeanDefinition:
-
Spring 会为每个 Bean 创建一个
BeanDefinition对象,描述 Bean 的元数据(如类名、作用域、依赖关系等)。
-
-
注册 Bean:
-
将
BeanDefinition注册到 Spring 容器的BeanFactory中。
-
2.2 Bean 的创建和依赖注入
-
实例化 Bean:
-
当 Spring 容器需要创建
UserService的实例时,会调用UserService的默认构造器(如果没有显式定义构造器,Java 会提供一个默认的无参构造器)。
-
-
依赖注入:
-
Spring 容器会检查
UserService类中是否有需要注入的依赖(通过@Autowired注解标记的字段、构造器或方法)。 -
在你的代码中,
userRepository字段被@Autowired注解标记,因此 Spring 会查找UserRepository类型的 Bean。
-
-
查找依赖的 Bean:
-
Spring 容器会查找所有实现了
UserRepository接口的 Bean。 -
如果找到多个匹配的 Bean,Spring 会根据
@Qualifier注解或 Bean 的名称进一步筛选。 -
如果找不到匹配的 Bean,Spring 会抛出
NoSuchBeanDefinitionException。
-
-
注入依赖:
-
Spring 会将找到的
UserRepositoryBean 注入到userRepository字段中。 -
注入的过程是通过反射实现的,Spring 会直接设置
userRepository字段的值。
-
2.3 Bean 的初始化
-
调用初始化方法:
-
如果
UserService类实现了InitializingBean接口,Spring 会调用afterPropertiesSet()方法。 -
如果
UserService类配置了init-method,Spring 会调用指定的初始化方法。
-
-
Bean 准备就绪:
-
初始化完成后,
UserServiceBean 就可以被应用程序使用了。
-
2.4 Bean 的使用
-
获取 Bean:
-
当应用程序需要
UserServiceBean 时,可以通过@Autowired注入或ApplicationContext.getBean()方法获取。 -
例如:
-
@Autowired
private UserService userService;
-
调用方法:
-
当调用
userService.saveUser()时,Spring 会先找到userRepository字段的值(即UserRepository的实例),然后调用其save()方法。
-
3. 底层原理
3.1 反射机制
-
Spring 通过反射机制创建 Bean 的实例,并设置字段的值。
-
例如,
userRepository字段的注入是通过反射调用Field.set()方法实现的。
3.2 动态代理
-
如果
UserRepository被 AOP 增强(如事务管理或日志记录),Spring 会为其创建代理对象。 -
代理对象会拦截方法调用,并执行增强逻辑(如事务管理)。
3.3 依赖查找
-
Spring 容器通过
BeanFactory或ApplicationContext查找和获取 Bean。 -
例如,
@Autowired注解的背后是 Spring 调用BeanFactory.getBean()方法查找依赖的 Bean。
4. 代码示例
4.1 定义 Bean
// 用户仓库接口
public interface UserRepository {void save();
}// 用户仓库实现类
@Repository
public class UserRepositoryImpl implements UserRepository {@Overridepublic void save() {System.out.println("保存用户到数据库...");}
}// 用户服务类
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public void saveUser() {userRepository.save();}
}
4.2 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {@Autowiredprivate UserService userService;@Testpublic void testSaveUser() {userService.saveUser();}
}
4.3 运行结果
保存用户到数据库...
5. 总结
-
@Service:标识UserService类为 Spring Bean,并将其注册到容器中。 -
@Autowired:将UserRepository类型的 Bean 注入到userRepository字段中。 -
Spring 容器的运行流程:
-
扫描并注册 Bean。
-
创建 Bean 的实例。
-
解析并注入依赖。
-
调用初始化方法。
-
Bean 准备就绪,可以被应用程序使用。
-
相关文章:
再次理解 Spring 中的 IOC、DI、AOP 与多态
目录 引言 1. IOC(控制反转) 1.1 什么是 IOC? 1.2 IOC 的核心思想 1.3 IOC 的实现 2. DI(依赖注入) 2.1 什么是 DI? 2.2 DI 的实现方式 2.3 DI 的核心作用 3. AOP(面向切面编程&#x…...
rocky linux 与centos系统的区别
Rocky Linux 和 CentOS 都是基于 Red Hat Enterprise Linux(RHEL)的社区发行版,但两者在目标定位、更新策略和社区管理上有显著差异。以下是核心区别的详细对比: 一、背景与定位 特性Rocky LinuxCentOS起源由 CentOS 联合创始人…...
操作系统导论——第13章 抽象:地址空间
一、早期系统 从内存来看,早期的机器并没有提供多少抽象给用户。基本上,机器的物理内存如图13.1所示 操作系统曾经是一组函数(实际上是一个库),在内存中(在本例中,从物理地址0开始)&…...
C# 调用 VITS,推理模型 将文字转wav音频net8.0 跨平台
一、系统环境 操作系统:win10,win11 运行环境:dotnet8 工具:命令行,powershell 开源库:sherpa-onnx 二、工具和源码下载 开源库:https://k2-fsa.github.io/sherpa/onnx/index.html 运行环境下载 https://dotnet.microsoft.c…...
织梦DedeCMS如何获得在列表和文章页获得顶级或上级栏目名称
获得顶级或二级栏目的名称,都需要修改php文件,修改的文件【/include/common.func.php】将代码插入到这个文件的最下面即可; 一、获得当前文章或栏目的【顶级栏目】名称 1、插入顶级栏目代段 //获取顶级栏目名 function GetTopTypename($id…...
深度学习仓库代码结构认识
规范化深度学习代码仓库的目录结构和文件组织方式,以便于代码的管理、协作和复现性。 一种供参考的目录树结构: . ├── README.md ├── requirements.txt ├── data/ ├── docs/ ├── logs/ └── src/├── configs/│ └── config.y…...
C#基于MVC模式实现TCP三次握手,附带简易日志管理模块
C#基于MVC模式实现TCP三次握手 1 Model1.1 ServerModel1.2 ClientModel1.3 配置参数模块1.4 日志管理模块1.4.1 数据结构1.4.1 日志管理工具类1.4.1 日志视图展示1.4.1.1 UcLogManage.cs1.4.1.2 UcLogManage.Designer.cs 2 视图(View)2.1 ViewServer2.1.…...
6、linux c 线程 -下
1. 线程的取消 意义 随时终止一个线程的执行。 函数 #include <pthread.h> int pthread_cancel(pthread_t thread); pthread_t thread:要取消的线程 ID。 返回值 成功时返回 0。 失败时返回非零错误码。 注意 线程的取消需要有取消点,…...
分布式算法:Paxos Raft 两种共识算法
1. Paxos算法 Paxos算法是 Leslie Lamport(莱斯利兰伯特)在 1990 年提出的一种分布式系统共识算法。也是第一个被证明完备的共识算法(前提是不存在恶意节点)。 1.1 简介 Paxos算法是第一个被证明完备的分布式系统共识算法。共识…...
什么是数据库监控
数据库监控是一个综合的过程,涉及观察、分析和优化组织内数据库的性能、运行状况和可用性。通过持续跟踪查询执行时间、CPU使用率、内存消耗和存储I/O等指标,数据库监控使管理员能够主动识别和解决潜在问题。这种对数据库操作的实时可见性对于确保应用程…...
Java学习总结-泛型
什么是泛型? 定义 类、接口、方法时,同时声明了一个或多个类型变量(如:<E>),称为泛型类、泛型接口、泛型方法、他们统称为泛型。public class ArrayList<E>{ }。 有什么作用呢…...
基于深度学习的相位调制算法步骤
1.构建网络结构 2.制作数据集 3.训练网络 4.引入评价指标 5.迭代优化 总结 通过以上步骤,可以实现基于深度学习的相位调制算法: 使用 U-Net 构建神经网络。 生成数据集并训练网络。 使用训练好的网络预测相位分布。 通过相关系数 γ 评估调制效果&…...
curl使用报错error LNK2001: 无法解析的外部符号 __imp__CertCloseStore@8
使用curl静态库libcurl_a.lib 时报错,内容如下: 1>libcurl_a.lib(openssl.obj) : error LNK2001: 无法解析的外部符号 __imp__CertCloseStore8 1>libcrypto.lib(libcrypto-lib-e_capi.obj) : error LNK2001: 无法解析的外部符号 __imp__CertClose…...
Go语言的基础类型
一基础数据类型 一、布尔型(Bool) 定义:表示逻辑真 / 假,仅有两个值:true 和 false内存占用:1 字节使用场景:条件判断、逻辑运算 二、数值型(Numeric) 1. 整数类型&…...
动力保护板测试仪:电池安全的坚实守护者
在新能源技术日新月异的今天,电池作为各类电子设备的心脏,其安全性与可靠性成为了行业内外关注的焦点。而动力保护板,作为电池系统中的重要组成部分,承担着精准调控电池充放电、防止电池过充、过放、短路等危险情况的重任。然而&a…...
Lineageos 22.1(Android 15)制定应用强制横屏
一、前言 有时候需要系统的某个应用强制衡平显示,不管他是如何配置的。我们只需要简单的拿到top的Task下面的ActivityRecord,并判断包名来强制实现。 二、调整wms com.android.server.wm.DisplayRotation /*** Given an orientation constant, return…...
【Python】【PyQt5】设置事件绑定(例为按钮点击显示提示框)
前言 上篇文章我们讲了如何创作一个UI界面,并将其使用代码显示出来,这篇文章我们来讲讲事件的绑定 为增加文章趣味性,此篇文章我们将以点击窗口中的按钮来后并显示一个提示框 修改上次代码(优化) 上篇文章我所讲的要…...
node-ddk, electron组件, 自定义本地文件协议,打开本地文件
node-ddk 文件协议 https://blog.csdn.net/eli960/article/details/146207062 也可以下载demo直接演示 http://linuxmail.cn/go#node-ddk 安全 考虑到安全, 本系统禁止使用 file:/// 在主窗口, 自定义文件协议,可以多个 import main, { NODEDDK } from "node-ddk/m…...
SpringBoot-3-JWT令牌
目录 引入 引入依赖 拦截器 创建工具类 创建拦截器的包及拦截器 注册拦截器 修改一下登录成功后token的代码 测试 引入 试想一下,以上我们的访问都是直接访问对应的接口。比如说用户登录的时候就访问登录的接口。 那么如果有人他不访问你的登录接口&#…...
ChatGPT vs DeepSeek vs Copilot vs Claude:谁将问鼎AI王座?
李升伟 整理 2025年的人工智能领域创新涌动,ChatGPT、DeepSeek、Copilot和Claude四大模型各领风骚。这些AI系统各具特色,分别专注于编程、创意写作、技术推理和AI伦理等不同领域。本文将深入解析这些AI模型的功能特性及其优势领域。 核心AI模型解析 C…...
git使用经验(一)
git使用经验(一) 我之前已经下载了别人的代码,我想在此基础上进行修改,并移动到自己的私有仓库,方便上传到自己的私有仓库自己进行版本控制 git clone下来别人的代码,删除有关git的隐藏文件 进入到自己的…...
文件上传的小点总结
1.文件上传漏洞 服务器端脚本语言对上传文件没有严格的验证和过滤,就可以给攻击者上传恶意脚本文件的可能。 文件上传检测绕过: 简单思路:(这里以前端上传图片为例) ①开启phpstudy,启动apache即可&…...
基于WebRtc,GB28181,Rtsp/Rtmp,SIP,JT1078,H265/WEB融合视频会议接入方案
智能融合视频会议系统方案—多协议、多场景、全兼容的一站式视频协作平台 OvMeet,LiveMeet针对用户核心痛点实现功能与用户价值 ,Web平台实现MCU多协议,H265/H264等不同编码监控,直播,会议,调度资源统一融合在一套界…...
Python常用库全解析:从数据处理到机器学习
适合人群:Python初学者 | 数据分析师 | 机器学习爱好者 目录 一、NumPy:科学计算的核心库 1. 核心功能 2. 应用领域 3. 常用方法示例 二、Pandas:数据分析的瑞士军刀 1. 核心功能 2. 应用领域 3. 常用方法示例 三、Matplotlib&#…...
基于漂浮式海上风电场系统的浮式风力发电机matlab仿真
目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于漂浮式海上风电场系统的浮式风力发电机matlab仿真,通过MATLAB数值仿真对浮式风力发电机的性能做模拟与仿真。 2.系统仿真结果 3.核心程序与模型 版本&#x…...
深入探索ArkUI中的@LocalBuilder装饰器:构建高效可维护的UI组件
在ArkUI框架中,组件化开发是提升代码复用性和维护性的关键手段。随着项目复杂度的增加,开发者常常面临如何在保持组件封装性的同时,灵活处理组件内部逻辑的问题。传统的Builder装饰器虽然提供了强大的自定义构建能力,但在某些场景…...
【QA】外观模式在Qt中有哪些应用?
1. QWidget及其布局管理系统 外观模式体现 QWidget 是Qt中所有用户界面对象的基类,而布局管理系统(如 QVBoxLayout、QHBoxLayout、QGridLayout 等)就像是一个外观类。客户端代码(开发者编写的界面代码)通常不需要直接…...
在ASP.NET Core中使用NLog:配置与性能优化指南
在ASP.NET Core中使用NLog:配置与性能优化指南 在ASP.NET Core中使用NLog:配置与性能优化指南1. 安装NLog包2. 基础配置2.1 创建nlog.config文件2.2 程序启动配置 3. 在代码中使用日志4. 性能优化配置4.1 异步日志处理4.2 自动清理旧日志4.3 缓冲写入优化…...
yaffs
YAFFS(Yet Another Flash File System)是专为NAND闪存设计的日志结构文件系统,其核心原理围绕NAND闪存的特性优化数据管理。以下是其关键原理的详细说明: 1. NAND闪存适配 写入限制:NAND闪存需按页写入(通…...
快速查询手机是否处于联网状态?
手机是否处于联网状态对于我们日常生活中的沟通、工作和娱乐都至关重要。有时候我们需要迅速了解一个手机号码的在网状态,例如是正常使用、停机、不在网等。而要实现这一功能,我们可以利用挖数据平台提供的在线查询工具,通过API接口来查询手机…...
