最近项目需要版本号乐观锁,但发现每个需要加锁的地方都要做处理发现很繁琐很臃肿,所以使用aop切面+自定义注解来抽取实现乐观锁。
第一步使用@Aspect需要pom引入
<!--使用AspectJ方式注解需要相应的包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency> |
第二步: 自定义注解
package com.aop; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * 自定义尝试切面接口 */ // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 @Retention(RetentionPolicy.RUNTIME) //@interface定义一个注解 public @interface IsTryAgain { } |
第三步: 自定义一个异常 ApiException
第四部:编写AOP
package com.aop; import com.aop.exception.ApiException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; /** * 定义重试切面方法,是为了发生乐观锁异常时在一个全新的事务里提交上一次的操作, * 直到达到重试上限;因此切面实现 org.springframework.core.Ordered 接口, * 这样我们就可以把切面的优先级设定为高于事务通知 。 */ @Aspect @Component public class ConcurrentOperationExecutor implements Ordered { private int maxRetries = 3; private int order = 1; public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } public int getOrder() { return this.order; } /** * 切点可访问性修饰符与类可访问性修饰符的功能是相同的,它可以决定定义的切点可以在哪些类中可使用。 * 因为命名切点仅利用方法名及访问修饰符的信息,所以我们一般定义方法的返回类型为 void ,并且方法体为空 。 * * 所有加了自定义注解的地方切入 * */ @Pointcut("@annotation(IsTryAgain)") public void businessService() { } @Around("businessService()") @Transactional(rollbackFor = Exception.class) public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { int numAttempts = 0; do { numAttempts++; try { return pjp.proceed(); } catch (TryAgainException ex) { if (numAttempts > maxRetries) { throw new ApiException(1, "重试达到最大次数"); } else { System.out.println("=====try recovery====="); } } } while (numAttempts <= this.maxRetries); return null; } } |
方法类:
/** * update有排它锁,当当前事务update没提交之前,另外集群上面的事务不能更新需要等待 * @param content * @throws Exception */ @IsTryAgain @Transactional(rollbackFor = Exception.class) public void IsTryAgainMethod(String content) throws Exception{ HappyLockTest happyLockTest = happyLockTestMapper.selectByPrimaryKey(1L); happyLockTest.setContent(content); System.out.println(content+"--->"+happyLockTest.getVersion()); Thread.sleep(8000); System.out.println("开始--->"); happyLockTest.setVersion(happyLockTest.getVersion()); int temp = happyLockTestMapper.updateByPrimaryKeySelective(happyLockTest); if (temp == 0){ throw new TryAgainException("重试"); } System.out.println("结束了去查询吧------------》"); Thread.sleep(8000); System.out.println("---------------结束------------"); } |
sql:
<update id="updateByPrimaryKeySelective" parameterType="com.pojo.HappyLockTest" > update happy_lock_test <set > <if test="content != null" > content = #{content,jdbcType=VARCHAR}, </if> <if test="version != null" > version = #{version,jdbcType=INTEGER} + 1, </if> </set> where id = #{id,jdbcType=BIGINT} and version = #{version} </update> |
参考网友:https://blog.csdn.net/zhanghongzheng3213/article/details/50819539