该功能是参照https://www.cnblogs.com/jianjianyang/p/4910851.html一文实现的。
以UAP项目为例:Spring版本3.1,JDK版本1.8,TOMCAT版本8.5,数据库技术为myBatis
1、在spring-service.xml中添加以下代码,启动对@AspectJ注解的支持:
<aop:aspectj-autoproxy/>
在其他相关博客中看到使用该标签时将其属性proxy-target-class设为了true,代表强制使用cglib代理(该属性默认是false,如果你的类实现了接口就走JDK代理,如果没有,走cglib代理),但在我的项目里如果设为了true,会在项目启动时报错,具体报错原因尚未深究,留待后面处理。
(注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib ----> 具体说明可看:http://blog.csdn.net/kingson_wu/article/details/50864637)
2、创建日志实体对象:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.bw.scpw.model; 2 3 import java.util.Date; 4 5 import com.bw.core.model.BaseModel; 6 import com.bw.core.model.IBaseModel; 7 8 public class SystemLog extends BaseModel implements IBaseModel 9 { 10 /** id */ 11 private String id; 12 /** 模块名 */ 13 private String model; 14 /** 方法名 */ 15 private String method; 16 /** 请求ip地址 */ 17 private String requestIp; 18 /** 方法参数 */ 19 private String params; 20 /** 操作人 */ 21 private String creator; 22 /** 操作人id */ 23 private String creatorId; 24 /** 操作时间 */ 25 private Date createDate; 26 /** 操作时间 开始时间 */ 27 private String createDateBegin; 28 /** 操作时间 结束时间 */ 29 private String createDateEnd; 30 31 /** 32 * id 33 * @return 34 */ 35 public String getId() 36 { 37 return id; 38 } 39 40 /** 41 * id 42 * @param id 43 */ 44 public void setId(String id) 45 { 46 this.id = id; 47 } 48 49 /** 50 * 模块名 51 * @return 52 */ 53 public String getModel() 54 { 55 return model; 56 } 57 58 /** 59 * 模块名 60 * @param model 61 */ 62 public void setModel(String model) 63 { 64 this.model = model; 65 } 66 67 /** 68 * 方法名 69 * @return 70 */ 71 public String getMethod() 72 { 73 return method; 74 } 75 76 /** 77 * 方法名 78 * @param method 79 */ 80 public void setMethod(String method) 81 { 82 this.method = method; 83 } 84 85 /** 86 * 请求ip 87 * @return 88 */ 89 public String getRequestIp() 90 { 91 return requestIp; 92 } 93 94 /** 95 * 请求ip 96 * @param requestIp 97 */ 98 public void setRequestIp(String requestIp) 99 { 100 this.requestIp = requestIp; 101 } 102 103 /** 104 * 请求参数 105 * @return 106 */ 107 public String getParams() 108 { 109 return params; 110 } 111 112 /** 113 * 请求参数 114 * @param params 115 */ 116 public void setParams(String params) 117 { 118 this.params = params; 119 } 120 121 /** 122 * 操作人 123 * @return 124 */ 125 public String getCreator() 126 { 127 return creator; 128 } 129 130 /** 131 * 操作人 132 * @param creator 133 */ 134 public void setCreator(String creator) 135 { 136 this.creator = creator; 137 } 138 139 /** 140 * 操作人id 141 * @return 142 */ 143 public String getCreatorId() 144 { 145 return creatorId; 146 } 147 148 /** 149 * 操作人id 150 * @param creatorId 151 */ 152 public void setCreatorId(String creatorId) 153 { 154 this.creatorId = creatorId; 155 } 156 157 /** 158 * 操作时间 159 * @return 160 */ 161 public Date getCreateDate() 162 { 163 return createDate; 164 } 165 166 /** 167 * 操作时间 168 * @param createDate 169 */ 170 public void setCreateDate(Date createDate) 171 { 172 this.createDate = createDate; 173 } 174 175 /** 176 * 操作时间 开始时间 177 * @param createDateBegin 178 */ 179 public void setCreateDateBegin(String createDateBegin) 180 { 181 this.createDateBegin = createDateBegin; 182 } 183 184 /** 185 * 操作时间 开始时间 186 * @return 187 */ 188 public String getCreateDateBegin() 189 { 190 return createDateBegin; 191 } 192 193 /** 194 * 操作时间 结束时间 195 * @param CreateDateEnd 196 */ 197 public void setCreateDateEnd(String createDateEnd) 198 { 199 this.createDateEnd = createDateEnd; 200 } 201 202 /** 203 * 操作时间 结束时间 204 * @return 205 */ 206 public String getCreateDateEnd() 207 { 208 return createDateEnd; 209 } 210 211 }
3、编写dao层、service层的接口和实现类(因为该项目已有dao层、service层的底层实现,直接继承父类即可,所以此处无具体代码):
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.bw.scpw.dao.impl; 2 3 import com.bw.core.dao.impl.IBaseDaoImpl; 4 import com.bw.scpw.model.SystemLog; 5 6 /** 7 * 系统操作日志DAO层 8 * @author liying 2019年7月2日 9 */ 10 public class SystemLogDao extends IBaseDaoImpl<SystemLog> 11 { 12 13 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.bw.scpw.service; 2 3 import com.bw.core.service.IBaseService; 4 import com.bw.scpw.model.SystemLog; 5 6 /** 7 * 系统操作日志service层 8 * @author liying 2019年7月2日 9 */ 10 public interface SystemLogService extends IBaseService<SystemLog> 11 { 12 13 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.bw.scpw.service.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Service; 5 6 import com.bw.core.dao.IBaseDao; 7 import com.bw.core.service.impl.BaseServiceImpl; 8 import com.bw.scpw.dao.impl.SystemLogDao; 9 import com.bw.scpw.model.SystemLog; 10 import com.bw.scpw.service.SystemLogService; 11 12 /** 13 * 系统操作日志service层实现类 14 * @author liying 2019年7月2日 15 */ 16 @Service("systemLogService") 17 public class SystemLogServiceImpl extends BaseServiceImpl<SystemLog> implements SystemLogService 18 { 19 20 @Autowired 21 private SystemLogDao systemLogDao; 22 23 @Override 24 protected IBaseDao<SystemLog> getBaseDao() 25 { 26 return systemLogDao; 27 } 28 29 }
注:service层的实现类中对dao层实现类的引用是通过@Autowired注解的,因此dao层类需在项目启动时就注册好,否则会报因找不到对象而自动注入失败的错。注册方式(选其一即可):
(1)在spring-dao.xml中添加以下代码:<bean id="systemLogDao" class="com.bw.scpw.dao.impl.SystemLogDao" parent="bizBaseDAO" />;
(2)在dao层实现类名上添加@Service注解:@Service("systemLogDao")。
4、自定义注解:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.bw.scpw.annotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 /** 10 * 保存method操作日志 11 * @author liying 2019年7月3日 12 */ 13 @Target(ElementType.METHOD) 14 @Retention(RetentionPolicy.RUNTIME) 15 @Documented 16 public @interface Log 17 { 18 19 }
注:后续添加注解说明。
5、编写AOP切面代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.bw.scpw.annotation; 2 3 import java.util.Date; 4 5 import javax.annotation.Resource; 6 import javax.servlet.ServletRequest; 7 import javax.servlet.ServletResponse; 8 import javax.servlet.http.HttpServletRequest; 9 10 import org.apache.commons.lang.StringUtils; 11 import org.aspectj.lang.JoinPoint; 12 import org.aspectj.lang.annotation.After; 13 import org.aspectj.lang.annotation.Aspect; 14 import org.aspectj.lang.annotation.Pointcut; 15 import org.slf4j.Logger; 16 import org.slf4j.LoggerFactory; 17 import org.springframework.stereotype.Component; 18 import org.springframework.web.context.request.RequestContextHolder; 19 import org.springframework.web.context.request.ServletRequestAttributes; 20 21 import com.bw.scpw.isc.help.SysUser; 22 import com.bw.scpw.isc.help.SysUserUtil; 23 import com.bw.scpw.model.SystemLog; 24 import com.bw.scpw.service.SystemLogService; 25 26 import net.sf.json.JSONArray; 27 28 /** 29 * 操作日志保存 30 * @author liying 2019年7月3日 31 */ 32 @Aspect 33 @Component 34 public class SystemLogAspect 35 { 36 private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class); 37 38 @Resource 39 private SystemLogService systemLogService; 40 41 // 切点 42 @Pointcut("@annotation(com.bw.scpw.annotation.Log)") 43 public void methodLogAspect() 44 { 45 } 46 47 /** 48 * 后置通知 用于拦截Controller层记录用户的操作 49 * @param joinPoint 切点 50 */ 51 @After("methodLogAspect()") 52 public void after(JoinPoint joinPoint) 53 { 54 try 55 { 56 SysUser user = SysUserUtil.getUser(); 57 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 58 // *========控制台输出=========*// 59 System.out.println("=====methodLog后置通知开始====="); 60 System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); 61 System.out.println("请求人:" + user.getUserName()); 62 System.out.println("请求IP:" + getCliectIp(request)); 63 // *========数据库日志=========*// 64 SystemLog log = new SystemLog(); 65 log.setModel(joinPoint.getTarget().getClass().getName()); 66 log.setMethod(joinPoint.getSignature().getName()); 67 log.setRequestIp(getCliectIp(request)); 68 log.setParams(getParams(joinPoint)); 69 log.setCreator(user.getUserName()); 70 log.setCreatorId(user.getUserId()); 71 log.setCreateDate(new Date()); 72 systemLogService.add(log); 73 74 System.out.println("=====methodLog后置通知结束====="); 75 } 76 catch (Exception e) 77 { 78 // 记录本地异常日志 79 logger.error("==后置通知异常=="); 80 logger.error("异常信息:{}", e.getMessage()); 81 e.printStackTrace(); 82 } 83 } 84 85 /** 86 * 获取方法参数 87 * @param joinPoint 88 * @return 89 */ 90 private String getParams(JoinPoint joinPoint) 91 { 92 Object[] arguments = joinPoint.getArgs(); 93 JSONArray jsonArr = new JSONArray(); 94 if (null != arguments && arguments.length > 0) 95 { 96 for (Object arg : arguments) 97 { 98 if (arg instanceof ServletRequest || arg instanceof ServletResponse) 99 { 100 continue; 101 } 102 jsonArr.add(arg); 103 } 104 } 105 return jsonArr.toString(); 106 } 107 108 /** 109 * 获取客户端ip地址 110 * @param request 111 * @return 112 */ 113 private String getCliectIp(HttpServletRequest request) 114 { 115 String ip = request.getHeader("x-forwarded-for"); 116 if (StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) 117 { 118 return ip.split(",")[0]; 119 } 120 ip = request.getHeader("X-Real-IP"); 121 if (StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) 122 { 123 return ip; 124 } 125 return request.getRemoteAddr(); 126 } 127 }
注:我的切面代码中只用到了后置通知,其实还有很多,留待以后研究。
6、在controller层的方法中添加注解:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/** * 加载枚举下拉项 * @return */ @RequestMapping("/wrapDictList") @Log public @ItemResponseBody List<DicItems> wrapDictList() { return scpwythJhxmglPcglAppService.wrapDictList(); }
以上就是操作日志管理功能实现的全部步骤。下面是后台运行数据和入库数据: