Spring 框架既可以从 广义 和 狭义 两个角度理解,下面讲解这两个层面的概念:
(本文主要讲解的是狭义上的spring,广义上的简单概括)
从广义上讲,Spring 是一个涵盖多个模块的企业级应用开发框架,它提供了从基础架构到复杂企业应用开发所需的全面解决方案。Spring 框架的模块化设计帮助开发者在不同的场景中选择合适的模块或子项目。
广义的 Spring 包含以下几个子项目:
因此,广义上的 Spring 不仅仅是一个框架,而是一个生态系统,可以用于构建从小型应用到复杂分布式系统的各种项目。
从狭义上讲,Spring 特指 Spring Framework,它是 Spring 生态系统中的核心部分,主要提供 IoC(控制反转)容器和 AOP(面向切面编程)功能。
狭义上的 Spring 主要包括以下几个模块:
狭义的 Spring 主要指围绕核心容器(IoC)与面向切面编程(AOP)的功能,它是企业级应用开发的基础,能够帮助开发者通过解耦、简化配置等方式高效开发应用程序。
下面会先简单介绍一下 Spring DAO 模块 和 Spring ORM 模块(我们主要讲解的是 ioc、依赖注入、aop相关内容)
Spring DAO 模块主要用于简化对数据库的访问,特别是简化 JDBC(Java Database Connectivity) 编程。直接使用 JDBC 进行数据库操作通常会涉及到大量样板代码,例如创建连接、执行查询、处理异常、关闭资源等。而 Spring DAO 模块通过封装这些底层操作,提供了更简洁的 API。
JdbcTemplate
等工具类,极大简化了数据库操作,不需要手动管理数据库连接和资源的关闭。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 语句即可。
Spring ORM 模块用于简化与 ORM(对象关系映射)框架的集成,例如 Hibernate、JPA(Java Persistence API)、MyBatis 等。ORM 框架用于将 Java 对象映射到数据库中的表,使得开发者可以通过操作对象来进行数据库操作,而不是直接编写 SQL 语句。
Spring ORM 模块通过封装和简化 ORM 框架的配置和使用,使得它们能够无缝集成到 Spring 应用中。Spring ORM 不是自己实现 ORM,而是帮助开发者更好地使用现有的 ORM 工具,如 Hibernate 或 JPA。
@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 {
List findByName(String name);
}
在这个例子中:
users
表对应。每个字段(如 id
, name
, email
)对应表中的一列。JpaRepository
,可以自动生成常用的数据库操作方法,如保存、查询等。你甚至不需要写 SQL,只需要通过定义接口来操作数据库。简单来说,Spring DAO 更倾向于手动管理 SQL,而 Spring ORM 则是通过映射 Java 对象与数据库表来进行操作。如果你的项目是以 ORM 框架为主,可以使用 Spring ORM;如果你需要更多的 SQL 自定义控制,可以使用 Spring DAO。
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 以及相关的概念。
IoC 容器是 Spring 框架中的核心组件,它负责管理应用程序中的对象(即 Bean)。主要的 IoC 容器有两个:
ClassPathXmlApplicationContext
、FileSystemXmlApplicationContext
和 AnnotationConfigApplicationContext
。@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 管理,并可以在其他地方通过依赖注入来使用。
作用:通过 @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 并提供它的注入。
作用:@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 销毁前调用。
作用:可以通过 @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。
Bean对象 是指被 Spring 容器 管理的对象。Bean 是 Spring 容器中的核心概念之一,它代表了一个受 Spring 管理的对象实例。Spring 提供了多种方式来定义 Bean:(spring 容器就是 ioc容器)
@Component
、@Service
、@Repository
、@Controller
)自动注册 Bean,并通过 @Autowired
自动注入依赖。@Configuration
注解的类和 @Bean
注解的方法定义 Bean。在 XML 配置文件中定义 Bean 是 Spring 的传统方式。这种方式在 Spring 2.x 和之前版本中广泛使用,虽然现在注解和 Java 配置更常见,但 XML 配置依然有效。
示例 XML 配置:
id
:Bean 的唯一标识符。class
:Bean 实现的类名。property
:配置 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
:注入属性值。Java 配置是 Spring 3.0 引入的,通过 @Configuration
注解的类和 @Bean
注解的方法来定义 Bean。这种方式将配置逻辑与代码放在一起,提高了类型安全性和可重构性。
示例 Java 配置:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean("example");
}
}
@Configuration
:标识这是一个配置类。@Bean
:方法返回的对象会被注册为 Spring 容器中的 Bean。Bean 的获取是指从 Spring 容器中获取已定义的 Bean 实例。Spring 提供了多种方法来获取 Bean:
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。@Autowired
注解@Autowired
注解用于自动注入依赖的 Bean。Spring 会自动查找容器中匹配的 Bean 并注入。(下面 依赖注入 的时候会讲)
示例自动注入:
@Component
public class MyService {
@Autowired
private MyBean myBean;
// 使用 myBean
}
@Autowired
:标识自动注入依赖的 Bean。Spring 根据类型自动注入对应的 Bean。Spring 容器在创建、初始化和销毁 Bean 时,会经过以下几个生命周期阶段:
@Component
public class MyBean {
@PostConstruct
public void init() {
// 初始化代码
}
@PreDestroy
public void destroy() {
// 销毁代码
}
}
Spring 支持多种 Bean 的作用域,决定了 Bean 的生命周期和可见性:
@Component
@Scope("prototype")
public class MyBean {
// 每次注入都创建新的实例
}
依赖注入 是一种设计模式,它将对象的依赖关系从对象的内部管理转移到外部容器(如 Spring)。通过这种方式,Spring 容器负责创建和管理对象的依赖关系,从而降低组件之间的耦合,提高应用的灵活性和可测试性。
在 Spring 中,Bean 的定义和依赖注入有多种方式,可以通过 XML 配置、注解或 Java 配置来完成。
XML 配置 是 Spring 的传统方式,通过 XML 文件定义 Bean 的属性和依赖关系。
示例 XML 配置:
元素定义了一个 Bean,其中 id
是 Bean 的唯一标识,class
是 Bean 实现的类。
元素用于设置 Bean 的属性值。name
是属性的名称,value
是属性的值,ref
指定引用的其他 Bean(注入 bean 对象)。注解配置 提供了更为简洁和灵活的方式来定义 Bean 和注入依赖。它使用注解来标识 Bean 和依赖关系。
示例注解配置:
@Component
public class MyBean {
@Value("example")
private String name;
// Getter 和 Setter
}
@Component
public class MyService {
@Autowired
private MyBean myBean; // 自动注入
// 使用 myBean
}
@Component
:标识 MyBean
和 MyService
是 Spring 管理的 Bean。@Autowired
:自动注入 MyBean
到 MyService
中。Spring 根据类型自动查找和注入 MyBean
实例。@Value
:注入属性值。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。依赖注入的方式的不通 主要体现在在 @Autowired
注解 的位置不通
通过构造函数将依赖注入到 Bean 中。构造器注入确保了 Bean 在创建时就有所有的依赖项。
示例:
@Component
public class MyService {
private final MyBean myBean;
@Autowired
public MyService(MyBean myBean) {
this.myBean = myBean;
}
// 使用 myBean
}
通过 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
}
通过普通方法注入依赖,通常用于更灵活的场景。
示例:
@Component
public class MyService {
private MyBean myBean;
@Autowired
public void init(MyBean myBean) {
this.myBean = myBean;
}
// 使用 myBean
}
补充内容:
@Component
、@Service
、@Repository
、@Controller
:如果没有指定 value
属性,Spring 会使用类名的首字母小写形式作为 Bean 的默认名称。例如,MyComponent
的默认 Bean 名称是 myComponent
。@Bean
:如果没有指定 name
属性,Bean 名称将是方法名。例如,myBean()
方法定义的 Bean 名称是 myBean
。Spring 根据 Bean 的类型自动匹配依赖项。
示例:
@Component
public class MyService {
@Autowired
private MyBean myBean; // 按类型自动注入 (MyBean这个类)
// 如果 MyBean 是一个接口, 并且它有两个实现类都注册为 bean 了, 那么根据类型自动注入就会报错, 我们需要用 按照名称自动装配
}
Spring 根据 Bean 的名称进行注入。
示例:
@Component
public class MyService {
@Resource(name = "myBean")
private MyBean myBean; // 按名称注入
}
@Qualifier
注解当有多个符合条件的 Bean 时,可以使用 @Qualifier
注解指定具体的 Bean。
示例:
@Component
public class MyService {
@Autowired
@Qualifier("myBean1")
private MyBean myBean; // 指定具体的 Bean
}
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将 横切关注点(cross-cutting concerns)与核心业务逻辑分离。横切关注点是指那些影响多个模块的功能,例如 日志记录、性能监控、事务管理、权限控制 等。这些功能通常与业务逻辑无关,但需要在多个地方执行。
通过 AOP,开发者可以将这些功能提取出来,以模块化的方式进行管理,而不是将这些功能分散到业务代码中,从而提高代码的可读性和可维护性。
Spring AOP 是 Spring 框架的一部分,专门用于简化横切关注点的处理。它允许开发者通过 声明式 方式(使用注解或 XML 配置)来定义切面,而不需要手动修改原有的业务代码。
在 AOP 中,有几个关键的概念需要理解:
Aspect(切面)
切面是横切关注点的模块化实现。一个切面通常包含多个横切功能,例如日志记录或事务管理。切面由 通知(advice) 和 切点(pointcut) 组成。
Join Point(连接点)
连接点是程序执行过程中的某个点,在 Spring 中通常是方法调用(还可以是异常抛出、字段访问等)。切面可以在这些连接点执行某些操作。
Pointcut(切点)
切点是一个定义了哪些连接点会被拦截的表达式。切面会在匹配切点的连接点上执行。
Advice(通知)
通知定义了切面在连接点上执行的具体操作。通知可以在方法执行的 之前、之后、抛出异常时 或 最终 执行。
Spring 提供了以下几种常见的通知类型:
Target(目标对象)
被 AOP 代理的对象。切面功能最终是应用在目标对象的某些方法上的。
Proxy(代理对象)
AOP 框架通过为目标对象生成代理对象来实现切面的功能。Spring AOP 基于 动态代理 机制,在运行时为目标对象生成一个代理类,拦截方法调用,并在调用之前、之后或抛出异常时执行通知。
Weaving(织入)
织入是将切面应用到目标对象并创建代理对象的过程。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 配置文件中,可以配置切面、通知和切点。这种方式较少使用,更多用于老项目中。
示例:
在 XML 配置中,
用于定义前置通知,
用于定义切点表达式。
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()
处执行目标方法。
在基于注解的方式中,主要使用三个注解:
@Aspect
:用于声明切面类。@Before
、@After
、@Around
等:用于定义通知(Advice)。@Pointcut
:用于定义切点(Pointcut)。在开始之前,确保 Spring AOP 的依赖已经包含在项目中。
org.springframework.boot
spring-boot-starter-aop
接下来,启用 Spring AOP 功能。在 Spring Boot 项目中,Spring AOP 是默认开启的。如果在非 Spring Boot 项目中,则需要手动启用:
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
// 配置类
}
@EnableAspectJAutoProxy
注解用于开启基于 AspectJ 注解风格的 AOP 支持。
假设我们有一个简单的业务类 UserService
,该类包含一个方法 getUserById
,用来获取用户信息。
@Service
public class UserService {
public String getUserById(int userId) {
System.out.println("Fetching user with ID: " + userId);
return "User" + userId;
}
}
接下来,我们定义一个切面类 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;
}
}
@Aspect
@Aspect
注解用于声明一个切面类。这个类中的方法会包含横切关注点(如日志、事务等),这些方法可以在目标方法执行的不同阶段执行。
@Pointcut
@Pointcut
注解用于定义切点。切点是一个表达式,用于匹配哪些连接点(方法)会被切面拦截。Spring AOP 中常用的切点表达式有:
execution(* 包名.类名.方法名(..))
。bean(beanName)
。示例:
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}
这个切点匹配 UserService
类中的所有方法。
@Before
@Before
注解定义了前置通知,它会在目标方法执行前执行。例如:
@Before("userServiceMethods()")
public void logBefore() {
System.out.println("Before method execution");
}
在 UserService
类的方法执行前,这段代码会先打印出 "Before method execution"
。
@After
@After
注解定义了后置通知,无论目标方法是否正常返回,都会在方法执行后执行。例如:
@After("userServiceMethods()")
public void logAfter() {
System.out.println("After method execution");
}
这个通知会在目标方法执行完后打印 "After method execution"
。
@AfterReturning
@AfterReturning
注解定义了返回通知,它会在目标方法正常返回结果后执行,可以获取到方法的返回值。
@AfterReturning(pointcut = "userServiceMethods()", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Method returned with value: " + result);
}
这里,result
参数用于接收目标方法的返回值,并打印它。
@AfterThrowing
@AfterThrowing
注解定义了异常通知,它会在目标方法抛出异常时执行,可以获取到异常信息。
@AfterThrowing(pointcut = "userServiceMethods()", throwing = "error")
public void logAfterThrowing(Throwable error) {
System.out.println("Method threw an exception: " + error);
}
当目标方法抛出异常时,这个通知会打印出异常信息。
@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()
方法执行目标方法,目标方法的返回值会被返回给调用者。
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
等注解,开发者可以在不改变业务代码的情况下,将日志记录、事务管理、性能监控等功能模块化地注入到代码中。
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.*(..))
UserService
类中的所有方法。execution(* com.example.service.UserService.getUserById(int))
UserService
类中参数为 int
的 getUserById
方法。String
的方法: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
表达式 用于匹配方法执行的连接点,可以通过灵活的模式匹配包、类、方法名、参数列表等。execution
精准地指定哪些方法需要被增强,从而实现功能的横切关注点(如日志记录、事务管理等)。先写到这里... ~
参与评论
手机查看
返回顶部