Spring框架综述
Spring 框架是一个开源的 Java 平台,它被广泛用于创建企业级应用程序。它提供了一个全面的编程和配置模型,支持现代基于 Java 的企业应用程序 - 无论是在应用程序级别还是在底层 Java EE 构建块级别。
Spring 框架的核心理解包括以下几个方面:
- 依赖注入(Dependency Injection, DI):
- Spring 的核心特性之一是依赖注入,它是一种设计模式,用于实现控制反转(Inversion of Control, IoC)。
- 通过依赖注入,对象的依赖项(例如服务、配置)不需要由对象自己创建或查找,而是由外部容器(在 Spring 中叫做 IoC 容器)注入。
- 这种模式减少了组件间的耦合度,增强了模块的独立性,便于测试和维护。
- 面向切面编程(Aspect-Oriented Programming, AOP):
- AOP 允许开发者将横切关注点(如日志、事务管理、安全等)从业务逻辑中分离出来,从而提高代码的模块化。
- Spring AOP 模块提供了 AOP 支持,使用代理模式将这些关注点应用于程序的相应部分。
- 企业服务抽象(Enterprise Services Abstraction):
- Spring 提供了对许多企业服务的抽象,包括事务管理、消息服务、邮件服务和其他多种服务。
- 这种抽象允许开发者以一致的方式使用这些服务,同时保持对底层资源的控制。
- 数据访问框架:
- Spring 框架为数据访问提供了一致的模型,主要是通过其 DAO(Data Access Object)支持以及对 ORM(Object-Relational Mapping)框架的集成,例如 Hibernate、JPA等。
- 它还提供了资源管理、异常处理和模板方法(如 JdbcTemplate、HibernateTemplate 等)。
- 事务管理:
- Spring 支持声明式和编程式的事务管理,这使得事务管理在不同的环境中保持一致,并简化了事务管理代码。
- 模型-视图-控制器(Model-View-Controller, MVC):
- Spring Web MVC 框架提供了一个用于构建 Web 应用程序的 MVC 实现。
- 它支持 RESTful 应用程序的构建,并与 Spring 的其他特性(如依赖注入)集成。
- 轻量级容器:
- Spring IoC 容器是一个轻量级的容器,它管理应用程序中 bean 的生命周期和依赖关系。
- 这个容器是非侵入性的,意味着对象不需要依赖于 Spring 的 API。
- 模块化:
- Spring 框架是高度模块化的,开发者可以根据需要选择和使用特定的部分。
- 例如,如果只需要依赖注入和 AOP 功能,可以只使用 Spring Core 和 Spring AOP 模块。
- 集成:
- Spring 提供了与其他流行框架和库的良好集成,例如 MyBatis、Quartz、Thymeleaf 等。
Spring 框架的设计哲学是提供一个开放的、灵活的、全面的基础架构,支持各种不同的应用程序架构和编程模型。它的目标是使 Java 开发更容易、更快速、更安全,并促进良好编程实践,如编程到接口和依赖注入。
IOC
IOC的理念
控制反转(Inversion of Control, IoC)是一种设计原则,用于降低计算机代码之间的耦合关系。在传统的程序设计中,程序的流程由应用程序代码直接控制。例如,一个程序可能按照特定的顺序创建对象、管理它们的生命周期和依赖关系等。这种设计方式使得代码高度耦合且难以维护。
控制反转改变了程序流程的控制方式。应用程序代码不再直接控制每一个执行步骤,相反,它将控制权交给一个外部系统或框架。这意味着程序的高层策略将控制权委托给低层的组件。
以下是理解控制反转的几个关键点:
- 颠倒控制:
- 控制反转的核心概念是将控制权从程序代码转移至外部框架或容器。这意味着依赖关系的创建和绑定不再是由应用程序代码直接控制,而是由框架负责。
- 依赖注入:
- 依赖注入(Dependency Injection, DI)是控制反转的一种实现方式。在这种模式下,组件的依赖项不是由组件内部创建,而是由外部容器在运行时注入。这样,组件就不需要负责查找或创建它们所需的依赖。
- 服务查找:
- 另一种实现控制反转的方式是服务查找模式(也称为依赖查找)。在这种模式中,组件动态地从一个服务注册处查找依赖,而不是直接实例化依赖。
- 框架和库:
- 在使用框架时,控制权在很大程度上由框架本身持有,它决定了何时和如何调用应用程序代码。相比之下,使用库时,应用程序代码调用库中的方法,保持了控制权。
- 设计模式:
- 控制反转与几个设计模式有关,例如工厂模式、策略模式和模板方法模式。这些模式通过在组件之间建立抽象层,减少了代码的直接耦合。
- 优点:
- 控制反转促进了代码的解耦,使得组件之间的依赖关系更加灵活,易于管理。
- 组件更容易测试和重用,因为它们不依赖于具体的实现。
- 应用程序更容易适应变化,因为组件间的耦合降低了。
- 框架的典型例子:
- Spring 框架是控制反转的一个著名例子,它通过依赖注入和面向切面编程(AOP)实现了控制反转。
通过控制反转,开发者可以将注意力集中在业务逻辑上,而将对象管理的复杂性交给框架处理。这种方式改变了程序的结构和行为,帮助创建更加模块化、可测试和可维护的代码。
Spring的IOC
Spring 框架通过其核心容器实现了 IoC(Inversion of Control)机制,主要依靠依赖注入(Dependency Injection,DI)模式。以下是 Spring 实现 IoC 的关键步骤:
- 定义 Bean 配置:
- Spring 允许你通过 XML 配置文件、Java 注解或 Java 配置类定义 bean 及其依赖关系。
- Bean 是 Spring 管理的对象,可以是组件、服务、配置类等。
- 创建 Spring IoC 容器:
- 当应用启动时,Spring IoC 容器被实例化。它负责读取配置元数据,并创建和管理应用中的 bean。
- 容器的类型通常是 ApplicationContext 的实现,如 ClassPathXmlApplicationContext、AnnotationConfigApplicationContext 等。
- 实例化 Bean:
- 根据配置信息,IoC 容器将负责实例化对象。这涉及构造对象并注入必要的依赖。
- 容器使用反射创建 bean 实例。
- 依赖注入:
- IoC 容器将依赖注入到 bean 中,它通过使用以下方式完成:
- 构造器注入:容器在调用类的构造器创建 bean 实例时,将依赖对象作为构造器参数传递。
- 设置器注入:容器通过调用 bean 的 setter 方法来注入依赖。
- 字段注入:容器直接设置 bean 的字段上的依赖。这通常是通过反射完成的,尽管不推荐使用。
- 方法注入:除了 setter 方法外,容器还可以通过调用任何自定义方法来注入依赖。
- 这些依赖通常是其他 bean 的引用,但也可以是值类型(如字符串、整数等)。
- IoC 容器将依赖注入到 bean 中,它通过使用以下方式完成:
- 管理 Bean 生命周期:
- IoC 容器管理 bean 的整个生命周期。它不仅包括初始化和依赖注入,还包括调用生命周期回调(如 @PostConstruct 和 @PreDestroy 注解方法)、以及最终的 bean 销毁。
- 提供 Bean:
- 容器将已创建和组装好的 bean 提供给应用。通常,这是通过调用容器的 getBean() 方法完成的,或者在需要 bean 的组件中使用自动装配(@Autowired 注解)。
- 提供其他企业级服务:
- 除了基本的 IoC 功能外,Spring 还提供了许多企业级服务,如事务管理、安全框架集成和消息服务集成等,它们也都利用了 IoC 容器和依赖注入模式。
Spring IoC 容器的强大之处在于它的自动化和灵活性。开发者几乎不需要编程式地创建和管理对象及其依赖关系,而是可以将这些任务交给 Spring IoC 容器来处理,从而实现了应用组件之间的松耦合和更高的模块化。通过这种方式,Spring 支持了代码的易测试性、模块化和可重用性。
BeanFactory 和 ApplicationContext
BeanFactory
BeanFactory 是 Spring 框架中的一个核心接口,它定义了 Spring IoC 容器的最基本形式。BeanFactory 提供了依赖注入(DI)的功能,管理应用程序中的 beans,包括它们的创建、配置、获取以及管理它们的生命周期。下面是 BeanFactory 的基本工作原理:
- 配置元数据:
- BeanFactory 使用配置元数据,这些元数据可以是 XML 文件、Java 注解或 Java 代码。
- 配置元数据描述了如何创建和初始化 beans,以及它们之间的依赖关系。
- Bean 定义:
- BeanFactory 将配置元数据中定义的每个 bean 表示为一个 BeanDefinition 对象。
- BeanDefinition 包含了有关 bean 的必要信息,包括类类型、作用域(单例、原型等)、生命周期回调和依赖关系。
- Bean 创建:
- 当 BeanFactory 获取 bean 的请求时(通过 getBean 方法),它会检查对应的 BeanDefinition,并使用反射或工厂方法来创建 bean 实例。
- 依赖注入:
- 在 bean 实例化后,BeanFactory 会进行依赖注入。
- 依赖注入可以通过构造器注入、设置器注入或使用 Spring 支持的其他方法(如 @Autowired 注解)。
- 初始化:
- 创建和配置 bean 之后,BeanFactory 可能会调用初始化方法,例如那些通过 init-method 属性定义的方法或者实现了 InitializingBean 接口的 afterPropertiesSet 方法。
- 如果 bean 使用了 AOP 功能,这一阶段还可能涉及代理的创建。
- 延迟加载:
- BeanFactory 默认不会预先实例化单例 beans,它会在第一次调用 getBean 方法请求该 bean 时才创建实例。这被称为懒加载(延迟加载)。
- Bean 生命周期管理:
- BeanFactory 负责整个 bean 生命周期的管理,包括销毁时的清理工作。
- 对于定义了 destroy-method 的 beans,BeanFactory 在容器关闭时调用这些方法来进行清理。
虽然 BeanFactory 提供了 Spring IoC 容器的基本功能,但在实际开发中,更常用的是 ApplicationContext 接口,它是 BeanFactory 的子接口。ApplicationContext 提供了更多的高级特性,如事件发布、国际化支持、Web 应用程序上下文等。ApplicationContext 也负责预先实例化所有单例 beans,除非指定了懒加载,这通常让应用程序启动时花费更多时间,但请求处理过程中响应更快。
BeanFactory 是 Spring IoC 容器的基石,它通过提供精细的管理和配置 beans 的能力,支持了构建可扩展、可维护和可测试的 Java 应用程序。
ApplicationContext
ApplicationContext 是 Spring 框架中的一个核心接口,它是 BeanFactory 的子接口,提供了更多面向企业级应用的功能。ApplicationContext 提供了框架式的方式来管理 Java 应用程序中的 beans,并且通过依赖注入(DI)来组装这些 beans。以下是 ApplicationContext 的常见用法:
- 初始化 ApplicationContext:
- 你可以通过不同的方式来初始化 ApplicationContext,这取决于你的应用程序的类型和配置方式(XML,注解,Java 配置等)。例如:
- ClassPathXmlApplicationContext: 从类路径下的 XML 配置文件中加载上下文定义。
- FileSystemXmlApplicationContext: 从文件系统路径下的 XML 文件中加载上下文。
- AnnotationConfigApplicationContext: 从注解配置类中加载上下文。
- 你可以通过不同的方式来初始化 ApplicationContext,这取决于你的应用程序的类型和配置方式(XML,注解,Java 配置等)。例如:
示例代码初始化一个基于 XML 配置的 ApplicationContext:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
获取 Bean:
- 一旦 ApplicationContext 被初始化,你可以使用 getBean() 方法来获取 beans。
- getBean() 方法可以按照 bean 的 id、名称或者类型进行调用。
示例代码获取一个 bean:
MyBean myBean = ctx.getBean("myBean", MyBean.class);
自动装配:
- ApplicationContext 支持自动装配 beans 的依赖关系,这通常是通过 @Autowired 注解实现的。
示例代码使用 @Autowired 进行自动装配: ```java @Component public class MyComponent { @Autowired private MyDependency myDependency;
// … }
7. **使用配置类**:
- 对于基于 Java 的配置,使用 AnnotationConfigApplicationContext 与配置类一起使用。
8. 示例代码初始化基于 Java 配置的 ApplicationContext:
```java
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
- 关闭上下文:
- 当应用程序结束时,你应该关闭 ApplicationContext 以确保所有的资源都被适当地清理。如果你的上下文是 ConfigurableApplicationContext(几乎所有的 ApplicationContext 实现都是),你可以调用 close() 方法来关闭上下文。
示例代码关闭 ApplicationContext:
if (ctx instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) ctx).close();
}
访问应用程序服务:
- ApplicationContext 提供了许多内建服务,比如国际化(MessageSource)、事件发布(ApplicationEventPublisher)和环境抽象(Environment)等。
- 示例代码使用 MessageSource 获取国际化消息:
ApplicationContext 是 Spring 应用程序的中心,它不仅负责管理和配置 beans,还提供了许多企业级服务支持。使用 ApplicationContext 是创建可维护、可扩展和松耦合的应用程序的关键。String message = ctx.getMessage("message.key", null, Locale.ENGLISH);
AOP切面
面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,它旨在通过将应用程序中的横切关注点(cross-cutting concerns)与业务逻辑分离来增加模块性。横切关注点是那些影响多个类的问题,例如日志记录、事务管理、安全性、缓存等。AOP 通过引入称为“切面”的特殊类,来集中管理这些横切关注点。
AOP 切面的原理可以通过以下几个关键概念解释:
- 切面(Aspect):
- 一个切面是一个模块化的横切关注点实现。在 Spring AOP 中,切面可以是带有 @Aspect 注解的任何类。
- 连接点(Join Point):
- 连接点是应用程序执行过程中的某个特定点,例如方法调用或字段赋值操作。在 Spring AOP 中,一个连接点总是代表一个方法的执行。
- 通知(Advice):
- 通知是在切面的某个特定连接点上采取的动作。这些动作是在程序执行的某个时刻织入的,例如方法执行前、方法执行后、方法抛出异常后等。在 Spring AOP 中,通知可以是以下几种形式:
- 前置通知(Before advice):在方法执行之前执行。
- 后置通知(After returning advice):在方法正常执行之后执行。
- 异常通知(After throwing advice):在方法抛出异常后执行。
- 最终通知(After (finally) advice):在方法执行之后执行,无论方法退出是正常还是异常返回。
- 环绕通知(Around advice):在方法执行之前和之后执行,可以在方法执行前后自定义行为,甚至可以阻止方法的执行。
- 通知是在切面的某个特定连接点上采取的动作。这些动作是在程序执行的某个时刻织入的,例如方法执行前、方法执行后、方法抛出异常后等。在 Spring AOP 中,通知可以是以下几种形式:
- 切入点(Pointcut):
- 切入点是一个表达式,用于定义我们希望通知被应用的一组连接点。它匹配指定的执行点,例如特定方法的执行。在 Spring AOP 中,切入点可以使用切入点表达式语言(AspectJ 表达式语言)来定义。
- 织入(Weaving):
- 织入是将切面与其他应用程序类型或对象连接,以创建被通知对象的过程。织入可以在编译时(使用 AspectJ 编译器)、加载时或运行时发生。Spring AOP 默认在运行时通过代理对象来完成织入。
- 代理(Proxy):
- 在 Spring AOP 中,代理是通过使用 JDK 动态代理或 CGLIB 代理创建的。这个代理包装了目标对象,并在调用目标方法时提供横切逻辑的执行。例如,当你调用一个代理对象的方法时,它可能首先执行一个关联的前置通知,然后调用实际方法,最后执行后置通知。
通过使用 AOP 和切面,开发者可以将横切关注点的代码从业务逻辑中分离出来,从而使系统的设计更清晰,业务逻辑更纯粹,同时提高了代码的可重用性和可维护性。在 Spring 框架中,AOP 是通过声明或编程方式轻松集成的。