技术点: Spring Aop 反射
背景:目前在做一个项目,做数据库设计的时候对一些表进行了埋点,比如跟我业务相关的每个表,都有create_time,create_user_id,create_user_name,update_time,update_user_id,update_user_name等字段
如果放在Dao处理,则我每一个表对应的每一个实体都需要setCreateUserId,setCreateUserName,setUpdateUserId,
setUpdateUserName 等等,代码咸的很臃肿,基于这样的背景,写了一个切面,处理这些公共的逻辑。
代码:
aspect 包:
注解
import java.lang.annotation.*; /** * @author qiwenshuai * @note * @since 18-11-8 14:00 by jdk 1.8 */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface OTADaoRecord { OTARecordEnum type() default OTARecordEnum.NONE; } 枚举
/** * @author qiwenshuai * @note * @since 18-11-9 09:17 by jdk 1.8 */ public enum OTARecordEnum { NONE("NONE",0), CREATE("CREATE",1), UPDATE("UPDATE",2), MAPCREATE("MAPCREATE",3), MAPUPDATE("MAPUPDATE",4), ; private String type; private Integer value; OTARecordEnum(String type,Integer value){ this.type=type; this.value=value; } public String getType() { return type; } public Integer getValue() { return value; } }
切面
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.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; /** * @author qiwenshuai * @note 每次修改和新增都要增加 userId和userName 为了简便,写个注解只关注业务层。 * @since 18-11-8 17:45 by jdk 1.8 */ @Aspect @Component public class RecordAspect { private static final Logger logger = LoggerFactory.getLogger(RecordAspect.class); private static final String CREATE_USER_ID_METHOD = "setCreateUserId"; private static final String CREATE_USER_NAME_METHOD = "setCreateUserName"; private static final String CREATE_DATA_SOURCE_METHOD = "setDataSource"; private static final String CREATE_MAP_USER_ID_METHOD = "createUserId"; private static final String CREATE_MAP_USER_NAME_METHOD = "createUserName"; private static final String CREATE_MAP_DATA_SOURCE_METHOD = "dataSource"; private static final String UPDATE_MAP_USER_ID = "updateUserId"; private static final String UPDATE_MAP_USER_NAME = "updateUserName"; private static final String UPDATE_USER_ID_METHOD = "setUpdateUserId"; private static final String UPDATE_USER_NAME_METHOD = "setUpdateUserName"; protected final HttpServletRequest request; @Autowired public RecordAspect(HttpServletRequest request) { this.request = request; } //Controller层切点 @Pointcut("@annotation(cn.futuremove.tsp.vehicle.aspect.OTADaoRecord)") public void OTADaoRecord() { } /** * 环绕AOP */ @Around("OTADaoRecord()") public Object around(ProceedingJoinPoint pjp) throws Throwable { try { if (OTARecordEnum.UPDATE.equals(getControllerMethodDescription(pjp))) { setUpdateProperty(pjp); } else if (OTARecordEnum.CREATE.equals(getControllerMethodDescription(pjp))) { setCreateProperty(pjp); } else if (OTARecordEnum.MAPCREATE.equals(getControllerMethodDescription(pjp))) { setMapCreateProperty(pjp); } else if (OTARecordEnum.MAPUPDATE.equals(getControllerMethodDescription(pjp))) { setMapUpdateProperty(pjp); } return pjp.proceed(); } catch (Exception e) { logger.error("切面设置值发生错误:{}", e); return null; } } /** * 反射获取type值的逻辑 */ private OTARecordEnum getControllerMethodDescription(ProceedingJoinPoint pjp) throws ClassNotFoundException { String targetName = pjp.getTarget().getClass().getName(); String methodName = pjp.getSignature().getName(); Object[] arguments = pjp.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); OTARecordEnum recordEnum = null; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { recordEnum = method.getAnnotation(OTADaoRecord.class).type(); break; } } } return recordEnum; } private void setCreateProperty(ProceedingJoinPoint pjp) throws InvocationTargetException, IllegalAccessException { //create的逻辑 Object[] arguments = pjp.getArgs(); for (Object object : arguments) { Method[] methods = object.getClass().getMethods(); createInvoke(methods, object); } } private void setUpdateProperty(ProceedingJoinPoint pjp) throws InvocationTargetException, IllegalAccessException { //create的逻辑 Object[] arguments = pjp.getArgs(); for (Object object : arguments) { Method[] methods = object.getClass().getMethods(); updateInvoke(methods, object); } } private void setMapCreateProperty(ProceedingJoinPoint pjp) { Object[] arguments = pjp.getArgs(); for (Object o : arguments) { if (o instanceof Map) { for (Object key : ((Map) o).keySet()) { Map map = (Map)((Map<String,Object>) o).get(key); map.put(CREATE_MAP_USER_ID_METHOD,"999"); map.put(CREATE_MAP_USER_NAME_METHOD,"create"); map.put(CREATE_MAP_DATA_SOURCE_METHOD,"1"); } } } } private void setMapUpdateProperty(ProceedingJoinPoint pjp) { Object[] arguments = pjp.getArgs(); for (Object o : arguments) { if (o instanceof Map) { for (Object key : ((Map) o).keySet()) { Map map = (Map)((Map<String,Object>) o).get(key); map.put(UPDATE_MAP_USER_ID,"999"); map.put(UPDATE_MAP_USER_NAME,"update"); } } } } private void createInvoke(Method[] methods, Object object) throws InvocationTargetException, IllegalAccessException { for (Method method : methods) { if (method.getName().equals(CREATE_USER_ID_METHOD)) { //后期取值 method.invoke(object, "99999"); } if (method.getName().equals(CREATE_USER_NAME_METHOD)) { //后期取值 method.invoke(object, "create"); } if (method.getName().equals(CREATE_DATA_SOURCE_METHOD)) { //创建数据的时候,记录数据源 method.invoke(object, "1"); } } } private void updateInvoke(Method[] methods, Object object) throws InvocationTargetException, IllegalAccessException { for (Method method : methods) { if (method.getName().equals(UPDATE_USER_ID_METHOD)) { //后期取值 method.invoke(object, "99999"); } if (method.getName().equals(UPDATE_USER_NAME_METHOD)) { //后期取值 method.invoke(object, "update"); } } } } 具体的Controller调用只需要在Controller层加上注解就可以了
例如:
@OTADaoRecord(type = OTARecordEnum.MAPUPDATE)
@OTADaoRecord(type = OTARecordEnum.CREATE)
等等。。。。。。。
要注意的是,使用声明式事务注解的时候,Controller层不要写
@Transactional
基于Spring事务在Service层的传播性,要在Service处理所有的Dao逻辑,并且在Service层加上注解,并且不要加try-catch,否则Controller拦截不到Exception,无法回滚事务。