Spring 框架既可以从 广义 和 狭义 两个角度明白,下面讲解这两个层面的概念:
(本文重要讲解的是狭义上的spring,广义上的简朴概括)
1、spring 的含义
1. 广义上的 Spring
从广义上讲,Spring 是一个涵盖多个模块的企业级应用开发框架,它提供了从根本架构到复杂企业应用开发所需的全面办理方案。Spring 框架的模块化设计资助开发者在差别的场景中选择合适的模块或子项目。
广义的 Spring 包罗以下几个子项目:
- Spring Framework:最核心的模块,提供了 IoC(控制反转)和 AOP(面向切面编程)支持,是整个 Spring 体系的根本。
- Spring Boot:简化 Spring 应用开发的框架,提供自动配置、内嵌服务器等特性,大大淘汰了项目配置的复杂度。
- Spring Data:用于处理惩罚数据访问层,简化与数据库的交互,支持多种恒久化技术(如 JPA、MongoDB、Redis)。
- Spring Security:一个功能强大的安全框架,提供认证和授权的功能。
- Spring Cloud:用于微服务架构,提供服务注册与发现、负载均衡、配置管理等功能。
- Spring Batch:用于批处理惩罚任务,支持大数据量的批量利用。
- Spring Integration:用于企业应用集成,支持消息驱动的架构和异步通讯。
因此,广义上的 Spring 不仅仅是一个框架,而是一个生态系统,可以用于构建从小型应用到复杂分布式系统的各种项目。
2. 狭义上的 Spring
从狭义上讲,Spring 特指 Spring Framework,它是 Spring 生态系统中的核心部分,重要提供 IoC(控制反转)容器和 AOP(面向切面编程)功能。
狭义上的 Spring 重要包括以下几个模块:
- Spring Core:核心容器模块,提供了 IoC 和 DI(依赖注入)的功能,是 Spring 应用的根本。
- Spring AOP:提供面向切面编程的支持,资助开发者将横切关注点(如日志、事务)从业务逻辑中分离出来。
- Spring Context:提供上下文支持,是 IoC 容器的高级封装。
- Spring ORM:为与 Hibernate、JPA 等 ORM 框架集成提供支持。
- Spring MVC:用于构建 Web 应用的模型-视图-控制器框架。
狭义的 Spring 重要指围绕核心容器(IoC)与面向切面编程(AOP)的功能,它是企业级应用开发的根本,能够资助开发者通过解耦、简化配置等方式高效开发应用程序。
总结
- 广义上的 Spring 是一个完整的生态系统,包括 Spring Framework、Spring Boot、Spring Cloud 等多个子项目,涵盖了从根本应用到分布式系统开发的方方面面。
- 狭义上的 Spring 是指 Spring Framework,它是 Spring 生态的核心,重要提供 IoC、AOP 等核心功能。
下面会先简朴先容一下 Spring DAO 模块 和 Spring ORM 模块(我们重要讲解的是 ioc、依赖注入、aop相干内容)
1. Spring DAO(Data Access Object)
Spring DAO 模块重要用于简化对数据库的访问,特别是简化 JDBC(Java Database Connectivity) 编程。直接利用 JDBC 进行数据库利用通常会涉及到大量样板代码,比方创建连接、执行查询、处理惩罚异常、关闭资源等。而 Spring DAO 模块通过封装这些底层利用,提供了更简洁的 API。
核心功能:
- 简化 JDBC 编程:Spring DAO 通过 JdbcTemplate 等工具类,极大简化了数据库利用,不需要手动管理数据库连接和资源的关闭。
- 同一异常处理惩罚:Spring 将差别数据库访问技术(JDBC、Hibernate 等)的异常抽象成同一的异常层次布局,避免了捕获特定数据库的异常。
JdbcTemplate 是 Spring DAO 最常用的类,它可以执行 SQL 查询、插入、更新和删除利用,封装了底层的 JDBC API。
示例:利用 JdbcTemplate 进行数据库利用
- // 定义 JdbcTemplate bean
- @Autowired
- private JdbcTemplate jdbcTemplate;
- public void insertUser(User user) {
- String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
- jdbcTemplate.update(sql, user.getName(), user.getEmail());
- }
- public User findUserById(Long id) {
- String sql = "SELECT * FROM users WHERE id = ?";
- return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(User.class));
- }
复制代码 在上面的例子中,JdbcTemplate 资助我们省去了手动管理数据库连接和处理惩罚 SQL 异常的复杂工作,只需要编写简洁的 SQL 语句即可。
2. Spring ORM(Object-Relational Mapping)
Spring ORM 模块用于简化与 ORM(对象关系映射)框架的集成,比方 Hibernate、JPA(Java Persistence API)、MyBatis 等。ORM 框架用于将 Java 对象映射到数据库中的表,使得开发者可以通过操尴尬刁难象来进行数据库利用,而不是直接编写 SQL 语句。
Spring ORM 模块通过封装和简化 ORM 框架的配置和利用,使得它们能够无缝集成到 Spring 应用中。Spring ORM 不是自己实现 ORM,而是资助开发者更好地利用现有的 ORM 工具,如 Hibernate 或 JPA。
核心功能:
- 集成主流 ORM 框架:Spring ORM 可以与 Hibernate、JPA、MyBatis 等主流 ORM 框架进行集成,简化配置和事务管理。
- 简化恒久化利用:开发者可以利用 JPA 或 Hibernate 注解定义实体类,将它们与数据库中的表进行映射。
- 事务管理:Spring 提供了同一的事务管理机制,ORM 框架可以与 Spring 的事务管理器无缝集成。
示例:利用 JPA 结合 Spring ORM 进行数据库利用
- @Entity
- public class User {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- private String name;
- private String email;
- }
- // Spring Data JPA 提供的接口
- @Repository
- public interface UserRepository extends JpaRepository<User, Long> {
- List<User> findByName(String name);
- }
复制代码 在这个例子中:
- User 类是一个 JPA 实体类,它与数据库中的 users 表对应。每个字段(如 id, name, email)对应表中的一列。
- UserRepository 是一个数据访问接口,继承自 JpaRepository,可以自动生成常用的数据库利用方法,如生存、查询等。你甚至不需要写 SQL,只需要通过定义接口来利用数据库。
总结:
- Spring DAO 是直接利用数据库的模块,重要通过简化 JDBC 编程来进行 SQL 利用,它得当那些需要手动编写 SQL 的场景。
- Spring ORM 是与对象关系映射框架(如 Hibernate、JPA)集成的模块,适用于盼望利用对象利用数据库、淘汰 SQL 编写的场景。
简朴来说,Spring DAO 更倾向于手动管理 SQL,而 Spring ORM 则是通过映射 Java 对象与数据库表来进行利用。如果你的项目是以 ORM 框架为主,可以利用 Spring ORM;如果你需要更多的 SQL 自定义控制,可以利用 Spring DAO。
2、IoC(IoC, Inversion of Control)
1. IoC 的概念
IoC 是 Spring Framework 的核心理念。它通过将对象的创建和管理职责交给容器,使对象之间的依赖关系由外部容器来处理惩罚,从而解耦组件之间的关系。
传统的编程方式下,对象 A 需要依赖对象 B 时,通常由对象 A 直接创建或获取对象 B。比方:- public class A {
- private B b;
- public A() {
- b = new B(); // A 负责创建 B 对象
- }
- }
复制代码 这种方式的问题是,当需要改变对象 B 的实现或配置时,必须修改 A 的代码,从而增加了耦合度,降低了系统的灵活性。
将控制权从对象 A 手中交给外部的 IoC 容器,让容器负责创建和管理对象 B,并将它注入到对象 A 中。对象 A 不再关心 B 的创建过程,只需利用 B。这样,系统中的对象依赖关系就被 "反转" 了。
IoC(Inversion of Control) 是 Spring 框架的核心概念之一,它用于管理对象的生命周期和依赖关系。通过 IoC,Spring 框架接管了对象的创建和管理,使得应用程序的组件解耦,从而进步了代码的可维护性和可测试性。下面详细讲解 IoC 怎样管理 Bean 以及相干的概念。
2. IoC 容器 (spring 容器)
IoC 容器是 Spring 框架中的核心组件,它负责管理应用程序中的对象(即 Bean)。重要的 IoC 容器有两个:
- BeanFactory:根本容器,提供了基本的容器功能。
- ApplicationContext:继承自 BeanFactory,提供了更丰富的功能,如变乱传播、声明式事务管理等。常用的实现有 ClassPathXmlApplicationContext、FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext。
3. @Bean 注解的重要功能和用途
1. 定义 Bean
- 作用:@Bean 注解用于标记一个方法,使其返回的对象被 Spring 容器作为 Bean 管理。该方法的返回值将会作为 Bean 被注册到 Spring 容器中,并可以通过依赖注入来利用。
- 适用场景:当需要在 Java 配置类中手动创建和配置 Bean 时利用。它允许开发者更灵活地定义 Bean 的创建逻辑和初始化过程。
- 示例:
- @Configuration
- public class AppConfig {
-
- @Bean
- public MyBean myBean() {
- return new MyBean(); // 创建并返回一个 MyBean 实例
- }
- }
复制代码 在上述示例中,myBean() 方法利用 @Bean 注解标记,该方法返回的 MyBean 实例将被 Spring 管理,并可以在其他地方通过依赖注入来利用。
2. 自定义 Bean 配置
- 作用:通过 @Bean 注解,开发者可以在 Java 配置类中自定义 Bean 的初始化参数、配置属性等。可以利用方法参数来通报依赖。
- 示例:
- @Configuration
- public class AppConfig {
-
- @Bean
- public DataSource dataSource() {
- DriverManagerDataSource dataSource = new DriverManagerDataSource();
- dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
- dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
- dataSource.setUsername("user");
- dataSource.setPassword("password");
- return dataSource;
- }
- }
复制代码 在这个示例中,dataSource() 方法返回了一个配置好的 DataSource 实例,Spring 容器将管理该 Bean 并提供它的注入。
3. Bean 的生命周期管理
- 作用:@Bean 注解的方法支持配置 Bean 的生命周期,包括初始化和烧毁回调。可以利用 @Bean 注解的 initMethod 和 destroyMethod 属性指定初始化和烧毁方法。
- 示例:
- @Configuration
- public class AppConfig {
-
- @Bean(initMethod = "init", destroyMethod = "cleanup")
- public MyBean myBean() {
- return new MyBean(); // 创建并返回一个 MyBean 实例
- }
- }
复制代码 在这个示例中,MyBean 类的 init 方法会在 Bean 初始化后调用,而 cleanup 方法会在 Bean 烧毁前调用。
4. 配置 Bean 的作用域
- 作用:可以通过 @Bean 注解的 @Scope 注解指定 Bean 的作用域。比方,singleton、prototype 等。
- 示例:
- @Configuration
- public class AppConfig {
-
- @Bean
- @Scope("prototype")
- public MyBean myBean() {
- return new MyBean(); // 创建并返回一个 MyBean 实例
- }
- }
复制代码 在这个示例中,myBean 的作用域被设置为 prototype,每次哀求都会创建一个新的 MyBean 实例。 @Bean 注解时没有显式指定 scope,则利用的是 Spring 的默认作用域。Spring 的默认作用域是 singleton。
4. Bean 的定义与配置
1. Bean 的定义
Bean对象 是指被 Spring 容器 管理的对象。Bean 是 Spring 容器中的核心概念之一,它代表了一个受 Spring 管理的对象实例。Spring 提供了多种方式来定义 Bean:(spring 容器就是 ioc容器)
- XML 配置:在 XML 文件中定义 Bean 和它们的依赖关系。
- 注解配置:利用注解(如 @Component、@Service、@Repository、@Controller)自动注册 Bean,并通过 @Autowired 自动注入依赖。
- Java 配置:通过 @Configuration 注解的类和 @Bean 注解的方法定义 Bean。
a. XML 创建bean
在 XML 配置文件中定义 Bean 是 Spring 的传统方式。这种方式在 Spring 2.x 和之前版本中广泛利用,虽然现在注解和 Java 配置更常见,但 XML 配置依然有效。
示例 XML 配置:- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- <aop:config>
- <aop:pointcut id="userServiceMethods" expression="execution(* com.example.service.UserService.*(..))"/>
- <aop:aspect ref="loggingAspect">
- <aop:before method="logBefore" pointcut-ref="userServiceMethods"/>
- </aop:aspect>
- </aop:config> http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
- <bean id="myBean" >
-
- <property name="name" value="example"/>
- </bean>
- </beans>
复制代码
- id:Bean 的唯一标识符。
- class:Bean 实现的类名。
- property:配置 Bean 的属性。
b. 注解创建bean
注解配置是 Spring 2.5 引入的,提供了更加简洁的方式来定义和管理 Bean。
常用注解:
- @Component:通用的组件注解。
- @Service:用于服务层的 Bean。
- @Repository:用于数据访问层的 Bean。
- @Controller:用于控制器层的 Bean(MVC 模式下)。
示例注解配置:- @Component
- public class MyBean {
- @Value("example")
- private String name;
- // Getter 和 Setter
- }
复制代码
- @Component:标识 MyBean 是一个 Spring 管理的 Bean。
- @Value:注入属性值。
c. Java 创建bean
Java 配置是 Spring 3.0 引入的,通过 @Configuration 注解的类和 @Bean 注解的方法来定义 Bean。这种方式将配置逻辑与代码放在一起,进步了范例安全性和可重构性。
示例 Java 配置:- @Configuration
- public class AppConfig {
- @Bean
- public MyBean myBean() {
- return new MyBean("example");
- }
- }
复制代码
- @Configuration:标识这是一个配置类。
- @Bean:方法返回的对象会被注册为 Spring 容器中的 Bean。
5. Bean 的获取
Bean 的获取是指从 Spring 容器中获取已定义的 Bean 实例。Spring 提供了多种方法来获取 Bean:
a. 利用 ApplicationContext
ApplicationContext 是 Spring 容器的重要接口,通过它可以获取 Bean。
示例获取 Bean:- // 使用 XML 配置
- ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
- MyBean myBean = context.getBean("myBean", MyBean.class);
- // 使用 Java 配置
- ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
- MyBean myBean = context.getBean(MyBean.class);
复制代码
- getBean(String name, Class requiredType):通过 Bean 的名称和范例获取 Bean。
- getBean(Class requiredType):通过 Bean 的范例获取 Bean。
b. 利用 @Autowired 注解
@Autowired 注解用于自动注入依赖的 Bean。Spring 会自动查找容器中匹配的 Bean 并注入。(下面 依赖注入 的时间会讲)
示例自动注入:- @Component
- public class MyService {
- @Autowired
- private MyBean myBean;
- // 使用 myBean
- }
复制代码
- @Autowired:标识自动注入依赖的 Bean。Spring 根据范例自动注入对应的 Bean。
6. Bean 的生命周期
Spring 容器在创建、初始化和烧毁 Bean 时,会经过以下几个生命周期阶段:
- 实例化:根据配置创建 Bean 的实例。
- 填充属性:将配置中的属性注入到 Bean 中。
- 初始化:调用 Bean 的初始化方法(如果有配置)。
- 利用:Bean 在应用程序中被利用。
- 烧毁:当容器关闭时,调用 Bean 的烧毁方法(如果有配置)。
示例:初始化和烧毁方法
- @Component
- public class MyBean {
-
- @PostConstruct
- public void init() {
- // 初始化代码
- }
-
- @PreDestroy
- public void destroy() {
- // 销毁代码
- }
- }
复制代码 7. Bean 的作用域
Spring 支持多种 Bean 的作用域,决定了 Bean 的生命周期和可见性:
- Singleton(默认):整个应用程序中只有一个实例。
- Prototype:每次哀求都会创建一个新的实例。
- Request:每次 HTTP 哀求创建一个新的 Bean 实例(只在 Web 应用中有效)。
- Session:每个 HTTP 会话创建一个新的 Bean 实例(只在 Web 应用中有效)。
- GlobalSession:每个全局 HTTP 会话创建一个新的 Bean 实例(只在 Portlet 应用中有效)。
示例:指定 Bean 的作用域
- @Component
- @Scope("prototype")
- public class MyBean {
- // 每次注入都创建新的实例
- }
复制代码 3、依赖注入
1. 依赖注入概述
依赖注入 是一种设计模式,它将对象的依赖关系从对象的内部管理转移到外部容器(如 Spring)。通过这种方式,Spring 容器负责创建和管理对象的依赖关系,从而降低组件之间的耦合,进步应用的灵活性和可测试性。
2. Bean 的定义与依赖注入
在 Spring 中,Bean 的定义和依赖注入有多种方式,可以通过 XML 配置、注解或 Java 配置来完成。
a. XML 配置中的依赖注入
XML 配置 是 Spring 的传统方式,通过 XML 文件定义 Bean 的属性和依赖关系。
示例 XML 配置:- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- <aop:config>
- <aop:pointcut id="userServiceMethods" expression="execution(* com.example.service.UserService.*(..))"/>
- <aop:aspect ref="loggingAspect">
- <aop:before method="logBefore" pointcut-ref="userServiceMethods"/>
- </aop:aspect>
- </aop:config> http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
- <bean id="myBean" >
-
- <property name="name" value="example"/>
- </bean>
- </beans>
复制代码
- 元素定义了一个 Bean,此中 id 是 Bean 的唯一标识,class 是 Bean 实现的类。
- 元素用于设置 Bean 的属性值。name 是属性的名称,value 是属性的值,ref 指定引用的其他 Bean(注入 bean 对象)。
b. 注解配置中的依赖注入
注解配置 提供了更为简洁和灵活的方式来定义 Bean 和注入依赖。它利用注解来标识 Bean 和依赖关系。
示例注解配置:- @Component
- public class MyBean {
- @Value("example")
- private String name;
- // Getter 和 Setter
- }@Componentpublic class MyService { @Autowired private MyBean myBean; // 自动注入 // 利用 myBean}
复制代码
- @Component:标识 MyBean 和 MyService 是 Spring 管理的 Bean。
- @Autowired:自动注入 MyBean 到 MyService 中。Spring 根据范例自动查找和注入 MyBean 实例。
- @Value:注入属性值。
c. Java 配置中的依赖注入
Java 配置 允许利用 Java 类来定义 Bean 和注入依赖。这种方式将配置和代码放在一起,提供了范例安全性。
示例 Java 配置:- @Configuration
- public class AppConfig {
- @Bean
- public MyBean myBean() {
- return new MyBean("example");
- }
- @Bean
- public MyService myService() {
- return new MyService(myBean()); // 通过构造函数注入
- }
- }
复制代码
- @Configuration:标识一个配置类,该类用于定义 Bean。
- @Bean:定义 Bean 的实例,并可以通过方法参数注入其他 Bean。
3. 依赖注入的方式
依赖注入的方式的不通 重要体现在在 @Autowired注解 的位置不通
a. 构造器注入
通过构造函数将依赖注入到 Bean 中。构造器注入确保了 Bean 在创建时就有所有的依赖项。
示例:- @Component
- public class MyService {
- private final MyBean myBean;
- @Autowired
- public MyService(MyBean myBean) {
- this.myBean = myBean;
- }
- // 使用 myBean
- }
复制代码
- 构造器注入 优点:依赖项是不可变的,强制要求在 Bean 创建时提供所有必需的依赖项,适用于必须的依赖项。
b. 属性注入
通过 Setter 方法或字段直接注入依赖。
示例:Setter 方法注入:- @Component
- public class MyService {
- private MyBean myBean;
- @Autowired
- public void setMyBean(MyBean myBean) {
- this.myBean = myBean;
- }
- // 使用 myBean
- }
复制代码 示例:字段注入:- @Component
- public class MyService {
- @Autowired
- private MyBean myBean;
- // 使用 myBean
- }
复制代码
- Setter 方法注入 和 字段注入 优点:代码简洁,允许依赖项在 Bean 创建后进行设置,适用于可选的依赖项。
c. 方法注入
通过普通方法注入依赖,通常用于更灵活的场景。
示例:- @Component
- public class MyService {
- private MyBean myBean;
- @Autowired
- public void init(MyBean myBean) {
- this.myBean = myBean;
- }
- // 使用 myBean
- }
复制代码
- 方法注入:允许在 Bean 创建后注入依赖,适用于复杂的初始化逻辑。
4. 依赖注入的自动装配
增补内容:
- 默认 Bean 名称:
- @Component、@Service、@Repository、@Controller:如果没有指定 value 属性,Spring 会利用类名的首字母小写情势作为 Bean 的默认名称。比方,MyComponent 的默认 Bean 名称是 myComponent。
- @Bean:如果没有指定 name 属性,Bean 名称将是方法名。比方,myBean() 方法定义的 Bean 名称是 myBean。
a. 按范例自动装配
Spring 根据 Bean 的范例自动匹配依赖项。
示例:- @Component
- public class MyService {
- @Autowired
- private MyBean myBean; // 按类型自动注入 (MyBean这个类)
- // 如果 MyBean 是一个接口, 并且它有两个实现类都注册为 bean 了, 那么根据类型自动注入就会报错, 我们需要用 按照名称自动装配
- }
复制代码
- 优点:简化了依赖注入的配置。
- 缺点:当有多个匹配的 Bean 时,大概会引发冲突。
b. 按名称自动装配
Spring 根据 Bean 的名称进行注入。
示例:- @Component
- public class MyService {
- @Resource(name = "myBean")
- private MyBean myBean; // 按名称注入
- }
复制代码
- 优点:可以通过 Bean 名称明确指定注入对象。
- 缺点:需要在配置中明确指定 Bean 的名称。
c. 利用 @Qualifier 注解
当有多个符合条件的 Bean 时,可以利用 @Qualifier 注解指定详细的 Bean。
示例:- @Component
- public class MyService {
- @Autowired
- @Qualifier("myBean1")
- private MyBean myBean; // 指定具体的 Bean
- }
复制代码
- 优点:在多个候选 Bean 中指定详细的 Bean 进行注入。
4、aop
1. AOP(面向切面编程,Aspect-Oriented Programming)
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将 横切关注点(cross-cutting concerns)与核心业务逻辑分离。横切关注点是指那些影响多个模块的功能,比方 日志记录、性能监控、事务管理、权限控制 等。这些功能通常与业务逻辑无关,但需要在多个地方执行。
通过 AOP,开发者可以将这些功能提取出来,以模块化的方式进行管理,而不是将这些功能分散到业务代码中,从而进步代码的可读性和可维护性。
Spring AOP 是 Spring 框架的一部分,专门用于简化横切关注点的处理惩罚。它允许开发者通过 声明式 方式(利用注解或 XML 配置)来定义切面,而不需要手动修改原有的业务代码。
2. AOP 的核心概念
在 AOP 中,有几个关键的概念需要明白:
- Aspect(切面)
切面是横切关注点的模块化实现。一个切面通常包罗多个横切功能,比方日志记录或事务管理。切面由 通知(advice) 和 切点(pointcut) 构成。
- Join Point(连接点)
连接点是程序执行过程中的某个点,在 Spring 中通常是方法调用(还可以是异常抛出、字段访问等)。切面可以在这些连接点执行某些利用。
- Pointcut(切点)
切点是一个定义了哪些连接点会被拦截的表达式。切面会在匹配切点的连接点上执行。
- Advice(通知)
通知定义了切面在连接点上执行的详细利用。通知可以在方法执行的 之前、之后、抛出异常时 或 终极 执行。
Spring 提供了以下几种常见的通知范例:
- Before Advice:在方法执行之前执行通知。
- After Returning Advice:在方法乐成执行之后执行通知。
- After Throwing Advice:在方法抛出异常后执行通知。
- After (Finally) Advice:无论方法是否乐成执行,都会执行通知。
- Around Advice:在方法执行的 前后 都可以执行的通知。
- Target(目标对象)
被 AOP 代理的对象。切面功能终极是应用在目标对象的某些方法上的。
- Proxy(代理对象)
AOP 框架通过为目标对象生成代理对象来实现切面的功能。Spring AOP 基于 动态代理 机制,在运行时为目标对象生成一个代理类,拦截方法调用,并在调用之前、之后或抛出异常时执行通知。
- Weaving(织入)
织入是将切面应用到目标对象并创建代理对象的过程。Spring AOP 是 运行时织入,即在运行时动态创建代理对象。
Spring AOP 的实现方式
在 Spring 中,AOP 可以通过以下两种方式实现:(我们重要讲解的是注解方法实现)
- 基于注解的方式
这种方式最为常见,开发者可以通过注解来声明切面、通知和切点。利用起来简洁且直观。
示例:- @Aspect
- @Component
- public class LoggingAspect {
- @Before("execution(* com.example.service.UserService.*(..))")
- public void logBefore(JoinPoint joinPoint) {
- System.out.println("Before method: " + joinPoint.getSignature().getName());
- }
- }
复制代码 这里,@Aspect 声明了一个切面类,@Before 定义了一个前置通知,拦截 UserService 的所有方法调用。
- 基于 XML 配置的方式
在 Spring 的 XML 配置文件中,可以配置切面、通知和切点。这种方式较少利用,更多用于老项目中。
示例:- <aop:config>
- <aop:pointcut id="userServiceMethods" expression="execution(* com.example.service.UserService.*(..))"/>
- <aop:aspect ref="loggingAspect">
- <aop:before method="logBefore" pointcut-ref="userServiceMethods"/>
- </aop:aspect>
- </aop:config>
复制代码 在 XML 配置中, 用于定义前置通知, 用于定义切点表达式。
3. AOP 通知范例详解
- Before Advice(前置通知)
前置通知会在目标方法执行之前运行。它通常用于做一些前置处理惩罚,比如验证参数、日志记录等。
示例:- @Before("execution(* com.example.service.UserService.*(..))")
- public void logBefore(JoinPoint joinPoint) {
- System.out.println("Before method: " + joinPoint.getSignature().getName());
- }
复制代码 在这个例子中,logBefore 方法会在 UserService 的所有方法执行前运行,打印出方法名。
- After Returning Advice(返回通知)
返回通知在目标方法乐成执行并返回结果之后运行。它通常用于记录方法的返回值。
示例:- @AfterReturning(pointcut = "execution(* com.example.service.UserService.*(..))", returning = "result")
- public void logAfterReturning(JoinPoint joinPoint, Object result) {
- System.out.println("Method returned: " + result);
- }
复制代码 这里的 logAfterReturning 方法会在 UserService 的方法执行乐成后,记录其返回值。
- After Throwing Advice(异常通知)
异常通知会在目标方法抛出异常时执行。它常用于异常处理惩罚和记录异常信息。
示例:- @AfterThrowing(pointcut = "execution(* com.example.service.UserService.*(..))", throwing = "error")
- public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
- System.out.println("Exception thrown: " + error);
- }
复制代码 该通知会在 UserService 的方法抛出异常时执行,记录异常信息。
- After (Finally) Advice(终极通知)
终极通知在目标方法执行完成后,无论是否抛出异常,都会执行。常用于清理资源等利用。
示例:- @After("execution(* com.example.service.UserService.*(..))")
- public void logAfter(JoinPoint joinPoint) {
- System.out.println("After method: " + joinPoint.getSignature().getName());
- }
复制代码 不管方法是否抛出异常,该通知都会执行。
- Around Advice(环绕通知)
环绕通知是功能最强大的通知范例,它可以在方法调用的 前后 进行自定义利用,还可以决定是否执行目标方法。常用于性能监控、事务管理等复杂场景。
示例:- @Around("execution(* com.example.service.UserService.*(..))")
- public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("Before method: " + joinPoint.getSignature().getName());
- Object result = joinPoint.proceed(); // 执行目标方法
- System.out.println("After method: " + joinPoint.getSignature().getName());
- return result;
- }
复制代码 在这个例子中,logAround 方法会在目标方法执行前后都打印日志,并在 proceed() 处执行目标方法。
4. AOP 的实际应用场景
- 日志记录:可以利用 AOP 拦截方法调用,在方法执行前后记录日志。
- 权限控制:在某些方法调用前检查用户是否有权限执行该利用。
- 性能监控:环绕通知可以用于盘算方法执行的时间,从而进行性能分析。
- 事务管理:在业务方法调用时自动开启、提交或回滚事务,通常通过环绕通知实现。
5. 基于注解方式实现 Spring AOP
在基于注解的方式中,重要利用三个注解:
- @Aspect:用于声明切面类。
- @Before、@After、@Around 等:用于定义通知(Advice)。
- @Pointcut:用于定义切点(Pointcut)。
1. 预备工作
在开始之前,确保 Spring AOP 的依赖已经包罗在项目中。- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
复制代码 接下来,启用 Spring AOP 功能。在 Spring Boot 项目中,Spring AOP 是默认开启的。如果在非 Spring Boot 项目中,则需要手动启用:- @Configuration
- @EnableAspectJAutoProxy
- public class AppConfig {
- // 配置类
- }
复制代码 @EnableAspectJAutoProxy 注解用于开启基于 AspectJ 注解风格的 AOP 支持。
2. 定义业务类
假设我们有一个简朴的业务类 UserService,该类包罗一个方法 getUserById,用来获取用户信息。- @Service
- public class UserService {
- public String getUserById(int userId) {
- System.out.println("Fetching user with ID: " + userId);
- return "User" + userId;
- }
- }
复制代码 3. 定义切面类
接下来,我们定义一个切面类 LoggingAspect 来实现日志记录功能。这个切面会在 UserService 的方法调用前后打印日志。- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Pointcut;
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.AfterThrowing;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.springframework.stereotype.Component;
- @Aspect
- @Component
- public class LoggingAspect {
- // 定义切点,匹配 UserService 类中的所有方法
- @Pointcut("execution(* com.example.service.UserService.*(..))")
- public void userServiceMethods() {}
- // 前置通知:在方法执行前执行
- @Before("userServiceMethods()")
- public void logBefore() {
- System.out.println("Before method execution");
- }
- // 后置通知:在方法执行后执行
- @After("userServiceMethods()")
- public void logAfter() {
- System.out.println("After method execution");
- }
- // 返回通知:方法成功返回结果后执行
- @AfterReturning(pointcut = "userServiceMethods()", returning = "result")
- public void logAfterReturning(Object result) {
- System.out.println("Method returned with value: " + result);
- }
- // 异常通知:方法抛出异常时执行
- @AfterThrowing(pointcut = "userServiceMethods()", throwing = "error")
- public void logAfterThrowing(Throwable error) {
- System.out.println("Method threw an exception: " + error);
- }
- // 环绕通知:在方法执行前后都执行,可以控制方法是否执行
- @Around("userServiceMethods()")
- public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("Before proceeding the method");
- Object result = joinPoint.proceed(); // 执行目标方法
- System.out.println("After proceeding the method");
- return result;
- }
- }
复制代码 6. 详细讲解各个注解
6.1 @Aspect
@Aspect 注解用于声明一个切面类。这个类中的方法会包罗横切关注点(如日志、事务等),这些方法可以在目标方法执行的差别阶段执行。
6.2 @Pointcut
@Pointcut 注解用于定义切点。切点是一个表达式,用于匹配哪些连接点(方法)会被切面拦截。Spring AOP 中常用的切点表达式有:
- execution():匹配方法执行连接点。最常用,格式如 execution(* 包名.类名.方法名(..))。
- within():限制匹配某个类或包中的所有方法。
- bean():匹配 Spring 容器中的 bean,格式如 bean(beanName)。
示例:- @Pointcut("execution(* com.example.service.UserService.*(..))")
- public void userServiceMethods() {}
复制代码 这个切点匹配 UserService 类中的所有方法。
6.3 @Before
@Before 注解定义了前置通知,它会在目标方法执行前执行。比方:- @Before("userServiceMethods()")
- public void logBefore() {
- System.out.println("Before method execution");
- }
复制代码 在 UserService 类的方法执行前,这段代码会先打印出 "Before method execution"。
6.4 @After
@After 注解定义了后置通知,无论目标方法是否正常返回,都会在方法执行后执行。比方:- @After("userServiceMethods()")
- public void logAfter() {
- System.out.println("After method execution");
- }
复制代码 这个通知会在目标方法执行完后打印 "After method execution"。
6.5 @AfterReturning
@AfterReturning 注解定义了返回通知,它会在目标方法正常返回结果后执行,可以获取到方法的返回值。- @AfterReturning(pointcut = "userServiceMethods()", returning = "result")
- public void logAfterReturning(Object result) {
- System.out.println("Method returned with value: " + result);
- }
复制代码 这里,result 参数用于接收目标方法的返回值,并打印它。
6.6 @AfterThrowing
@AfterThrowing 注解定义了异常通知,它会在目标方法抛出异常时执行,可以获取到异常信息。- @AfterThrowing(pointcut = "userServiceMethods()", throwing = "error")
- public void logAfterThrowing(Throwable error) {
- System.out.println("Method threw an exception: " + error);
- }
复制代码 当目标方法抛出异常时,这个通知会打印出异常信息。
6.7 @Around
@Around 注解定义了环绕通知,是功能最强大的一种通知范例。它不仅可以在目标方法执行前后执行,还可以决定是否执行目标方法。通常用于性能监控、事务管理等场景。- @Around("userServiceMethods()")
- public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("Before proceeding the method");
- Object result = joinPoint.proceed(); // 执行目标方法
- System.out.println("After proceeding the method");
- return result;
- }
复制代码 ProceedingJoinPoint 对象可以用来执行目标方法。proceed() 方法执行目标方法,目标方法的返回值会被返回给调用者。
7. 运行结果
- 当调用 UserService 的方法时,比如 userService.getUserById(1):
- @Before 通知会首先执行,打印 "Before method execution"。
- 目标方法执行,打印 "Fetching user with ID: 1"。
- @AfterReturning 通知会执行,打印 "Method returned with value: User1"。
- @After 通知会执行,打印 "After method execution"。
如果目标方法抛出异常:
- @AfterThrowing 通知会执行,打印异常信息。
环绕通知会在目标方法执行前后都执行,包裹住目标方法的执行过程。
总结
基于注解的 Spring AOP 提供了一种简洁且强大的方式来实现横切关注点的管理。通过 @Aspect、@Before、@After、@Around 等注解,开发者可以在不改变业务代码的环境下,将日志记录、事务管理、性能监控等功能模块化地注入到代码中。
9、execution 表达式匹配详解
execution 是 Spring AOP 中最常用的切点表达式之一,它用来匹配方法执行的连接点。通过 execution 表达式,可以灵活定义要拦截的目标方法。execution 表达式的语法格式如下:- execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)
复制代码
- modifiers-pattern:可选,方法的修饰符(如 public、protected 等)。
- return-type-pattern:方法的返回范例,利用通配符 * 表现任意返回范例。
- declaring-type-pattern:可选,方法所在的类或接口。
- method-name-pattern:方法名,支持通配符 *,表现匹配任意方法。
- param-pattern:参数列表,利用 (..) 表现匹配任意参数,利用 (*) 表现匹配一个参数,利用 (*,String) 表现匹配两个参数且第一个参数为任意范例,第二个为 String。
- throws-pattern:可选,声明抛出的异常。
execution 匹配的常见例子
- execution(* com.example.service.*.*(..))
复制代码
- 匹配 com.example.service 包下所有类中的所有方法。
- * 表现任意返回范例,.*(..) 表现任意方法名和任意参数列表。
- execution(* com.example.service.UserService.*(..))
复制代码- execution(* com.example.service.UserService.getUserById(int))
复制代码
- 匹配 UserService 类中参数为 int 的 getUserById 方法。
- execution(String com.example.service.UserService.*(..))
复制代码
- 匹配 UserService 类中返回范例为 String 的所有方法。
- execution(public * com.example.service.UserService.*(..))
复制代码
- 匹配 UserService 类中所有 public 方法。
- execution(* com.example.service.UserService.updateUser(String, int))
复制代码
- 匹配 UserService 类中带有 String 和 int 参数的 updateUser 方法。
通配符的利用
- *:匹配任意返回值、任意方法名或任意参数。
- ..:匹配零个或多个参数。
比方:- @Before("execution(* com.example.*.*(..))")
复制代码
- 该切点表达式匹配 com.example 包下的所有类的所有方法。
总结
- execution 表达式 用于匹配方法执行的连接点,可以通过灵活的模式匹配包、类、方法名、参数列表等。
- 通常在 AOP 中,通过 execution 精准地指定哪些方法需要被增强,从而实现功能的横切关注点(如日志记录、事务管理等)。
先写到这里... ~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |