aop实现乐观锁结合redis
说明
本项目需要对业务实现乐观锁,防止数据重复操作,结合redis实现。本文为代码实现,无其他说明,学习aop,redis,乐观锁请自行学习。
1、业务处理后置加锁
自定义注解
package com.e6yun.project.tms.revoke.annotation;
import com.e6yun.project.tms.revoke.common.ParamType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterDistributeHandle {
/**
* 唯一标识字段名
* 实体类参数名
* @return
*/
public String columnName();
/**
* 基本数据类型使用single
* 对象使用 mutile
* @return
*/
public ParamType type() default ParamType.SINGLE;
}
切面
package com.e6yun.project.tms.revoke.aspect;
import com.e6yun.project.common.redis.E6RedisService;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.annotation.AfterDistributeHandle;
import jodd.util.StringUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@Lazy(false)
public class AfterDistributeAspect {
Logger logger = LoggerFactory.getLogger(AfterDistributeAspect.class);
final String status = "Running";
@Resource
@Qualifier("reportRedis")
private E6RedisService redisService;
@Pointcut("@annotation(com.e6yun.project.tms.revoke.annotation.AfterDistributeHandle)")
private void cutMethod(){
}
/**
* 后置处理
* @param joinPoint
* @throws Throwable
*/
@AfterReturning("cutMethod()")
public void after(JoinPoint joinPoint) throws Throwable {
logger.info(">>>>>>>>>>>>>>>>加锁<<<<<<<<<<<<<<<<<<<<<");
// 获取方法传入参数
String orderNum = getColumnValue(joinPoint);
logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);
if (StringUtil.isNotBlank(orderNum)){
try {
//根据从redis获取对应value
redisService.setValue(orderNum, status, 60, TimeUnit.SECONDS);
}catch (Exception e){
redisService.delete(orderNum);
logger.info(">>>>>>>>>>>>>>redis加锁失败{},参数:{}",e ,orderNum);
}
}
}
/**
* 后置异常处理
* @param joinPoint
* @throws Throwable
*/
@AfterThrowing(pointcut = "cutMethod()")
public void afterThrow(JoinPoint joinPoint) throws Throwable {
logger.info(">>>>>>>>>>>>>>>>异常去锁<<<<<<<<<<<<<<<<<<<<<");
// 获取方法传入参数
String orderNum = getColumnValue(joinPoint);
logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);
if (StringUtil.isNotBlank(orderNum)){
try {
//从redis删除对应value
redisService.delete(orderNum);
}catch (Exception e){
logger.info(">>>>>>>>>>>>>>redis加锁失败{},参数:{}",e ,orderNum);
}
}
}
/**
* 获取方法中声明的注解
*
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
private static AfterDistributeHandle getDeclaredAnnotation(JoinPoint joinPoint) throws NoSuchMethodException {
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 反射获取目标类
Class<?> targetClass = joinPoint.getTarget().getClass();
// 拿到方法对应的参数类型
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
// 根据类、方法、参数类型(重载)获取到方法的具体信息
Method objMethod = targetClass.getMethod(methodName, parameterTypes);
// 拿到方法定义的注解信息
AfterDistributeHandle annotation = objMethod.getDeclaredAnnotation(AfterDistributeHandle.class);
// 返回
return annotation;
}
/**
* 根据注解设置获取对象列参数值
* @param joinPoint
* @return
*/
protected static String getColumnValue(JoinPoint joinPoint){
AfterDistributeHandle handle = null;
Object[] params = joinPoint.getArgs();
try {
handle = getDeclaredAnnotation(joinPoint);
for (Object param : params) {
// 修改为参数判断,单参数和对象
handle.type().value();
if (handle.type().value() == ParamType.SINGLE.value()){
//根据列明 获取单参数信息
return String.valueOf(param);
}
if (handle.type().value() == ParamType.MUTIL.value()){
Field[] fields = param.getClass().getDeclaredFields();
for (Field field : fields) {
// 私有属性必须设置访问权限
field.setAccessible(true);
String name = field.getName();
if (name.equals(handle.columnName())){
Object a = field.get(param);
System.out.println(a);
return String.valueOf(a);
}else {
continue;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2、并发业务进入
注解
package com.e6yun.project.tms.revoke.annotation;
import com.e6yun.project.tms.revoke.common.ParamType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 返回类型为E6Wrapper
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RevokeHandle {
/**
* 唯一标识字段名
* 实体类参数名
* @return
*/
public String columnName();
/**
* 基本数据类型 使用single
* 入参为 对象 使用 mutile
* @return
*/
public ParamType type() default ParamType.SINGLE;
}
并发前置处理,判断是否加锁
package com.e6yun.project.tms.revoke.aspect;
import com.alibaba.fastjson.JSON;
import com.e6yun.project.common.redis.E6RedisService;
import com.e6yun.project.common.vo.E6Wrapper;
import com.e6yun.project.tms.revoke.annotation.CleanCacheHandle;
import com.e6yun.project.tms.revoke.annotation.RevokeHandle;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.utils.AspectUtil;
import jodd.util.StringUtil;
import net.bytebuddy.implementation.bytecode.Throw;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.rmi.server.ExportException;
@Aspect
@Component
@Lazy(false)
public class RevokeAspect {
Logger logger = LoggerFactory.getLogger(RevokeAspect.class);
final String status = "Running";
@Resource
@Qualifier("reportRedis")
private E6RedisService redisService;
@Pointcut("@annotation(com.e6yun.project.tms.revoke.annotation.RevokeHandle)")
private void cutMethod(){
}
/**
* 前置通知:在目标方法执行前调用
*/
@Around("cutMethod()")
public Object begin(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info(">>>>>>>>>>>>>>>>撤销加锁判断<<<<<<<<<<<<<<<<<<<<<");
// 获取方法传入参数
String orderNum = getColumnValue(joinPoint);
logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);
if (StringUtil.isNotBlank(orderNum)){
try {
//根据num从redis获取对应value
String catchInfo = redisService.getValue(orderNum);
//判断当前加锁情况
if (StringUtil.isNotBlank(catchInfo) && catchInfo.equals(status)){
//已被锁定,直接返回禁止操作
E6Wrapper wrapper = new E6Wrapper();
wrapper.setCode(E6Wrapper.CODE_NO_PERMISSION);
wrapper.setMessage("当前数据正在处理,请稍后再试");
return wrapper;
}
}catch (Exception e){
logger.info(">>>>>>>>>>>>>>获取redis缓存失败:{}",e);
}
}
//未被锁定,允许操作
return joinPoint.proceed();
}
/**
* 获取方法中声明的注解
*
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
private static RevokeHandle getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
Method objMethod = AspectUtil.getMethod(joinPoint);
// 拿到方法定义的注解信息
RevokeHandle annotation = objMethod.getDeclaredAnnotation(RevokeHandle.class);
// 返回
return annotation;
}
/**
* 根据注解设置获取对象列参数值
* @param joinPoint
* @return
*/
public static String getColumnValue(ProceedingJoinPoint joinPoint){
RevokeHandle handle = null;
Object[] params = joinPoint.getArgs();
try {
handle = getDeclaredAnnotation(joinPoint);
for (Object param : params) {
// 修改为参数判断,单参数和对象
handle.type().value();
if (handle.type().value() == ParamType.SINGLE.value()){
//根据列明 获取单参数信息
return String.valueOf(param);
}
if (handle.type().value() == ParamType.MUTIL.value()){
Field[] fields = param.getClass().getDeclaredFields();
for (Field field : fields) {
// 私有属性必须设置访问权限
field.setAccessible(true);
String name = field.getName();
if (name.equals(handle.columnName())){
Object a = field.get(param);
System.out.println(a);
return String.valueOf(a);
}else {
continue;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3、业务完成取消加锁
注解
package com.e6yun.project.tms.revoke.annotation;
import com.e6yun.project.tms.revoke.common.ParamType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 返回类型为E6Wrapper
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CleanCacheHandle {
/**
* 唯一标识字段名
* 实体类参数名
* @return
*/
public String columnName();
/**
* 基本数据类型 使用single
* 入参为 对象 使用 mutile
* @return
*/
public ParamType type() default ParamType.SINGLE;
}
切面实现,删除redis缓存
package com.e6yun.project.tms.revoke.aspect;
import com.e6yun.project.common.redis.E6RedisService;
import com.e6yun.project.common.vo.E6Wrapper;
import com.e6yun.project.tms.revoke.annotation.CleanCacheHandle;
import com.e6yun.project.tms.revoke.annotation.RevokeHandle;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.utils.AspectUtil;
import jodd.util.StringUtil;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@Aspect
@Component
@Lazy(false)
public class CleanCacheAspect {
Logger logger = LoggerFactory.getLogger(CleanCacheAspect.class);
final String status = "Running";
@Resource
@Qualifier("reportRedis")
private E6RedisService redisService;
@Pointcut("@annotation(com.e6yun.project.tms.revoke.annotation.CleanCacheHandle)")
private void cutMethod(){
}
/**
* 前置通知:在目标方法执行前调用
*/
@Around("cutMethod()")
public Object begin(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info(">>>>>>>>>>>>>>>>删除加锁缓存<<<<<<<<<<<<<<<<<<<<<");
// 获取方法传入参数
String orderNum = getColumnValue(joinPoint);
logger.info(">>>>>>>>>>>>>>唯一标识列参数:{}",orderNum);
if (StringUtil.isNotBlank(orderNum)){
try {
//根据num从redis获取对应value
redisService.delete(orderNum);
}catch (Exception e){
logger.info(">>>>>>>>>>>>>>删除redis缓存失败:{}",e);
}
}
//未被锁定,允许操作
return joinPoint.proceed();
}
/**
* 获取方法中声明的注解
*
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
private static CleanCacheHandle getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
Method objMethod = AspectUtil.getMethod(joinPoint);
// 拿到方法定义的注解信息
CleanCacheHandle annotation = objMethod.getDeclaredAnnotation(CleanCacheHandle.class);
// 返回
return annotation;
}
/**
* 根据注解设置获取对象列参数值
* @param joinPoint
* @return
*/
public static String getColumnValue(ProceedingJoinPoint joinPoint){
CleanCacheHandle handle = null;
Object[] params = joinPoint.getArgs();
try {
handle = getDeclaredAnnotation(joinPoint);
for (Object param : params) {
// 修改为参数判断,单参数和对象
handle.type().value();
if (handle.type().value() == ParamType.SINGLE.value()){
//根据列明 获取单参数信息
return String.valueOf(param);
}
if (handle.type().value() == ParamType.MUTIL.value()){
Field[] fields = param.getClass().getDeclaredFields();
for (Field field : fields) {
// 私有属性必须设置访问权限
field.setAccessible(true);
String name = field.getName();
if (name.equals(handle.columnName())){
Object a = field.get(param);
System.out.println(a);
return String.valueOf(a);
}else {
continue;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
公共util
package com.e6yun.project.tms.revoke.utils;
import com.e6yun.project.tms.revoke.common.ParamType;
import com.e6yun.project.tms.revoke.annotation.RevokeHandle;
import org.apache.poi.ss.formula.functions.T;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class AspectUtil {
/**
* 获取方法中声明的注解
*
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
public static Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 反射获取目标类
Class<?> targetClass = joinPoint.getTarget().getClass();
// 拿到方法对应的参数类型
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
// 根据类、方法、参数类型(重载)获取到方法的具体信息
Method objMethod = targetClass.getMethod(methodName, parameterTypes);
return objMethod;
}
}
注:其中redis服务请自行配置。