本文将系统梳理Spring框架的几个核心概念,涵盖ApplicationContext与BeanFactory的区别、Bean的作用域与生命周期、基于注解的IoC/DI,以及AOP的原理与配置,旨在帮助开发者构建扎实的Spring基础。
一、ApplicationContext 与 BeanFactory 的区别
二者都是Spring IoC容器的核心接口,用于管理Bean的生命周期和依赖关系。
-
BeanFactory :IoC容器的顶级接口 。它采用了延迟加载策略,即在第一次调用
getBean()方法获取对象时,才会实例化该Bean。 -
ApplicationContext :它是
BeanFactory的子接口 ,在继承了其所有功能的基础上,增加了更多企业级特性(如国际化、事件发布等)。其关键区别在于,它采用了预加载(饿汉式)策略 ,在容器启动、加载配置文件时,就会创建并初始化所有的单例Bean。
简单比喻 :BeanFactory是基础版工厂,按需生产;ApplicationContext是增强版工厂,开业前就备好了所有招牌产品,启动更耗时但运行时响应更快。在绝大多数现代Spring应用中,我们直接使用功能更强大的ApplicationContext。
二、Spring Bean 的5个作用域
Spring为Bean定义了多种作用域,以适配不同的使用场景。
-
五种核心作用域:
-
singleton (默认):在Spring IoC容器中,一个Bean定义只对应一个实例 。所有对该Bean的请求都返回同一个共享对象。其本质是容器内部维护的一个
Map。 -
prototype :每次请求(调用
getBean())时,Spring都会创建一个新的Bean实例 。其本质是每次调用newInstance()。 -
request :每次HTTP请求都会创建一个新的Bean,仅适用于Web应用。
-
session :在一个HTTP Session的生命周期内,一个Bean定义对应一个实例,仅适用于Web应用。
-
application :在一个
ServletContext的生命周期内,一个Bean定义对应一个实例,仅适用于Web应用。
-
-
如何指定作用域:
在XML配置中,通过
scope属性指定。<bean id="myBean" class="com.example.MyBean" scope="prototype"/>使用注解时,使用
@Scope注解。@Component @Scope("prototype") public class MyPrototypeBean { } -
单例与多例的使用场景:
-
使用单例(singleton) :无状态的、线程安全的对象。例如:各种
Service、Dao、工具类、以及所有的工厂类(如SqlSessionFactory)。这是最常用的作用域。 -
使用多例(prototype) :有状态的、非线程安全的对象。例如:数据库连接
Connection、MyBatis的SqlSession(旧版)、每次交互需要独立状态的对象。
-
三、Spring Bean 的生命周期
Bean的生命周期指其从创建到销毁的整个过程,Spring允许我们在特定节点插入自定义逻辑。
-
生命周期关键方法示例:
我们可以通过配置
init-method和destroy-method来指定初始化和销毁时调用的方法。Bean类定义:
public class UserServiceImpl { public UserServiceImpl() { System.out.println("1. 调用构造方法实例化Bean..."); } public void setDependency(...) { System.out.println("2. 调用set方法进行依赖注入..."); } public void myInit() { System.out.println("3. 调用init-method进行自定义初始化..."); } public void myDestroy() { System.out.println("5. 调用destroy-method进行自定义销毁..."); } }XML配置:
<bean id="userService" class="com.hg.UserServiceImpl" init-method="myInit" destroy-method="myDestroy"/> -
完整生命周期流程:
-
对于单例Bean(singleton):
- 启动容器 -> 2. 调用构造方法 实例化 -> 3. 调用setter 注入依赖 -> 4. 调用init-method -> [容器运行中] -> 5. 关闭容器 -> 6. 调用destroy-method
单例Bean的初始化在容器启动时完成,销毁在容器关闭时进行。
-
对于多例Bean(prototype):
- 调用
getBean() -> 2. 调用构造方法 实例化 -> 3. 调用setter 注入依赖 -> 4. 调用init-method -> [返回对象给使用者] -> 5. JVM垃圾回收 -> 6. 容器不管理其销毁,destroy-method不会被Spring调用。
多例Bean的初始化在每次获取时完成,其生命周期由JVM管理,Spring容器不负责销毁。
- 调用
-
四、基于注解的 IoC 与 DI
注解极大地简化了Spring的配置。
-
配置步骤:
-
步骤1:在配置文件中启用组件扫描。
<context:component-scan base-package="com.hg"/> -
步骤2 (IoC):在类上使用组件注解,将其声明为Spring Bean。
@Repository // 等价于<bean id="userDaoImpl" class="..."> public class UserDaoImpl implements UserDao { } -
步骤3 (DI):在需要依赖的字段/方法上使用自动装配注解。
@Service public class UserServiceImpl implements UserService { @Autowired // 按类型自动查找并注入UserDao的Bean // @Resource(name="userDaoImpl") // 也可按名称注入 private UserDao userDao; }
-
-
常用注解:
-
IoC (Bean声明):
-
@Repository:标注数据访问层 (DAO) 组件。 -
@Service:标注业务逻辑层 (Service) 组件。 -
@Controller:标注控制层 (Web Controller) 组件。 -
@Component:标注不属于以上三层的通用组件。 -
@Scope:指定Bean的作用域,如@Scope("prototype")。
-
-
DI (依赖注入):
-
@Autowired:Spring提供,默认按类型 (byType)注入。可配合@Qualifier指定名称。 -
@Resource:JSR-250规范,默认按名称 (byName)注入。name属性指定Bean id。 -
@Value:注入普通值(基本类型、String)或SpEL表达式,常用于注入配置文件属性。
-
-
五、AOP (面向切面编程)
-
为什么需要AOP?
考虑为所有Service方法添加日志记录。若不使用AOP,需要在每个方法中手动添加日志代码,导致代码冗余 、维护困难、核心业务逻辑不清晰。
public class Service { public void add() { System.out.println("方法开始:" + new Date()); // 重复的日志代码 // ... 核心业务逻辑 ... System.out.println("方法结束:" + new Date()); // 重复的日志代码 } // 其他每个方法都需要写一遍日志 }AOP的目标就是将此类横切关注点(日志、事务、安全等)从业务逻辑中剥离出来。
-
什么是AOP?
AOP (Aspect Oriented Programming) 面向切面编程:一种编程范式,允许将程序中重复的代码(横切关注点)抽取 出来,形成独立的"切面",然后通过动态代理 技术,在不修改源代码的前提下,将切面逻辑织入到目标方法中,从而对程序功能进行增强。
-
AOP的核心概念
-
连接点 (Joinpoint) :程序执行过程中明确的点,如方法调用、异常抛出。在Spring AOP中,通常指方法的执行。
-
切点 (Pointcut) :一个表达式,用于匹配 一个或多个连接点(即,哪些方法需要被增强)。例如:
execution(* com.hg.service.*.*(..))。 -
增强/通知 (Advice) :在切点处执行的具体逻辑(即,要"搞"的事情,如打印日志)。它定义了增强的时机(前置、后置等)和行为。
-
切面 (Aspect) :增强 和切点的结合。它定义了"在何处(切点)"执行"何种增强(通知)"。
-
-
五种通知类型及其执行顺序:
try { // 1. 前置通知 (@Before) // ... 执行目标方法 (Joinpoint) ... // 2. 后置/返回通知 (@AfterReturning) - 方法正常返回后执行 } catch (Exception e) { // 3. 异常通知 (@AfterThrowing) - 方法抛出异常后执行 } finally { // 4. 最终通知 (@After) - 类似于finally块,无论如何都执行 } // 5. 环绕通知 (@Around) - 能控制目标方法是否执行,最强大。 -
基于XML的AOP配置
<!-- 1. 定义增强(通知)Bean --> <bean id="logAdvice" class="com.hg.advice.LogAdvice"/> <!-- 2. 配置AOP --> <aop:config> <!-- 2.1 定义切点表达式 --> <aop:pointcut id="servicePointcut" expression="execution(* com.hg.service.*.*(..))"/> <!-- 2.2 定义切面 --> <aop:aspect ref="logAdvice"> <!-- 将增强应用到切点 --> <aop:before method="beforeLog" pointcut-ref="servicePointcut"/> </aop:aspect> </aop:config> -
基于注解的AOP配置
@Aspect // 1. 声明这是一个切面类 @Component // 2. 让Spring管理它 public class LogAdvice { // 3. 定义切面:在何处执行何种增强 @Before("execution(* com.hg.service.*.*(..))") // 切点表达式直接写在注解里 public void beforeLog() { System.out.println("前置通知: " + new Date()); } }在Spring配置文件中开启注解AOP支持:
<aop:aspectj-autoproxy/> -
切点表达式 (Pointcut Expression)
格式:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))-
*代表通配符。 -
..代表当前包及其子包,或代表任意参数。 -
示例:
-
execution(* com.hg.service.UserServiceImpl.addUser(..)):匹配UserServiceImpl的addUser方法。 -
execution(* com.hg.service.UserServiceImpl.*(..)):匹配UserServiceImpl的所有方法。 -
execution(* com.hg.service.*.*(..)):匹配com.hg.service包下所有类的所有方法(最常用)。
-
-
总结 :本文从容器基础(BeanFactory/ApplicationContext)、Bean管理(作用域、生命周期)、便捷开发(注解IoC/DI)到核心思想(AOP)系统回顾了Spring框架的基石。理解这些概念是掌握Spring生态和进行高效Java企业开发的关键。