在项目开发中,我们的数据库设计往往会包括创建人、创建时间、修改人、修改时间等公共字段,这些公共字段有助于帮助我们知道是谁对数据库记录进行了更改,但这带来的一个问题就是在进行数据库插入或修改的时候,这些字段需要手动设置,这会给我们带来很大不便,这里我简单讲一下采用aop方式进行公共字段填充,因为项目集成了tk.mybatis,很多单表的操作并没有显示指定接口,所以采用在service层方法进行切入来设置。以下列出用到的类,做一个思路的梳理,具体应用可自行更改。
其中SetCommonField是一个注解,该注解打在方法上即可启用aop,该注解包含一个methodType属性,这个属性定义数据库的四种操作(增删改查),在CommonFieldAspect类中定义了对应的常量。
功能实现思路是解析前端请求得出用户id(这里我的代码有点问题,各位可自行考虑怎么获取用户id),然后根据用户id查询redis缓存得到用户信息保存到UserInfo,然后再根据不同的methodType将userInfo转换为CommonField,最后我们读取切点方法的第一个参数,并将其与CommonField类同名的字段进行设置。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* aop设置公共字段增强处理注解
*
* @author TenSunLee
* @createdTime 2019/8/15 11点18分
* @version 1.0.0
* @updateBy TenSunLee
* @updatedTime 2019/8/15 11点18分
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SetCommonField {
/**
* 标注方法类型
* @return 方法类型字符串
*/
String methodType();
}
import java.sql.Timestamp;
import java.util.List;
/**
* 封装所有需要设置的公共字段类
*
* @author TenSunLee
* @createdTime 2019/8/15 13点38分
* @version 1.0.0
* @updateBy TenSunLee
* @updatedTime 2019/8/15 13点41分
*
*/
public class CommonField {
/**
* 组织机构id
*/
private Long orgId;
/**
* 公司id
*/
private Long companyId;
private Boolean manager;
private Byte managerLevel;
private List roles;
/**
* 创建人id
*/
private Long createdBy;
/**
* 创建时间
*/
private Timestamp createdTime;
/**
* 最后修改人id
*/
private Long updatedBy;
/**
* 最后修改时间
*/
private Timestamp updatedTime;
@Override
public String toString() {
return "CommonField{" +
"orgId=" + orgId +
", companyId=" + companyId +
", manager=" + manager +
", managerLevel=" + managerLevel +
", roles=" + roles +
", createdBy=" + createdBy +
", createdTime=" + createdTime +
", updatedBy=" + updatedBy +
", updatedTime=" + updatedTime +
'}';
}
public Long getOrgId() {
return orgId;
}
public void setOrgId(Long orgId) {
this.orgId = orgId;
}
public Long getCompanyId() {
return companyId;
}
public void setCompanyId(Long companyId) {
this.companyId = companyId;
}
public Boolean getManager() {
return manager;
}
public void setManager(Boolean manager) {
this.manager = manager;
}
public Byte getManagerLevel() {
return managerLevel;
}
public void setManagerLevel(Byte managerLevel) {
this.managerLevel = managerLevel;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
public Long getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Long createdBy) {
this.createdBy = createdBy;
}
public Timestamp getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Timestamp createdTime) {
this.createdTime = createdTime;
}
public Long getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(Long updatedBy) {
this.updatedBy = updatedBy;
}
public Timestamp getUpdatedTime() {
return updatedTime;
}
public void setUpdatedTime(Timestamp updatedTime) {
this.updatedTime = updatedTime;
}
}
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;
/**
* 公共字段设置aspect
*
* @author TenSunLee
* @version 1.0.0
* @createdTime 2019/8/15 11:08
* @updateBy TenSunLee
* @updatedTime 2019/8/22 14:45
*/
@Aspect
@Component
public class CommonFieldAspect {
/**
* 注解所需参数值,表示类型为查询方法
*/
public static final String TYPE_QUERY = "query";
/**
* 注解所需参数值,表示类型为删除方法
*/
public static final String TYPE_DELETE = "delete";
/**
* 注解所需参数值,表示类型为插入方法
*/
public static final String TYPE_INSERT = "insert";
/**
* 注解所需参数值,表示类型为更新方法
*/
public static final String TYPE_UPDATE = "update";
/**
* 默认方法类型
*/
private static final String DEFAULT_METHOD_TYPE = TYPE_UPDATE;
private final RedisTemplate redisTemplate;
@Autowired
public CommonFieldAspect(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
init();
}
/**
* 设置redisTemplate采用stringSerializer
*/
private void init(){
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
}
/**
* 定义增强处理切入点为CommonField注解所注方法
*/
@Pointcut("@annotation(com.bosssoft.bes.base.commonfield.annotation.SetCommonField)")
public void commonFieldPoint() {
}
/**
* 设置公共字段的方法
*
* @param joinPoint 连接点
*/
@Before("commonFieldPoint()")
public void setCommonField(JoinPoint joinPoint) throws ServiceException {
//获取httprequest对象,解析参数获取CommonRequest中携带的请求用户id
Long userId = getUserIdFromRequest();
if(null == userId){
throw new ServiceException(SystemExceptionEnum.SYSTEM_BASE_COMMON_FIELD_USER_NOT_FOUND_ON_REQUEST);
}
//通过userId获取相应字段值
CommonField commonField = getCommonFieldByUserId(userId);
//获取methodType
String methodType = getMethodType(joinPoint);
//根据方法类型从commonField对象中抽取不同字段设置进入切入点方法参数中
int result = setFields(commonField, joinPoint, methodType);
}
/**
* 根据methodType抽取不同的commonField中字段设置进切入点方法参数
*
* @param commonField 取到的公共字段值
* @param joinPoint 连接点
* @param methodType 所切方法类型
* @return int 执行情况 0表示正常完成
*/
private int setFields(CommonField commonField, JoinPoint joinPoint, String methodType) throws ServiceException {
if (!TYPE_INSERT.equals(methodType)) {
commonField.setCreatedBy(null);
commonField.setCreatedTime(null);
if (TYPE_QUERY.equals(methodType)) {
commonField.setUpdatedBy(null);
commonField.setUpdatedTime(null);
}
if (TYPE_UPDATE.equals(methodType)) {
commonField.setOrgId(null);
commonField.setCompanyId(null);
}
}
//获取切点第一个参数
Object[] args = joinPoint.getArgs();
Object param = null;
if (null != args) {
param = args[0];
}
if (null == param) {
return 1;
}
Field[] fields = FieldUtils.getAllFields(commonField.getClass());
Field[] fieldParams = FieldUtils.getAllFields(param.getClass());
//遍历CommonField所有字段
try {
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue = field.get(commonField);
//字段非null时将其设值给切点参数param
if (null != fieldValue) {
//获取param对应的setter方法后执行
String fieldName = field.getName();
//by lujinshan 循环遍历参数中是否存在需要被填充的字段
for (Field fieldParam : fieldParams) {
String fieldParamName = fieldParam.getName();
if (fieldName.equals(fieldParamName)) {
fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method fieldSetter = null;
if (fieldParam.getType().getSimpleName().equals("Date")) {
fieldSetter = param.getClass().getMethod("set" + fieldName, Date.class);
fieldValue = new Date();
} else {
fieldSetter = param.getClass().getMethod("set" + fieldName, field.getType());
}
fieldSetter.invoke(param, fieldValue);
}
}
}
}
} catch (Exception e) {
throw new ServiceException(SystemExceptionEnum.SYSTEM_BASE_COMMON_FIELD_SET_ERROR);
}
return 0;
}
/**
* 获取请求中的用户id
*
* @return 用户id, 未找到返回null
*/
private Long getUserIdFromRequest() throws ServiceException {
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder
.getRequestAttributes())).getRequest();
Long userId = Long.valueOf(request.getHeader("userId"));
return userId;
}
/**
* 根据用户id查找信息并封装到CommonField对象中返回
*
* @param userId 用户id
* @return 需要设置的公共字段对象
*/
private CommonField getCommonFieldByUserId(Long userId) throws ServiceException {
String redisKey = "user_info_" + userId;
ValueOperations valueOperations = redisTemplate.opsForValue();
UserInfo userInfo = JSONObject.parseObject(String.valueOf(valueOperations.get(redisKey)),UserInfo.class);
if (userInfo == null) {
throw new ServiceException(SystemExceptionEnum.SYSTEM_BASE_COMMON_FIELD_USER_NOT_FOUND_ON_CACHE);
}
CommonField commonField = new CommonField();
BeanUtils.copyProperties(userInfo,commonField);
commonField.setOrgId(userInfo.getOrgId());
commonField.setCompanyId(userInfo.getCompanyId());
commonField.setCreatedBy(userInfo.getId());
commonField.setCreatedTime(Timestamp.valueOf(LocalDateTime.now()));
commonField.setUpdatedBy(userInfo.getId());
commonField.setUpdatedTime(commonField.getCreatedTime());
System.out.println(commonField);
return commonField;
}
/**
* 获取注解中指定的methodType
*
* @param joinPoint 连接点
* @return 方法类型
*/
private String getMethodType(JoinPoint joinPoint) {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
SetCommonField commonFieldAnno = method.getAnnotation(SetCommonField.class);
String methodType = DEFAULT_METHOD_TYPE;
if (null != commonFieldAnno) {
if (TYPE_UPDATE.equals(commonFieldAnno.methodType())) {
methodType = TYPE_UPDATE;
} else if (TYPE_QUERY.equals(commonFieldAnno.methodType())) {
methodType = TYPE_QUERY;
} else if (TYPE_INSERT.equals(commonFieldAnno.methodType())) {
methodType = TYPE_INSERT;
} else if (TYPE_DELETE.equals(commonFieldAnno.methodType())) {
methodType = TYPE_DELETE;
}
}
return methodType;
}
}
/**
* 角色表简略信息
*/
public class RoleSimpleInfo {
protected Long id;
protected Long orgId;
protected Long companyId;
protected String roleName;
@Override
public String toString() {
return "RoleSimpleInfo{" +
"id=" + id +
", orgId=" + orgId +
", companyId=" + companyId +
", roleName='" + roleName + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getOrgId() {
return orgId;
}
public void setOrgId(Long orgId) {
this.orgId = orgId;
}
public Long getCompanyId() {
return companyId;
}
public void setCompanyId(Long companyId) {
this.companyId = companyId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
package com.bosssoft.bes.base.commonfield.aspect;
import java.io.Serializable;
import java.util.List;
/**
*
*
* @author TenSunLee
* @createdTime 2019/9/20 14:53
* @version 1.0.0
* @updateBy TenSunLee
* @updatedTime 2019/9/20 14:53
*
*/
public class UserInfo implements Serializable {
protected Long id;
protected Long companyId;
protected Long orgId;
protected Boolean manager;
protected Byte managerLevel;
protected List roles;
public UserInfo() {
}
@Override
public String toString() {
return "UserInfo{" +
"id=" + id +
", companyId=" + companyId +
", orgId=" + orgId +
", manager=" + manager +
", managerLevel=" + managerLevel +
", roles=" + roles +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCompanyId() {
return companyId;
}
public void setCompanyId(Long companyId) {
this.companyId = companyId;
}
public Long getOrgId() {
return orgId;
}
public void setOrgId(Long orgId) {
this.orgId = orgId;
}
public Boolean getManager() {
return manager;
}
public void setManager(Boolean manager) {
this.manager = manager;
}
public Byte getManagerLevel() {
return managerLevel;
}
public void setManagerLevel(Byte managerLevel) {
this.managerLevel = managerLevel;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
}