技术点: 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,无法回滚事务。