Spring AOP的使用总结

AOP相关概念

AOP相关概念
AOP相关概念

基于XML配置的AOP

快速入门

xml方式配置AOP的步骤:

  1. 导入AOP相关坐标,Spring-context坐标下包含spring-aop的包
  2. 准备目标类、准备增强类,并配置给Spring管理;
  3. 配置切点表达式(哪些方法被增强);
  4. 配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)。

准备目标类、准备增强类,并配置给Spring管理

目标类

public interface UserService {
	void show1();
	void show2();
}
public class UserServiceImpl implements UserService {
    public void show1() {
        System.out.println("show1...");
    }
    public void show2() {
        System.out.println("show2...");
    }
}

增强类

public class MyAdvice {
    public void beforeAdvice(){
    	System.out.println("beforeAdvice");
    }
    public void afterAdvice(){
    	System.out.println("afterAdvice");
    }
}

xml配置文件

<!--配置目标类,内部的方法是连接点-->
<bean id="userService" class="com.rqz.service.impl.UserServiceImpl"/>
<!--配置通知类,内部的方法是增强方法-->
<bean id=“myAdvice" class="com.rqz.advice.MyAdvice"/>

配置切点表达式(哪些方法被增强)和配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)

<aop:config>
    <!--配置切点表达式,对哪些方法进行增强-->
    <aop:pointcut id="myPointcut" expression="execution(void com.rqz.service.impl.UserServiceImpl.show1())"/>
    <!--切面=切点+通知-->
    <aop:aspect ref="myAdvice">
        <!--指定前置通知方法是beforeAdvice-->
        <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
        <!--指定后置通知方法是afterAdvice-->
        <aop:after-returning method="afterAdvice" pointcut-ref="myPointcut"/>
    </aop:aspect>
</aop:config>

切点表达式的配置方式有两种,直接将切点表达式配置在通知上,也可以将切点表达式抽取到外面,在通知上进行引用

<aop:config>
    <!--配置切点表达式,对哪些方法进行增强-->
    <aop:pointcut id="myPointcut" expression="execution(void com.rqz.service.impl.UserServiceImpl.show1())"/>
    <!--切面=切点+通知-->
    <aop:aspect ref="myAdvice">
        <!--指定前置通知方法是beforeAdvice-->
        <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
        
        <!--指定后置通知方法是afterAdvice-->
        <aop:after-returning method="afterAdvice" pointcut="execution(void com.rqz.service.impl.UserServiceImpl.show1())"/>
        
    </aop:aspect>
</aop:config>

配置详解

切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:

execution([访问修饰符]返回值类型 包名.类名.方法名(参数))

其中

  • 访问修饰符可以省略不写;
  • 返回值类型、某一级包名、类名、方法名 可以使用 * 表示任意;
  • 包名与类名之间使用单点 . 表示该包下的类,使用双点 … 表示该包及其子包下的类;
  • 参数列表可以使用两个点 … 表示任意参数。

切点表达式举例

//表示访问修饰符为public、无返回值、在com.rqz.aop包下的TargetImpl类的无参方法show
execution(public void com.rqz.aop.TargetImpl.show())
//表述com.rqz.aop包下的TargetImpl类的任意方法
execution(* com.rqz.aop.TargetImpl.*(..))
//表示com.rqz.aop包下的任意类的任意方法
execution(* com.rqz.aop.*.*(..))
//表示com.rqz.aop包及其子包下的任意类的任意方法
execution(* com.rqz.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..))

AspectJ的通知由以下五种类型

通知名称配置方式执行时机
前置通知< aop:before >目标方法执行之前执行
后置通知< aop:after-returning >目标方法执行之后执行,目标方法异常时,不在执行
环绕通知< aop:around >目标方法执行前后执行,目标方法异常时,环绕后方法不在执行
异常通知< aop:after-throwing >目标方法抛出异常时执行
最终通知< aop:after >不管目标方法是否有异常,最终都会执行

环绕通知

