目录
1. @Component、@Service、@Repository 的作用
3. @Component、@Service、@Repository 和 @Autowired 的区别
引言
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 {
@Autowired
private 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 {
@Override
public void save() {
System.out.println("保存用户到数据库...");
}
}
// 用户服务类
@Service
public class UserService {
@Autowired
private 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 {
@Autowired
private UserService userService;
@Test
public 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 {
@Autowired
private UserRepository userRepository;
}
@Repository
public class UserRepositoryImpl implements UserRepository {
// 实现代码
}
3.基于Java配置:使用@Configuration和@Bean注解在配置类中显式声明Bean。例如:
@Configuration
public class AppConfig {
@Bean
public 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 {
@Autowired
private 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 会将找到的
UserRepository
Bean 注入到userRepository
字段中。注入的过程是通过反射实现的,Spring 会直接设置
userRepository
字段的值。
2.3 Bean 的初始化
调用初始化方法:
如果
UserService
类实现了InitializingBean
接口,Spring 会调用afterPropertiesSet()
方法。如果
UserService
类配置了init-method
,Spring 会调用指定的初始化方法。
Bean 准备就绪:
初始化完成后,
UserService
Bean 就可以被应用程序使用了。
2.4 Bean 的使用
获取 Bean:
当应用程序需要
UserService
Bean 时,可以通过@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 {
@Override
public void save() {
System.out.println("保存用户到数据库...");
}
}
// 用户服务类
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void saveUser() {
userRepository.save();
}
}
4.2 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testSaveUser() {
userService.saveUser();
}
}
4.3 运行结果
保存用户到数据库...
5. 总结
@Service
:标识UserService
类为 Spring Bean,并将其注册到容器中。@Autowired
:将UserRepository
类型的 Bean 注入到userRepository
字段中。Spring 容器的运行流程:
扫描并注册 Bean。
创建 Bean 的实例。
解析并注入依赖。
调用初始化方法。
Bean 准备就绪,可以被应用程序使用。