Spring 是一个强大的 Java 开发框架,广泛用于企业级应用程序开发,其主要作用是简化开发过程,提高代码的可维护性和可扩展性。以下是 Spring 的核心功能和作用的详细说明:
Spring 3种配置方式
- 基于XML的配置
基于注解的配置
常见注解:
- @Component:标记类为 Spring 的组件
- @Autowired:自动注入依赖
- @Qualifier:指定注入的具体实现
- @Value:注入简单值
- @Configuration 和 @Bean:定义配置类和 Bean。
基于Java的配置
@Configuration public class AppConfig { @Bean public MyBean myBean() { MyBean myBean = new MyBean(); myBean.setName("Spring Java Config"); return myBean; } } ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyBean myBean = context.getBean(MyBean.class);
自动组装
Spring 自动装配(Autowiring)是指 Spring 容器根据配置或注解,自动满足 Bean 之间的依赖关系,而不需要显式配置依赖关系。这可以简化开发并减少 XML 配置文件或手动编码的冗余。
自动组装的方式
No(默认值)
- 不需要自动组装,需要显示配置依赖
ByType
- 根据属性的类型在容器中查找匹配的Bean,并自动组装
ByName
- 根据属性名称在容器中查找与属性名称同名的 Bean,并自动装配
Constructor
- 根据构造方法的参数类型自动装配依赖
@Autowired(最常用)
- 根据注解自动装配
@Qualifier(多个子类组装父类, 用子类实现父类)
- 用于在多候选 Bean 中选择特定的 Bean。
基于注解的自动装配(推荐方式)
- 使用 @Autowired 自动装配
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
public void performAction() {
userRepository.save();
}
}
@Repository
public class UserRepository {
public void save() {
System.out.println("User saved!");
}
}
- 使用 @Qualifier 解决冲突
@Component
public class UserService {
@Autowired
@Qualifier("myUserRepository")
private UserRepository userRepository;
public void performAction() {
userRepository.save();
}
}
@Repository("myUserRepository")
public class UserRepository {
public void save() {
System.out.println("User saved!");
}
}
当容器中有多个同类型 Bean 时,可使用 @Qualifier 指定具体的 Bean。
依赖注入(DI)==> 对象的引用注入
依赖注入是一种设计模式,用于将对象的依赖通过外部传递方式注入,而不是在对象内部自己创建依赖。这种方式能够解耦对象与其依赖,提高代码的灵活性、可测试性和可维护性。
依赖注入的三种方式
- 构造器注入
在对象创建时通过构造器传递依赖。
public class UserService {
public final UserRepository userRepository;
public UserService userService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
- Setter注入
使用公共的 setter 方法在对象创建后设置依赖
public class UserService {
public final UserRepository userReposititory;
public void setUserRepository(UserRepository userReposititory) {
this.userReposititory = userReposititory;
}
}
- 字段注入(基于注解)
直接在字段上添加注解
public class userService() {
@Auotwired
private UserRepository userReposititory;
}
Spring 中依赖注入的实现
Spring IoC 容器是依赖注入的核心,通过 Bean 定义和生命周期管理 完成依赖的注入。
主要原理和流程
Bean的定义
- Spring 使用配置文件(如 XML 或 Java 配置类)或注解(如 @Component)定义 Bean
Bean的加载
- Spring 容器会扫描或解析配置,找到所有需要管理的 Bean,并将其存储在 BeanDefinition 对象中
Bean 的实例化:
- 根据 BeanDefinition,通过反射机制创建Bean的实例。
依赖解析:
- Spring 根据 Bean 的依赖关系,在容器中查找需要注入的依赖(例如,通过构造器、setter 方法或字段注入)。
注入依赖
- 使用反射或代理技术,将找到的依赖赋值到目标Bean中
初始化回调
- 如果Bean实现了InitializingBean接口,Spring会调用afterPropertiesSet方法
- 或者,检查是否有自定义的初始化方法(通过 @PostConstruct 标注)。
Bean的生命周期管理
- Spring 会根据 Bean 的作用域(如单例或原型)对其生命周期进行管理
底层原理
使用反射机制
Spring 在依赖注入时,主要通过反射来完成注入:
构造器注入:
- 使用Constructor类的newInstance方法
Constructor<?> constructor = clazz.getConstructor(paramTypes);
Object bean = constructor.newInstance(args);
Setter注入
- 使用Metod类的invoke方法
Method method = clazz.getMethod("setDependency", Dependency.class);
method.invoke(bean, dependency);
字段注入
- 使用Field类的set方法直接修改字段
Field field = clazz.getDeclaredField("dependency");
field.setAccessible(true);
field.set(bean, dependency);
BeanDefinition 和 BeanFactory
Spring 通过 BeanDefinition 和 BeanFactory 实现 Bean 的加载、实例化和依赖注入
BeanDefinition:包含Bean的元信息(类名、依赖、作用域)
BeanFactory:负责解析 BeanDefinition 并创建 Bean
注入依赖的主要逻辑:
- 解析Bean的依赖信息
- 在容器中找到依赖对象
- 使用反射注入
InitializingBean
- InitializingBean 是一个接口,它提供了一个回调方法,用于在 Spring 完成依赖注入后进行初始化操作
- 其主要作用是允许 Bean 在其属性设置完成后执行自定义的初始化逻辑
作用:
依赖注入后的初始化
- 在 Bean 的属性依赖注入完成后,afterPropertiesSet() 方法允许开发者执行额外的逻辑,例如验证属性值或初始化复杂逻辑
初始化逻辑集中管理
- 如果初始化逻辑较复杂,放在构造器中可能显得混乱,afterPropertiesSet() 提供了一个清晰的分离点
补充配置
- 用于检查依赖项是否正确配置,或者设置默认值
场景:
- 属性的校验和初始化
资源连接初始化
- 在依赖注入完成后,连接外部资源(如数据库连接、消息队列连接)。
预处理操作
- 加载配置文件,初始化缓存,或者进行一些预处理任务。
业务逻辑初始化
- 构建复杂对象,或者启动某些特定服务。
InitializingBean与其他初始化方式比较(@PostConstruct)
构造方法
- 构造器适合简单的初始化,但当依赖项注入完成后需要初始化时,afterPropertiesSet 更合适
自定义初始化方法
- 配置文件中可以指定初始化方法,功能与 afterPropertiesSet 类似,但更解耦,不依赖接口。
@PostConstruct 注解
- 在 Spring 3 之后推荐使用,功能等同于 InitializingBean,但不与 Spring 特定接口耦合,更加通用。
import jakarta.annotation.PostConstruct;
public class MyBean {
private String property;
public void setProperty(String property) {
this.property = property;
}
@PostConstruct
public void init() {
if (this.property == null || this.property.isEmpty()) {
throw new IllegalArgumentException("Property must be set");
}
System.out.println("PostConstruct: Initialization logic executed.");
}
}
注解扫描和代理
对于注解驱动的依赖注入(如 @Autowired):
注解扫描:
- Spring 的 AnnotationConfigApplicationContext 会扫描注解并生成 BeanDefinition
依赖注入:
- 使用 依赖注入后处理器 完成注解字段的依赖解析和注入
- 底层同样使用的是反射修改字段或调用 setter 方法。
循环依赖处理
Spring通过三级缓存解决Bean的循环依赖
- 一级缓存:完整的单例 Bean。
- 二级缓存:半成品 Bean(尚未完成依赖注入)。
- 三级缓存:用于存储 Bean 的对象工厂(ObjectFactory)。
流程:
- 创建半成品Bean放入二级缓存
- 如果依赖其他Bean,会优先从一级或者二级缓存中获取,避免重复创建
- 依赖注入完成后,将 Bean 移入一级缓存。
Bean有几种作用域
- singleton:单例,每一个bean只能创建一个实例
- protoType:原型,每次对该bean的请求调用都会创建一个实例
- request:请求,针对每次HTTP请求,都会穿件一个实例
- session:会话,在一个Http Session中,一个bean定义对应一个bean实例
- global session,在一个全局Http Session中,一个bean定义对应一个bean实例
单例Bean是线程安全的吗
单例 Bean 的特点:
- Spring 中的单例 Bean 是基于容器的实例共享模式,一个容器中只有一个 Bean 实例。
- 该实例可以被多个线程同时访问。
并发访问的问题:
- 如果 Bean 的方法是无状态的(只操作局部变量),则是线程安全的
- 如果 Bean 的方法依赖类级变量(成员变量),可能在多线程访问时导致数据不一致的问题。
如何让单例 Bean 线程安全?
- 避免使用成员变量
- 使用sychronized或者其他同步机制
- 使用 ThreadLocal
- 使用单例 Bean 注入原型 Bean,从而每次请求获取新实例。
注入依赖的优势
- 解耦:对象与依赖解耦,便于替换和扩展。
- 可测试性:便于注入 mock 对象,提高测试效率。
- 简化开发:开发者专注于业务逻辑,减少手动创建对象的繁琐。
面向切面编程(AOP)
AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,旨在通过分离关注点(Separation of Concerns)来增强代码的可维护性和复用性。
在 Spring 中,AOP 是一种动态代理技术,用于在不修改源代码的情况下为程序添加横切逻辑(Cross-Cutting Concerns)。常见的横切逻辑包括日志记录、安全验证、事务管理等。
AOP 作用:
增强代码复用性
- 把通用功能抽取成切面(Aspect),减少重复代码。
提高代码可维护性
- 横切逻辑集中管理,便于维护和调整。
动态增强类功能
- 无需修改现有业务逻辑,通过配置即可扩展功能。
业务解耦
- 使业务逻辑与横切逻辑分离,提升系统的模块化。
AOP的核心概念
- 切面(Aspect):增强的代码块,如日志记录,事务管理
通知(Advice):
- 前置通知:方法执行前
- 后置通知:方法执行后
- 返回通知:方法正确返回结果后执行
- 异常通知:方法抛出异常时执行
- 环绕通知:在方法执行前后都可以插入逻辑,最强大的通知类型
- 切点(PointCut):定义拦截规则,用于确定哪些方法/类需要应用切面逻辑。
- 目标对象(Target Object):被 AOP 增强的对象。
织入(Waving):
将切面应用到目标对象的过程,分为以下几个阶段
- 编译时(AspectJ)
- 类加载时(通过ClassLoader动态增强)
- 运行时(Spring AOP,基于动态代理)
代理(Proxy):
- AOP 动态生成的对象,用来执行目标对象的方法,并附加增强逻辑。
AOP的用法
- 基于xml的配置
- 基于注解的配置(推荐)
- 定义切面
@Aspect
@Component
public class LoggingAspect {
@PointCut("execution(*.com.example.service.*.*(..))")
public void logPointut(){}
@Before("logPointcut()")
public void beforeAdvice() {
System.out.println("Before executing method...");
}
// 后置通知
@After("logPointcut()")
public void afterAdvice() {
System.out.println("After executing method...");
}
// 环绕通知
@Around("logPointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before around...");
Object result = joinPoint.proceed(); // 调用目标方法
System.out.println("After around...");
return result;
}
}
- 目标对象
@Service
public class UserService {
public void getUser() {
System.out.println("Executing UserService.getUser()");
}
}
- 配置类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy // 启用 AOP
public class AppConfig {}
AOP底层实现原理
基于动态代理
- JDK动态代理:如果目标类实现了接口,Spring AOP 使用 java.lang.reflect.Proxy 动态生成代理类。
- CGLIB 动态代理:如果目标类没有实现接口,Spring AOP 使用 CGLIB(Code Generation Library)动态生成子类代理。
AOP流程
- 配置解析:Spring 解析 AOP 配置并创建代理类。
代理对象生成
- 如果目标类实现了接口,则创建 JDK 动态代理
- 否则,生成目标类的 CGLIB 子类代理
方法调用拦截
- 在代理类中,拦截对目标对象方法的调用
- 根据通知类型,选择执行目标方法前或后进行增强逻辑。
动态代理的核心接口:
- InvocationHandler(JDK 代理)
- MethodInterceptor(Spring 的 AOP 底层使用)
CGLIB 的工作方式:
- 创建目标类的子类,覆盖目标类方法。
- 在覆盖的方法中加入增强逻辑,调用 super 实现目标逻辑。
AOP适用的场景
- 日志
- 权限验证
- 监控
- 事务处理
- 缓存设计
声明式事务的管理
Spring声明式事务管理是通过 AOP 和 事务管理器 相结合实现的,它允许开发者通过注解或XML配置,透明地管理事务逻辑,从而简化编程流程,提高代码的清晰度和维护性。
实现方式
基于注解的声明式事务
- 使用 @Transactional 注解标注需要事务支持的方法或类
@Transactional
public void performTransaction() {
// 业务逻辑
}
事务配置细节
- 事务传播行为:决定当一个事务方法调用另一个事务方法时如何传播现有事务(如 REQUIRED, REQUIRES_NEW 等)。
- 隔离级别:控制事务对其他事务的可见性。
- 超时时间:定义事务的最长执行时间
- 回滚规则:指定哪些异常会触发事务回滚。
底层原理
1.AOP拦截与代理
Spring声明式事务的核心依赖于 AOP。通过 Spring AOP 拦截对事务方法的调用,并动态处理事务。
- 代理对象生成:
• Spring为带有 @Transactional 的类或方法创建代理对象。
• 代理对象拦截方法调用,在调用方法前后执行事务相关操作。 - 切面处理:
• 在事务方法执行前,开启事务。
• 在方法执行成功后提交事务。
• 在方法抛出异常时回滚事务。
2.TransactionInterceptor
TransactionInterceptor 是事务逻辑的核心实现类。它通过解析 @Transactional 配置,调用事务管理器实现事务操作
3.PlatformTransactionManager
PlatformTransactionManager 是 Spring 事务管理的核心接口,具体实现由事务管理器负责(如 DataSourceTransactionManager、JpaTransactionManager)。
关键方法:
- getTransaction(TransactionDefinition):开启事务。
- commit(TransactionStatus):提交事务。
- rollback(TransactionStatus):回滚事务。
4.事务上下文管理
Spring通过 TransactionSynchronizationManager 管理事务的上下文信息:
- 当前事务的状态。
- 绑定的数据库连接。
- 资源清理工作。
5.@Transactional 注解解析
Spring 使用 TransactionAttributeSource 来解析 @Transactional 的元数据,并将这些信息传递给 AOP 切面。
6. 方法调用流程
以 @Transactional 方法为例:
- 调用方法时,代理对象拦截请求。
- 解析 @Transactional 注解,获取事务配置信息。
- 通过事务管理器开启事务。
调用目标方法:
- 方法成功执行:提交事务。
- 方法抛出异常:回滚事务。
- 释放资源,重置事务上下文。
关键点
事务传播行为
事务传播行为定义了事务方法之间如何相互影响:
- REQUIRED(默认):如果存在事务,则加入当前事务,否则创建新事务。
- REQUIRES_NEW:始终创建一个新事务。
- NESTED:嵌套在当前事务中。
- 其他:SUPPORTS、NOT_SUPPORTED、MANDATORY 等。
事务回滚规则
- Spring 默认仅对 运行时异常(RuntimeException) 和 Error 回滚。
- 对于受检查异常(Checked Exception),需要显式配置:
集成持久化框架(ORM)
简化的Web开发(SpringMVC)
MVC是什么?
MVC(Model-View-Controller) 是一种软件设计模式,广泛用于构建用户界面应用程序。它将应用程序分为三个核心部分:模型(Model)、视图(View)和控制器(Controller),实现业务逻辑和用户界面的分离。
MVC 模式的三个核心部分
Model(模型)
- 表示应用程序的数据和业务逻辑。
- 负责处理与数据库或其他外部数据源的交互。
- 在 Web 应用中,通常与实体类或数据访问对象(DAO)相关联
View(视图)
- 表示用户界面,负责展示数据。
- 通常是 HTML 页面、模板文件或前端框架组件。
- View 从 Model 中获取数据,通过 Controller 的控制进行显示。
Controller(控制器)
- 负责处理用户请求,调用 Model 和 View。
- 接受用户输入,执行相应的业务逻辑,决定调用哪个 Model 和 View。
- 在 Web 应用中,Controller 通常对应处理 HTTP 请求的方法或类。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。