public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    //环绕前
    System.out.println("环绕前通知");
    //目标方法
    joinPoint.proceed();
    ///环绕后
    System.out.println("环绕后通知");
}
<aop:around method="around" pointcut-ref="myPointcut"/>

异常通知

当目标方法抛出异常时,异常通知方法执行,且后置通知和环绕后通知不在执行

public void afterThrowing(){
	System.out.println("目标方法抛出异常了,后置通知和环绕后通知不在执行");
}
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>

最终通知

类似异常捕获中的finally,不管目标方法有没有异常,最终都会执行的通知

public void after(){
	System.out.println("不管目标方法有无异常,我都会执行");
}
<aop:after method="after" pointcut-ref="myPointcut"/>

通知方法在被调用时,Spring可以为其传递一些必要的参数

参数类型作用
JoinPoint连接点对象,任何通知都可使用,可以获得当前目标对象、目标方法参数等信息
ProceedingJoinPointJoinPoint子类对象,主要是在环绕通知中执行proceed(),进而执行目标方法
Throwable异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称

JoinPoint 对象

public void 通知方法名称(JoinPoint joinPoint){
    //获得目标方法的参数
    System.out.println(joinPoint.getArgs());
    //获得目标对象
    System.out.println(joinPoint.getTarget());
    //获得精确的切点表达式信息
    System.out.println(joinPoint.getStaticPart());
}

ProceedingJoinPoint对象

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println(joinPoint.getArgs());//获得目标方法的参数
    System.out.println(joinPoint.getTarget());//获得目标对象
    System.out.println(joinPoint.getStaticPart());//获得精确的切点表达式信息
    Object result = joinPoint.proceed();//执行目标方法
    return result;//返回目标方法返回值
}

Throwable对象

public void afterThrowing(JoinPoint joinPoint,Throwable th){
    //获得异常信息
    System.out.println("异常对象是:"+th+"异常信息是:"+th.getMessage());
}
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="th"/>

基于注解配置的AOP

基本使用

Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:

<!--配置目标-->
<bean id="target" class="com.rqz.aop.TargetImpl"></bean>
<!--配置通知-->
<bean id="advices" class="com.rqz.aop.Advices"></bean>
<!--配置aop-->
<aop:config proxy-target-class="true">
    <aop:aspect ref="advices">
    	<aop:around method="around" pointcut="execution(* com.rqz.aop.*.*(..))"/>
    </aop:aspect>
</aop:config>

目标类被Spring容器管理、通知类被Spring管理

@Component("target")
public class TargetImpl implements Target{
    public void show() {
    	System.out.println("show Target running...");
    }
}
@Component
public class AnnoAdvice {
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知...");
        joinPoint.proceed();
        System.out.println("环绕后通知...");
    }
}

配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么

@Component
@Aspect //第一步
public class AnnoAdvice {
    @Around("execution(* com.rqz.aop.*.*(..))") //第二步
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知...");
        joinPoint.proceed();
        System.out.println("环绕后通知...");
    }
}

注解@Aspect、@Around需要被Spring解析,所以在Spring核心配置文件中需要配置aspectj的自动代理

<aop:aspectj-autoproxy/>

如果核心配置使用的是配置类的话,需要配置注解方式的aop自动代理

@Configuration
@ComponentScan("com.rqz.aop")
@EnableAspectJAutoProxy //第三步
public class ApplicationContextConfig {
}

配置详解

各种注解方式通知类型

//前置通知
@Before("execution(* com.rqz.aop.*.*(..))")
public void before(JoinPoint joinPoint){}
//后置通知
@AfterReturning("execution(* com.rqz.aop.*.*(..))")
public void AfterReturning(JoinPoint joinPoint){}
//环绕通知
@Around("execution(* com.rqz.aop.*.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {}
//异常通知
@AfterThrowing("execution(* com.rqz.aop.*.*(..))")
public void AfterThrowing(JoinPoint joinPoint){}
//最终通知
@After("execution(* com.rqz.aop.*.*(..))")
public void After(JoinPoint joinPoint){}

切点表达式的抽取,使用一个空方法,将切点表达式标注在空方法上,其他通知方法引用即可

@Component
@Aspect
public class AnnoAdvice {
    //切点表达式抽取
    @Pointcut("execution(* com.rqz.aop.*.*(..))")
    public void pointcut(){}
    //前置通知
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){}
    //后置通知
    @AfterReturning("AnnoAdvice.pointcut()")
    public void AfterReturning(JoinPoint joinPoint){}
    // ... 省略其他代码 ...
}

