什么是AOP?
面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取公共模块复用,降低偶合。
Spring中事务是如何实现的?
其本质是通过AOP功能,对方法前后进行拦截,在执行方法开始之前开启事务,在执行完成目标方法之后根据执行情况提交或者回滚事务。
1、什么是AOP
AOP称为面向切面编程,用于将那些与业务无关,却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中重复代码,降低模块间的耦合度,同时提高系统的可维护性。
- 切点(Pointcut):定义了切面要作用的目标对象或方法。切点可以通过表达式、注解方式或者方法名匹配的方式进行定义。
- 通知(Advice):定义在切点何时执行的逻辑。通知可以在切点之前、之后或者异常抛出时进行执行。
- 切面(Aspect):由切点和通知组成的编程模块,用于定义特定的关注点。
- 织入(Weaving):将切面和目标对象进行关联的过程。织入可以在编译时、类加载时或者运行时进行。
- 连接点(Joinpoint):目标对象中能够被切面拦截的方法。连接点可以被通知所影响。
- 引入(Introduction):将新的接口或方法引入到目标对象中的过程。引入可以使得目标对象具有新的能力。
- 切面优先级(Aspect Ordering):定义多个切面之间的执行顺序。优先级高的切面可以被优先执行。
2、常见的AOP使用场景:
- 记录操作日志
- 缓存处理
- Spring中内置的事务处理
3、AOP使用场景-记录操作日志
3.1、记录操作日志的思路
其核心是:使用AOP中的环绕通知+切点表达式(找到要记录日志的方法),通过环绕通知的参数获取请求方法的参数(类、方法、注解、请求方式等),获取到这些参数以后,保存到数据库。
3.2、记录操作日志的实现
(基于Spring Boot)
1.添加AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建一个切面类并使用@Aspect
注解标记它
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.yourpackage..*.*(..))") // 根据实际情况配置切点表达式
public void logPointcut() {
}
@Before("logPointcut()")
public void doBefore(JoinPoint joinPoint) {
// 在此处记录请求开始的日志,例如请求方法、参数等
}
@AfterReturning(pointcut = "logPointcut()", returning = "retVal")
public void doAfterReturning(Object retVal) {
// 在此处记录请求结束的日志,例如响应结果等
}
}
3.配置AOP,确保切面类被Spring容器管理。
4.在控制器或服务层的方法上使用注解(如@RequestMapping, @Service等),AOP将自动应用日志记录逻辑。
4、AOP使用场景-Spring事务的实现
Spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务控制:需使用Transction Template来实现,对业务代码有侵入性,一般很少使用。
声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行目标方法之后根据执行情况提交或回滚事务。
4.1.Spring事务使用示例(重要)
1.使用@Transactional注解
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SomeService {
@Transactional
public void someMethod() {
// do some database operations
}
}
在上述代码中,someMethod()方法被@Transactional注解包围,这意味着Spring将此方法的调用包装在一个事务中。如果方法成功完成,Spring将提交事务。如果方法抛出异常,Spring将回滚事务。
4.2.Spring事务使用示例(拓展)
2.使用XML配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
在上述配置中,定义了一个事务管理器,并通过tx:annotation-driven告诉Spring我们想要基于注解的事务管理。然后我们可以在服务类的方法上使用@Transactional注解来声明事务。
3.使用Java配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
在上述Java配置中,我们使用@EnableTransactionManagement注解启用注解事务管理。然后我们定义了一个事务管理器的bean,可以在服务类的方法上使用@Transactional注解来声明事务。
5、使用Spring事务的注意事项
在使用Spring事务时,需确保数据源是支持事务的。例如,
- 如果使用的是JDBC,那么数据源必须是实现了PlatformTransactionManager接口的类的实例,如DataSourceTransactionManager。
- 如果你使用的是MyBatis,那么需要使用Spring的SqlSessionFactoryBean来创建SqlSessionFactory,并且使用Spring的事务管理器DataSourceTransactionManager。
特别地,在需要使用Spring事务时,我们需要保证Srping事务是生效的。这也是一个老生常谈的话题了:链接:Spring之事务失效的场景