SpringAOP注解方式记录操作日志(操作模块,操作功能,调用方法,主键信息等)支持多笔操作时记录
来源:互联网 发布:祸害成患妖成灾网络剧1 编辑:程序博客网 时间:2024/05/22 10:33
开源项目地址: github开源链接
使用AOP切入的方式记录操作日志,本代码主要采用枚举作为记录方式,具体代码如下.
首先先定义先关枚举:
/** * 枚举公共接口 * @author LeiYong * */public interface EnumSuper {/** * 获取值 * @return */public String getValue();/** * 获取描述信息 * @return */public String getDiscription();}
public enum LogOperateEnum implements EnumSuper{Select("0","查询"),Save("1","新增"),Update("2","修改"),Delete("3","删除");private String value;private String discription;LogOperateEnum(String value, String discription) {this.value = value;this.discription = discription;}@Overridepublic String getValue() {return value;}@Overridepublic String getDiscription() {return discription;}}
public enum LogEnum implements EnumSuper{Sys("系统",""),Activity("活动",""),Shop("商城",""),Usr("用户",""),Scd("预约","");private String value;private String discription;LogEnum(String value, String discription) {this.value = value;this.discription = discription;}@Overridepublic String getValue() {return value;}@Overridepublic String getDiscription() {return discription;}}
接下来定义注解Logger
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface Logger {/** * 操作类型 0-查询 1-新增 2-修改 3-删除 * @return */LogOperateEnum type();/** * 所属模块 * @return */LogEnum model();/** * 业务主键 * @return */String title() default "#pk";}
最后LoggerAspect切面类
此处keyGenerate为主键生成,参考另一篇博文,使用redis生成数据库主键自增
使用redis生成数据库主键自增
package com.cykj.base.core.aop;import java.lang.reflect.Method;import java.util.Date;import javax.servlet.http.HttpServletRequest;import org.apache.commons.lang3.StringUtils;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.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.cykj.base.common.annotation.Logger;import com.cykj.base.common.system.SystemConfig;import com.cykj.base.common.system.properties.AdminPropertiesConfig;import com.cykj.base.common.util.PropertiesCacheUtil;import com.cykj.base.common.util.StringUtil;import com.cykj.base.core.model.log.LogOperater;import com.cykj.base.core.model.sys.SysUser;import com.cykj.base.core.model.usr.UsrUser;import com.cykj.base.core.model.usr.UsrUserSlave;import com.cykj.base.core.provide.log.LogOperaterService;import com.cykj.base.core.util.AopUtil;import com.cykj.base.core.util.WebUtils;/** * 操作日志 * @author LeiYong * */@Aspect@Componentpublic class LoggerAspect {@Autowiredprivate LogOperaterService logOperaterService;@Pointcut("@annotation(com.cykj.base.common.annotation.Logger)") private void loggerMethod(){}//定义一个切入点,此处Logger为Logger类全路径 /** * 记录操作日志 * * @param jp * @return * @throws Throwable */@Around("loggerMethod()")public Object logger(ProceedingJoinPoint jp) throws Throwable {Object result = null;Boolean logEnable = SystemConfig.LOG_ENABLE;result = AopUtil.executeJoinPointMethod(jp, jp.getArgs());if (logEnable&&result!=null) {LogOperater lo = null;SysUser su = getLoginSysUser();if (su!=null) {lo = getLogBySysUser(su, jp);}else {UsrUserSlave uu = getLoginUsrUser();lo = getLogByUsrUser(uu, jp);}if (lo!=null) {logOperaterService.insertSelective(lo);}}return result;}/** * 记录系统用户操作日志 * @param user * @param jp * @return */private LogOperater getLogBySysUser(SysUser user,ProceedingJoinPoint jp){LogOperater logOperater = getLogger(jp);if (logOperater!=null) {logOperater.setCreateBy(user.getPk());logOperater.setFrom("0");logOperater.setOrgPk(user.getOrgPk());}return logOperater;}/** * 记录微信用户操作日志 * @param user * @param jp * @return */private LogOperater getLogByUsrUser(UsrUser user,ProceedingJoinPoint jp){LogOperater logOperater = getLogger(jp);if (logOperater!=null) {logOperater.setCreateBy(user.getPk());logOperater.setFrom("1");//logOperater.setOrgPk(user.getOrgPk());}return logOperater;}private LogOperater getLogger(ProceedingJoinPoint jp){//获取切入点方法及参数Method method = AopUtil.getMethod(jp);Logger t = AopUtil.getMethodAnnotation(method, Logger.class);try {Boolean flag = PropertiesCacheUtil.loadProjectProperties(AdminPropertiesConfig.LOGGER_CONFIG).getBoolean(t.type().toString());//未配置或true都记录日志,仅为false时不记录if (flag!=null&&!flag) {return null;}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}String[] paramNames = AopUtil.getMethodParamNames(method);String pk = AopUtil.parseKeyByParam(t.title(),paramNames, jp.getArgs());//主键,机构pk,创建人,创建时间,操作类型,模块,处理方法,主键,操作内容//自增长主键LogOperater logOperater = new LogOperater();logOperater.setCreateTime(new Date());logOperater.setType(t.type().getValue());logOperater.setModel(t.model().getValue());logOperater.setFunc(t.model().getDiscription());logOperater.setMethod(method.getName());if (StringUtils.isNotBlank(pk)) {int index = pk.indexOf(",");if (index<0) {logOperater.setTitle(pk);}else{logOperater.setTitle(StringUtil.subString(pk, 0, index));int count = StringUtils.countMatches(pk, ",");String content = SystemConfig.LOG_CONTENT_LENGTH>pk.length()?pk:StringUtil.subString(pk, 0, SystemConfig.LOG_CONTENT_LENGTH)+"...";logOperater.setContent(count+"笔:"+content);}}return logOperater;}/** * 获取系统登录用户 * @return */private SysUser getLoginSysUser(){HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); return WebUtils.getLoginUser(request);}/** * 获取微信登录用户(未实现) * @return */private UsrUserSlave getLoginUsrUser(){return null;}}
再在spring的xml中配置扫描到LoggerAspect所在package目录,大功告成
使用起来也非常方便,在需要加操作日志的方法上加个注解就OK了,如此则记录为系统模块,执行查询,主键为参数pk(此处使用springSpel表达式解析)
@Logger(model=LogEnum.Sys,type=LogOperateEnum.Select,title="#pk")public Json get(String orgPk,String pk) {}
最后附上AOPUtil代码
package com.cykj.base.core.util;import java.lang.annotation.Annotation;import java.lang.reflect.Method;import org.apache.commons.lang3.StringUtils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.core.LocalVariableTableParameterNameDiscoverer;import org.springframework.expression.ExpressionParser;import org.springframework.expression.spel.standard.SpelExpressionParser;import org.springframework.expression.spel.support.StandardEvaluationContext;public class AopUtil {private static ExpressionParser parser;/** * 获取被拦截方法对象 * * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象 而缓存的注解在实现类的方法上 * 所以应该使用反射获取当前对象的方法对象 */public static Method getMethod(ProceedingJoinPoint pjp) {// 获取参数的类型Class<?>[] parameterTypes = ((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes();//此方法当参数传递null时会报错,故而改为采用上述方式// Object[] args = pjp.getArgs();// Class[] argTypes = new Class[pjp.getArgs().length];// for (int i = 0; i < args.length; i++) {// if (args[i]!=null) {// argTypes[i] = args[i].getClass();// }// }Method method = null;try {method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), parameterTypes);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();}return method;}/** * 通过spring spel解析参数获取redis缓存key * * @param keys * 缓存keys * @param paramNames * 参数名 * @param args * 参数列表 * @return */public static String parseKeyByParam(String keys, String[] paramNames, Object[] args) {if (StringUtils.isBlank(keys)) {return "";}ExpressionParser parser = getParser();StandardEvaluationContext context = new StandardEvaluationContext();// 把方法参数放入SPEL上下文中for (int i = 0; i < paramNames.length; i++) {context.setVariable(paramNames[i], args[i]);}// 获取参数keyStringBuffer sb = new StringBuffer();// for (int i = 0; i < keys.length; i++) {sb.append(parser.parseExpression(keys).getValue(context, String.class));// }return sb.toString();}/** * 获取参数名列表 * * @param method * @return */public static String[] getMethodParamNames(Method method) {// 获取被拦截方法参数名列表(使用Spring支持类库)LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();String[] paraNameArr = u.getParameterNames(method);return paraNameArr;}/** * 获取缓存主键 * @param jp * @param clazz * @return * @throws SecurityException * @throws NoSuchFieldException *///public static <T extends Annotation> String[] getCachedKey(T t,String[] paramNames, Class<T> clazz) throws NoSuchFieldException, SecurityException {////Method method = getMethod(jp);////T t = getMethodAnnotation(method, clazz);//// String fieldKey = null;//// if (cache.isParam()) {//String fieldKey = parseKeyByParam(ReflectionUtil.getField(t.getClass().getField("cachekey"), t).toString(), paramNames, jp.getArgs());//// }//return fieldKey.split(",");//}/** * 获取方法注解 * @param method * @param clazz 注解类型 * @return */public static <T extends Annotation> T getMethodAnnotation(Method method,Class<T> clazz){T t = method.getAnnotation(clazz);return t;}/** * 获取redis缓存key通过集合参数 * * @param keys * 缓存keys * @param params * 参数名 * @param returnObj * 参数列表 * @return */private String parseKeyByReturn(String keys, Object returnObj) {ExpressionParser parser = getParser();StandardEvaluationContext context = new StandardEvaluationContext();// 把方法参数放入SPEL上下文中context.setVariable("obj", returnObj);// 获取参数keyStringBuffer sb = new StringBuffer();sb.append(parser.parseExpression(keys).getValue(context, String.class));return sb.toString();}/** * 使用SPEL进行key的解析 * * @return */public synchronized static ExpressionParser getParser() {if (parser == null) {parser = new SpelExpressionParser();}return parser;}/** * 执行切入点方法 * @param jp * @param params 方法参数 * @return */public static Object executeJoinPointMethod(ProceedingJoinPoint jp,Object[] params){Object obj = null;try {if (params==null||params.length==0) {obj = jp.proceed();}else{obj = jp.proceed(params);}} catch (Throwable e) {e.printStackTrace();}return obj;}}
0 0
- SpringAOP注解方式记录操作日志(操作模块,操作功能,调用方法,主键信息等)支持多笔操作时记录
- 基于SSM利用SpringAOP切面及自定义注解 记录每次操作记录(操作日志 同理)
- 基于SpringAOP的操作日志记录实现
- mysql记录操作日志功能
- Spring 自定义注解实现操作日志记录功能
- aop+自定义注解实现操作日志记录
- aop+自定义注解实现操作日志记录
- Spring AOP 自定义注解记录操作日志
- 一种用户操作日志信息的记录及读取方法
- mvc 用户操作记录注解方式
- sa--记录操作日志
- 记录mysql操作日志
- spring AOP的 操作日志记录功能
- linux操作命令日志 记录的方法
- 基于SpringAop操作日志实现
- JAVA记录用户操作日志
- MySQL设置记录操作日志
- Delphi操作Excel日志记录
- 自定义控件三部曲之绘图篇(十八)——BitmapShader与望远镜效果
- NGINX负载均衡
- 解决 object references an unsaved transient instance - save the transient instance before flushing
- UVA - 10050 Hartals
- STM32自学笔记——外部中断
- SpringAOP注解方式记录操作日志(操作模块,操作功能,调用方法,主键信息等)支持多笔操作时记录
- I/O流之File类常用方法(2)
- Object 详解
- java工具类开发之json工具类JsonUtils和写数据回页面工具类ResponseUtils
- UVA - 540 Team Queue
- .net 执行多条cmd 命令方法与大批量CSV文件合并成一个文件方法
- 使用Gallery控件实现个人相簿功能
- 1130数据结构上机测试1:顺序表的应用
- 终于决定对自己学的东西做一个总结