基于AOP的声明式事务控制

基于xml声明式事务控制

导入Spring事务的相关的坐标,spring-jdbc坐标已经引入的spring-tx坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.13.RELEASE</version>
</dependency>

配置目标类AccountServiceImpl

<bean id="accountService" class="com.rqz.service.impl.AccoutServiceImpl">
	<property name="accountMapper" ref="accountMapper"></property>
</bean>

Spring提供的事务通知

xmlns:tx="http://www.springframework.org/schema/tx" 
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
<!--Spring提供的事务通知-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="指定要进行事务的方法名称"/>
    </tx:attributes>
</tx:advice>
<!--平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>
<aop:config>
	<aop:advisor advice-ref="myAdvice" pointcut="execution(* com.rqz.service.impl.*.*(..))"/>
</aop:config>

对上述配置进行详解

平台事务管理器PlatformTransactionManager是Spring提供的封装事务具体操作的规范接口,封装了事务的提交和回滚方法

public interface PlatformTransactionManager extends TransactionManager {
	TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
	void commit(TransactionStatus var1) throws TransactionException;
	void rollback(TransactionStatus var1) throws TransactionException;
}

不同的持久层框架事务操作的方式有可能不同,所以不同的持久层框架有可能会有不同的平台事务管理器实现,例如,MyBatis作为持久层框架时,使用的平台事务管理器实现是DataSourceTransactionManager。Hibernate作为持久层框架时,使用的平台事务管理器是HibernateTransactionManager。

事务定义信息配置,每个事务有很多特性,例如:隔离级别、只读状态、超时时间等,这些信息在开发时可以通过connection进行指定,而此处要通过配置文件进行配置

<!-- name属性名称指定哪个方法要进行哪些事务的属性配置 -->
<tx:attributes>
    <tx:method 
            name="方法名称"
            isolation="隔离级别"
            propagation="传播行为"
            read-only="只读状态"
            timeout="超时时间"/>
</tx:attributes>

方法名在配置时,也可以使用 * 进行模糊匹配,例如:

<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!--精确匹配transferMoney方法-->
        <tx:method name="transferMoney"/>
        <!--模糊匹配以Service结尾的方法-->
        <tx:method name="*Service"/>
        <!--模糊匹配以insert开头的方法-->
        <tx:method name="insert*"/>
        <!--模糊匹配以update开头的方法-->
        <tx:method name="update*"/>
        <!--模糊匹配任意方法,一般放到最后作为保底匹配-->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

isolation属性:指定事务的隔离级别

事务并发存在三大问题:脏读、不可重复读、幻读/虚读。可以通过设置事务的隔离级别来保证并发问题的出现,常用的是READ_COMMITTED 和 REPEATABLE_READ

isolation属性解释
DEFAULT默认隔离级别,取决于当前数据库隔离级别,例如MySQL默认隔离级别是REPEATABLE_READ
READ_UNCOMMITTEDA事务可以读取到B事务尚未提交的事务记录,不能解决任何并发问题,安全性最低,性能最高
READ_COMMITTEDA事务只能读取到其他事务已经提交的记录,不能读取到未提交的记录。可以解决脏读问题,但是不能解决不可重复读和幻读
REPEATABLE_READA事务多次从数据库读取某条记录结果一致,可以解决不可重复读,不可以解决幻读
SERIALIZABLE串行化,可以解决任何并发问题,安全性最高,但是性能最低

read-only属性:设置当前的只读状态
如果是查询则设置为true,可以提高查询性能,如果是更新(增删改)操作则设置为false

