比如:我们想删除角色时,需要先判断是否有用户使用了该角色。
思路:我们在删除的方法上加上注解,标注需要校验哪个字段,在哪个表里有被关联。
更于2020-03-12:哈哈,我又有优化了,贴上最新的切面代码(只贴切面代码,注解使用和测试在最下方)
切面代码:
@Aspect
@Component
@Slf4j
public class DeleteCheckAspect {
@Autowired
private ApplicationContext applicationContext;
/**
* @throws
* @Title: doSetFieldValue
* @Description: 根据注解校验关联对象是否全部删除
* @param: [pjp, interfaceDeleteCheck, object]
* @return: java.lang.Object
* @author: guzy
* @date: 2020-03-11 11:35
* @version V1
* @modify
* @修改人:
* @修改日期:
* @修改原因:
* @修改描述:
*/
@Around("@annotation(com.sendinfo.ski.common.aspect.enums.AnnoDeleteCheck)" +
"&&@annotation(annoDeleteCheck)" +
"&&args(object)")
public Object deleteCheck(ProceedingJoinPoint pjp,
AnnoDeleteCheck annoDeleteCheck, Object object) throws Throwable {
log.info("开始对" + pjp.getSignature().getName() + "进行删除前校验");
Class cla = object.getClass();
for (DeleteCheckObj deleteCheckObj : annoDeleteCheck.objs()) {
//使用对象的哪个字段去查询
String code = deleteCheckObj.code();
//使用的biz的calss
Class bizClass = deleteCheckObj.bizClass();
//查询的对象的class
Class objClass = deleteCheckObj.objectClass();
//查询对象使用哪个字段
String objCode = deleteCheckObj.objCode();
//方法
Method method = null;
//接口的对象
Object mthodClass = null;
//方法传参的对象
Object obj = null;
try {
//获取到查询需要的值(此处没有使用现成工具类是因为,我们的属性有可能来自父类)
Object codeV = ReflectUtils.getObjValue(cla, object, code);
Object corpCode = ReflectUtils.getObjValue(cla, object, "corpCode");
//获取biz对象
mthodClass = applicationContext.getBean(bizClass);
//获取list方法(list方法属于BaseBiz,且使用的传参是BaseEntity,所以要获取superclass)
method = bizClass.getMethod("list", objClass.getSuperclass());
//拿到list方法需要的对象
obj = objClass.newInstance();
//list方法需要对象的字段进行设值
ReflectUtils.setObjValue(objClass, obj, objCode, codeV);
ReflectUtils.setObjValue(objClass, obj, "corpCode", corpCode);
} catch (Exception e) {
e.printStackTrace();
log.error(pjp.getSignature().getName() + "在使用InterfaceDeleteCheck时,使用错误");
throw new BusinessException("代码内部错误");
}
//方法使用
Object res = method.invoke(mthodClass, obj);
//list方法返回List对象,为了万一,进行判断
if (res instanceof Collection) {
if (((Collection) res).size() > 0) {
throw new BusinessException("关联" + deleteCheckObj.show() + "中的数据未删除");
}
} else {
if (res != null) {
throw new BusinessException("关联" + deleteCheckObj.show() + "中的数据未删除");
}
}
}
log.info("对" + pjp.getSignature().getName() + "校验成功,执行删除");
//判断无异常,执行删除
Object result = pjp.proceed();
return result;
}
}
自定义工具类ReflectUtils
public class ReflectUtils {
/**
* @throws
* @Title: setObjValue
* @Description: 给object的name属性赋值obj
* @param: [cla, object, name, obj]
* @return: void
* @author: guzy
* @date: 2020-03-12 14:12
* @version V1
* @modify
* @修改人:
* @修改日期:
* @修改原因:
* @修改描述:
*/
public static void setObjValue(Class cla, Object object, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = getCodeFile(cla, name);
//获取权限
field.setAccessible(true);
field.set(object, value);
}
/**
* @throws
* @Title: getObjValue
* @Description: 通过反射,获取object的属性为name的值
* @param: [cla, object, name]
* @return: java.lang.Object
* @author: guzy
* @date: 2020-03-12 14:04
* @version V1
* @modify
* @修改人:
* @修改日期:
* @修改原因:
* @修改描述:
*/
public static Object getObjValue(Class cla, Object object, String name) throws NoSuchFieldException, IllegalAccessException {
Field field = getCodeFile(cla, name);
//获取权限
field.setAccessible(true);
return field.get(object);
}
/**
* @throws
* @Title: getCodeFile
* @Description: 通过Class和属性名获取属性file
* @param: [cla, code]
* @return: java.lang.reflect.Field
* @author: guzy
* @date: 2020-03-12 13:56
* @version V1
* @modify
* @修改人:
* @修改日期:
* @修改原因:
* @修改描述:
*/
public static Field getCodeFile(Class cla, String code) throws NoSuchFieldException {
NoSuchFieldException throwe = null;
while (cla != null) {
try {
Field objCodeFile = cla.getDeclaredField(code);
return objCodeFile;
} catch (NoSuchFieldException e) {
//可能数据在父类里
if (throwe == null) {
throwe = e;
}
cla = cla.getSuperclass();
}
}
//一直找不到,抛出异常
throw throwe;
}
}
定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterfaceDeletedCheck {
//在删除类中的属性名
String[] codes();
//需要校验的类biz
Class[] bizs();
//需要校验的对象class
Class[] objects();
//在校验对象中的属性名
String[] objCodes();
//校验的表的名称
String[] shows();
}
定义切面:
@Aspect
@Component
@Slf4j
public class DeleteCheckAspect {
@Autowired
private ApplicationContext applicationContext;
@Around("@annotation(com.sendinfo.ski.common.aspect.enums.InterfaceDeletedCheck)" +
"&&@annotation(interfaceDeletedCheck)" +
"&&args(object)")
public Object doSetFieldValue(ProceedingJoinPoint pjp,
InterfaceDeletedCheck interfaceDeletedCheck, Object object) throws Throwable {
int len = interfaceDeletedCheck.codes().length;
if (interfaceDeletedCheck.codes().length != interfaceDeletedCheck.bizs().length ||
interfaceDeletedCheck.codes().length != interfaceDeletedCheck.objects().length ||
interfaceDeletedCheck.codes().length != interfaceDeletedCheck.objCodes().length ||
interfaceDeletedCheck.codes().length != interfaceDeletedCheck.shows().length) {
log.error(pjp.getSignature().getName() + "在使用interfaceDeletedCheck时,使用错误");
throw new BusinessException("代码内部错误");
}
Class cla = object.getClass();
for (int i = 0; i < len; i++) {
//使用对象的哪个字段去查询
String code = interfaceDeletedCheck.codes()[i];
//使用的biz的calss
Class biz = interfaceDeletedCheck.bizs()[i];
//查询的对象的class
Class objClass = interfaceDeletedCheck.objects()[i];
//查询对象使用哪个字段
String objCode = interfaceDeletedCheck.objCodes()[i];
Method method = null;
Object mthodClass = null;
Object obj = null;
try {
//获取到使用对象的字段Field
Field codeFile = null;
if ("id".equals(code)) {
//id属于父节点的属性
codeFile = cla.getSuperclass().getDeclaredField(code);
} else {
codeFile = cla.getDeclaredField(code);
}
Field corpFile = cla.getDeclaredField("corpCode");
//获取权限
codeFile.setAccessible(true);
corpFile.setAccessible(true);
//获取到查询需要的值
Object codeV = codeFile.get(object);
Object corpCode = corpFile.get(object);
//获取biz对象
mthodClass = applicationContext.getBean(biz);
//获取list方法(list方法属于BaseBiz,且使用的传参是BaseEntity,所以要获取superclass)
method = biz.getMethod("list", objClass.getSuperclass());
//拿到list方法需要的对象
obj = objClass.newInstance();
//list方法需要对象的字段
Field objCodeFile = null;
if ("id".equals(code)) {
//id属于父节点的属性
objCodeFile = objClass.getSuperclass().getDeclaredField(objCode);
} else {
objCodeFile = objClass.getDeclaredField(objCode);
}
Field corpCodeFile = objClass.getDeclaredField("corpCode");
//获取权限,并且设置值
objCodeFile.setAccessible(true);
objCodeFile.set(obj, codeV);
corpCodeFile.setAccessible(true);
corpCodeFile.set(obj, corpCode);
} catch (Exception e) {
e.printStackTrace();
log.error(pjp.getSignature().getName() + "在使用interfaceDeletedCheck时,使用错误");
throw new BusinessException("代码内部错误");
}
//方法使用
Object res = method.invoke(mthodClass, obj);
//list方法返回List对象,为了万一,进行判断
if (res instanceof Collection) {
if (((Collection) res).size() > 0) {
throw new BusinessException("关联" + interfaceDeletedCheck.shows()[i] + "中的数据未删除");
}
} else {
if (res != null) {
throw new BusinessException("关联" + interfaceDeletedCheck.shows()[i] + "中的数据未删除");
}
}
}
//判断无异常,执行删除
Object result = pjp.proceed();
return result;
}
}
代码使用:
@InterfaceDeletedCheck(codes = {"cardTypeCode", "cardTypeCode"},
bizs = {SkiComboCardTypeBiz.class, PrtnoInfoBiz.class},
objects = {SkiComboCardType.class, PrtnoInfo.class},
objCodes = {"cardType", "cardType"},
shows = {"套票类型", "卡印刷表"})
public void deleteWithCheck(SkiCardType skiCardType) {
this.delete(skiCardType);
}
说明:list方法是我们公司封装的方法,传入对象,返回符合条件的所有对象。
注意:当要使用对象或者拦截的注解时,切面的注解上要加上。
注意反射对象时,方法和属性属于对象还是其父类上。
新版:
之前发现用的那个注解太傻了,还要在切面了判断长度,新的注解进行优化。代码逻辑没有更改。
注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DeleteCheckObj {
//在删除类中的属性名
String code();
//需要校验的类biz
Class bizClass();
//需要校验的对象class
Class objectClass();
//在校验对象中的属性名
String objCode();
//校验的表的名称
String show();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterfaceDeleteCheck {
DeleteCheckObj[] objs() default {};
}
切面:
@Aspect
@Component
@Slf4j
public class DeleteCheckAspect {
@Autowired
private ApplicationContext applicationContext;
@Around("@annotation(com.sendinfo.ski.common.aspect.enums.InterfaceDeleteCheck)" +
"&&@annotation(interfaceDeleteCheck)" +
"&&args(object)")
public Object doSetFieldValue(ProceedingJoinPoint pjp,
InterfaceDeleteCheck interfaceDeleteCheck, Object object) throws Throwable {
Class cla = object.getClass();
for (DeleteCheckObj deleteCheckObj : interfaceDeleteCheck.objs()) {
//使用对象的哪个字段去查询
String code = deleteCheckObj.code();
//使用的biz的calss
Class bizClass = deleteCheckObj.bizClass();
//查询的对象的class
Class objClass = deleteCheckObj.objectClass();
//查询对象使用哪个字段
String objCode = deleteCheckObj.objCode();
//方法
Method method = null;
//接口的对象
Object mthodClass = null;
//方法传参的对象
Object obj = null;
try {
//获取到使用对象的字段Field
Field codeFile = null;
if ("id".equals(code)) {
//id属于父节点的属性
codeFile = cla.getSuperclass().getDeclaredField(code);
} else {
codeFile = cla.getDeclaredField(code);
}
Field corpFile = cla.getDeclaredField("corpCode");
//获取权限
codeFile.setAccessible(true);
corpFile.setAccessible(true);
//获取到查询需要的值
Object codeV = codeFile.get(object);
Object corpCode = corpFile.get(object);
//获取biz对象
mthodClass = applicationContext.getBean(bizClass);
//获取list方法(list方法属于BaseBiz,且使用的传参是BaseEntity,所以要获取superclass)
method = bizClass.getMethod("list", objClass.getSuperclass());
//拿到list方法需要的对象
obj = objClass.newInstance();
//list方法需要对象的字段
Field objCodeFile = null;
if ("id".equals(code)) {
//id属于父节点的属性
objCodeFile = objClass.getSuperclass().getDeclaredField(objCode);
} else {
objCodeFile = objClass.getDeclaredField(objCode);
}
Field corpCodeFile = objClass.getDeclaredField("corpCode");
//获取权限,并且设置值
objCodeFile.setAccessible(true);
objCodeFile.set(obj, codeV);
corpCodeFile.setAccessible(true);
corpCodeFile.set(obj, corpCode);
} catch (Exception e) {
e.printStackTrace();
log.error(pjp.getSignature().getName() + "在使用InterfaceDeleteCheck时,使用错误");
throw new BusinessException("代码内部错误");
}
//方法使用
Object res = method.invoke(mthodClass, obj);
//list方法返回List对象,为了万一,进行判断
if (res instanceof Collection) {
if (((Collection) res).size() > 0) {
throw new BusinessException("关联" + deleteCheckObj.show() + "中的数据未删除");
}
} else {
if (res != null) {
throw new BusinessException("关联" + deleteCheckObj.show() + "中的数据未删除");
}
}
}
//判断无异常,执行删除
Object result = pjp.proceed();
return result;
}
}
使用
@InterfaceDeleteCheck(objs = {
@DeleteCheckObj(code = "cardTypeCode", bizClass = SkiComboCardTypeBiz.class,
objectClass = SkiComboCardType.class, objCode = "cardType", show = "套票类型"),
@DeleteCheckObj(code = "cardTypeCode", bizClass = PrtnoInfoBiz.class,
objectClass = PrtnoInfo.class, objCode = "cardType", show = "卡印刷表")
})
public void deleteWithCheck(SkiCardType skiCardType) {
this.delete(skiCardType);
}