<!-- 一般查询相关的业务操作都会设置为只读模式 -->
<tx:method name="select*" read-only="true"/>
<tx:method name="find*" read-only="true"/>

timeout属性:设置事务执行的超时时间

单位是秒,如果超过该时间限制但事务还没有完成,则自动回滚事务,不在继续执行。默认值是-1,即没有超时时间限制

<!-- 设置查询操作的超时时间是3秒 -->
<tx:method name="select*" read-only="true" timeout="3"/>

propagation属性:设置事务的传播行为

主要解决是A方法调用B方法时,事务的传播方式问题的,例如:使用单方的事务,还是A和B都使用自己的事务等。事务的传播行为有如下七种属性值可配置

事务传播行为解释
REQUIRED(默认值)A调用B,B需要事务,如果A有事务B就加入A的事务中,如果A没有事务,B就自己创建一个事务
REQUIRED_NEWA调用B,B需要新事务,如果A有事务就挂起,B自己创建一个新的事务
SUPPORTSA调用B,B有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务方式执行
NOT_SUPPORTSA调用B,B以无事务方式执行,A如有事务则挂起
NEVERA调用B,B以无事务方式执行,A如有事务则抛出异常
MANDATORYA调用B,B要加入A的事务中,如果A无事务就抛出异常
NESTEDA调用B,B创建一个新事务,A有事务就作为嵌套事务存在,A没事务就以创建的新事务执行

基于注解声明式事务控制

@Service("accountService")
public class AccoutServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;
    //<tx:method name="*" isolation="REPEATABLE_READ" propagation="REQUIRED“/>
    @Transactional(isolation = Isolation.REPEATABLE_READ , propagation = Propagation.REQUIRED , readOnly = false,timeout = 5)
    public void transferMoney(String decrAccountName, String incrAccountName, int money) {
        accountMapper.decrMoney(decrAccountName,money); //转出钱
        int i = 1/0; //模拟某些逻辑产生的异常
        accountMapper.incrMoney(incrAccountName,money); //转入钱
    }
}
@Configuration
@ComponentScan("com.rqz.service")
@PropertySource("classpath:jdbc.properties")
@MapperScan("com.rqz.mapper")
@EnableTransactionManagement
public class ApplicationContextConfig {
    @Bean
    public PlatformTransactionManager tansactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
// ... 省略其他配置 ...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP依赖的 jar 包是指在使用 Spring AOP 时所需要的相关的 jar 文件。根据引用,Spring AOP 版本为 2.5.6,如果需要可以下载并使用该版本的 jar 包。请注意,这里的 jar 包指的是 Spring AOP 的依赖库,而非 Spring 框架本身。 另外,引用提到了 AOP 编程思想不是 Spring 独有的,而是 Spring 支持的一种编程思想。所以在理解 Spring AOP 时,要注意区分 AOP 编程思想与 Spring 框架的关系。 对于 Spring AOP 底层的实现方式,引用中提到,默认情况下,Spring 使用 Java 的动态代理来创建 AOP 代理。如果需要代理的是类而不是接口,Spring 会自动切换成 CGLIB 代理。当然,也可以强制使用 CGLIB 代理。此外,SpringAOP 还需要由 IOC 容器来生成和管理。因此,Spring AOP 可以直接使用 IOC 容器中的其他 bean 作为目标。这种依赖关系由 IOC 容器提供的依赖注入来实现。 总结起来,Spring AOP 依赖的是相关的 jar 包,其版本为 2.5.6。同时,要注意理解 AOP 编程思想与 Spring 框架的关系。另外,在使用 Spring AOP 时,要注意底层使用的是 Java 的动态代理或 CGLIB 代理,并且依赖 IOC 容器来生成和管理 AOP 相关的 bean。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [spring aop依赖jar包](https://download.csdn.net/download/solid_j/10799765)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【springSpringaop](https://blog.csdn.net/qq_43418737/article/details/122704568)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值