了解相关的AOP的知识的,可以参考该视频地址–【Spring AOP 详解及示例】 https://www.bilibili.com/video/BV1yK411M7hb/?share_source=copy_web&vd_source=8498d841f5087520b7df387d23104481
–建议开1.5倍速观看。 之后你会对基本的信息进行理解。
AOP的简单在于只需要在需记录操作的接口方法上,添加上自己的相关注解就好了,可以设计到controller与service层,实现的内容自己定义即可。
实现:
第一步:建立依赖包
pom.xml
tomcat编译器相关的jar包,不导入的话可能会导致 报错,tomcat编译不了jsp页面
AspectJ 的一个 weaver(编织器)依赖项。AspectJ 是一个面向切面编程的框架,可以将横切关注点(cross-cutting concerns)与主要逻辑进行解耦。
org.apache.tomcat.embedtomcat-embed-jasper9.0.80org.aspectjaspectjweaver1.8.7org.jsonjson20180813
系统的日志相关的jar
org.apache.logging.log4j:log4j-api:2.11.0
:这是 Apache Log4j 的 API 模块的依赖项。Log4j 是一个流行的日志框架,用于在 Java 应用程序中记录日志。org.apache.logging.log4j:log4j-core:2.11.0
:这是 Apache Log4j 的核心模块的依赖项。它包含了 Log4j 的核心功能和实现。org.slf4j:slf4j-log4j12:1.7.25
:这是 SLF4J 的 log4j12 桥接器的依赖项。SLF4J 是一个日志抽象层,它提供了统一的日志接口,可以和不同的日志实现框架进行集成。log4j12 桥接器将 SLF4J 的日志接口转发给 log4j 进行实际的日志记录。
org.apache.logging.log4jlog4j-api2.11.0org.apache.logging.log4jlog4j-core2.11.0org.slf4jslf4j-log4j121.7.25
这个库提供了在 Java 中进行 JSON 数据处理的功能,包括解析、生成和操作 JSON 格式的数据
org.jsonjson20180813
commons-logging:commons-logging:1.2
:这是 Apache Commons Logging 库的依赖项。Commons Logging 提供了一种日志抽象层,允许使用不同的日志实现(例如 Log4J、java.util.logging 等)而不需要修改代码。javax.servlet:javax.servlet-api:3.1.0
:这是 Java Servlet API 的依赖项。Servlet API 提供了编写基于 Java 的 Web 应用程序的基本功能,包括 HTTP 请求处理、会话管理等。org.projectlombok:lombok:1.18.24
:这是 Lombok 库的依赖项。Lombok 是一个 Java 库,通过注解的方式来简化 Java 代码的编写,包括自动生成 getter/setter、构造函数、equals/hashCode 方法等。provided
表示该库在编译时有效,但在运行时由容器或其他地方提供,比如在 Java EE 应用中,Servlet 容器会提供 Servlet API。
commons-loggingcommons-logging1.2javax.servletjavax.servlet-api3.1.0org.projectlomboklombok1.18.24provided
AOP依赖
org.apache.tomcat.embedtomcat-embed-core9.0.80org.springframeworkspring-beans5.1.6.RELEASEorg.springframeworkspring-aop5.1.6.RELEASEorg.springframeworkspring-tx5.1.6.RELEASE
第二步:那就是在XML中开启对AOP的支持
第三步:实现AOP
系统实现AOP后的架构如下
AOP注解的自己定义与实现:
package com.vilicode.Logger;import java.lang.annotation.*;@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface SystemControllerLog {/** * 该注解作用于方法上时需要备注信息 */String remarks() default "";}
package com.vilicode.Logger;import java.lang.annotation.*;@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface SystemServiceLog {/** * 该注解作用于方法上时需要备注信息 */String remarks() default "";}
AOP切面类的自己定义与实现:
package com.vilicode.Logger;import java.io.PrintWriter;import java.lang.reflect.Method;import javax.annotation.Resource;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import com.vilicode.bean.SystemLog;import com.vilicode.bean.User;import com.vilicode.service.SystemLogService;import lombok.Data;import org.apache.log4j.Logger;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.annotation.Aspect;import org.json.JSONArray;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.vilicode.Utils.IpUtil;/** * 日志管理切点类 * @author ct * */@Aspect@Componentpublic class SystemLogAspect {//注入Service用于把日志保存数据库@Resourceprivate SystemLogService systemLogService;//本地异常日志记录对象privatestaticfinal Logger logger = Logger.getLogger(SystemLogAspect.class);//controller后置层切入点@Pointcut("@annotation(com.vilicode.Logger.SystemControllerLog)")public void controllerAspect() {System.out.println("========controllerAspect===========");}//service层切入点@Pointcut("@annotation(com.vilicode.Logger.SystemServiceLog)")public void serviceAspect() {System.out.println("========serviceAspect===========");}//controller层前置切入点@Pointcut("@annotation(com.vilicode.Logger.SystemControllerqianzhiLog)")public void controllerqianzhiAspect() {System.out.println("========controller前置Aspect===========");}/** * 后置通知 用于拦截Controller层操作 * @param joinPoint 切点 */@After("controllerAspect()")public void doAfter(JoinPoint joinPoint) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //user==null不进行记录 只有user != null的时候才进行记录的// 获取登陆用户信息User session_user= (User) request.getSession().getAttribute("user");if(session_user!=null) { // 获取请求ip String ip = IpUtil.getClientIp(request);try {String params = "";Object[] args = joinPoint.getArgs();if (args != null) {JSONArray jsonArray = new JSONArray();jsonArray.put(args);params = jsonArray.toString();}// *========控制台输出=========*//System.out.println("=====后置通知开始=====");System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "."+ joinPoint.getSignature().getName() + "()"));System.out.println("方法描述:" + getControllerMethodDescription(joinPoint));System.out.println("请求人:" +session_user.getUname());System.out.println("请求IP:" + ip);System.out.println("请求参数:" + params);// *========数据库日志=========*//SystemLog log = new SystemLog();//方法描述log.setDescription(getControllerMethodDescription(joinPoint));//request 的请求记录log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));log.setType(0);// user requestlog type =0log.setIp(ip);log.setExceptionCode(null);log.setExceptionDetail(null);log.setParams(params.substring(0, Math.min(params.length(), 200)));log.setCreateUser(session_user);log.setUsersessionname(session_user.getUname());// 保存数据库systemLogService.save(log);System.out.println("=====后置通知结束=====");} catch (Exception e) {// 记录本地异常日志logger.error("==后置通知异常==");logger.error("异常信息:{}", e);}}else{System.out.println("user is null please login in ,then it will be logged");}}/** * 前置通知 用于拦截Controller层操作 * @param joinPoint 切点 */@Before("controllerqianzhiAspect()")public void doBefore(JoinPoint joinPoint) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//user==null不进行记录 只有user != null的时候才进行记录的// 获取登陆用户信息User session_user= (User) request.getSession().getAttribute("user");if(session_user!=null) {// 获取请求ipString ip = IpUtil.getClientIp(request);try {String params = "";Object[] args = joinPoint.getArgs();if (args != null) {JSONArray jsonArray = new JSONArray();jsonArray.put(args);params = jsonArray.toString();}// *========控制台输出=========*//System.out.println("=====前置通知开始=====");System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "."+ joinPoint.getSignature().getName() + "()"));System.out.println("方法描述:" + getControllerqianzhiMethodDescription(joinPoint));System.out.println("请求人:" +session_user.getUname());System.out.println("请求IP:" + ip);System.out.println("请求参数:" + params);// *========数据库日志=========*//SystemLog log = new SystemLog();//方法描述log.setDescription(getControllerqianzhiMethodDescription(joinPoint));//request 的请求记录log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));log.setType(0);// user requestlog type =0log.setIp(ip);log.setExceptionCode(null);log.setExceptionDetail(null);log.setParams(params.substring(0, Math.min(params.length(), 200)));log.setCreateUser(session_user);log.setUsersessionname(session_user.getUname());// 保存数据库systemLogService.save(log);System.out.println("=====前置通知结束=====");} catch (Exception e) {// 记录本地异常日志logger.error("==前置通知异常==");logger.error("异常信息:{}", e);}}else{System.out.println("user is null please login in ,then it will be logged");}}/** * 异常通知 用于拦截service层记录异常日志 * @param joinPoint * @param e */@AfterThrowing(pointcut="serviceAspect()", throwing="e")public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 获取登陆用户信息// 获取登陆用户信息User session_user= (User) request.getSession().getAttribute("user");if (session_user!=null) {// 获取请求ipString ip = IpUtil.getClientIp(request);// 获取用户请求方法的参数并序列化为JSON格式字符串String params = "";Object[] args = joinPoint.getArgs();if (args != null) {JSONArray jsonArray = new JSONArray();jsonArray.put(args);params = jsonArray.toString();}try {/* ========控制台输出========= */System.out.println("=====异常通知开始=====");System.out.println("异常代码:" + e.getClass().getName());System.out.println("异常信息:" + e.getMessage());System.out.println("异常方法:"+ (joinPoint.getTarget().getClass().getName() + "."+ joinPoint.getSignature().getName() + "()"));System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));System.out.println("请求人:" + session_user.getUname());System.out.println("请求IP:" + ip);System.out.println("请求参数:" + params);/* ==========数据库日志========= */SystemLog log = new SystemLog();log.setDescription(getServiceMthodDescription(joinPoint));log.setMethod((joinPoint.getTarget().getClass().getName() + "."+ joinPoint.getSignature().getName() + "()"));log.setType(1);//error log type=1log.setIp(ip);log.setExceptionCode(e.getClass().getName());log.setExceptionDetail(e.getMessage());log.setParams(params);log.setCreateUser(session_user);log.setUsersessionname(session_user.getUname());// 保存数据库systemLogService.save(log);System.out.println("=====异常通知结束=====");} catch (Exception ex) {// 记录本地异常日志logger.error("==异常通知异常==");logger.error("异常信息:{}", ex);}}else {System.out.println("session_user is null please loginin andit will be log in error");}}/** * 获取注解中对方法的描述信息 用于Controller后置层注解 * @param joinPoint 切点 * @return 方法描述 * @throws Exception */public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception{//获取目标类名String targetName = joinPoint.getTarget().getClass().getName();//获取方法名String methodName = joinPoint.getSignature().getName();//获取相关参数Object[] arguments = joinPoint.getArgs();//生成类对象Class targetClass = Class.forName(targetName);//获取该类中的方法Method[] methods = targetClass.getMethods();String description = "";for(Method method : methods) {if(!method.getName().equals(methodName)) {continue;}Class[] clazzs = method.getParameterTypes();if(clazzs.length != arguments.length) {continue;}description = method.getAnnotation(SystemControllerLog.class).remarks();}return description;}/** * 获取注解中对方法的描述信息 用于Controller前置---------层注解 * @param joinPoint 切点 * @return 方法描述 * @throws Exception */public static String getControllerqianzhiMethodDescription(JoinPoint joinPoint) throws Exception{//获取目标类名String targetName = joinPoint.getTarget().getClass().getName();//获取方法名String methodName = joinPoint.getSignature().getName();//获取相关参数Object[] arguments = joinPoint.getArgs();//生成类对象Class targetClass = Class.forName(targetName);//获取该类中的方法Method[] methods = targetClass.getMethods();String description = "";for(Method method : methods) {if(!method.getName().equals(methodName)) {continue;}Class[] clazzs = method.getParameterTypes();if(clazzs.length != arguments.length) {continue;}description = method.getAnnotation(SystemControllerqianzhiLog.class).remarks();}return description;}/** * 获取注解中对方法的描述信息 用于service层注解 * @param joinPoint 切点 * @return 方法描述 * @throws Exception */public static String getServiceMthodDescription(JoinPoint joinPoint) throws Exception{//获取目标类名String targetName = joinPoint.getTarget().getClass().getName();//获取方法名String methodName = joinPoint.getSignature().getName();//获取相关参数Object[] arguments = joinPoint.getArgs();//生成类对象Class targetClass = Class.forName(targetName);//获取该类中的方法Method[] methods = targetClass.getMethods();String description = "";for(Method method : methods) {if(!method.getName().equals(methodName)) {continue;}Class[] clazzs = method.getParameterTypes();if(clazzs.length != arguments.length) {continue;}description = method.getAnnotation(SystemServiceLog.class).remarks();}return description;}}
实现相关存储的日志的实体 SystemLogBean 与Mapper层, Controller层, Service层。
SystemLogBean
package com.vilicode.bean;import lombok.Data;import java.sql.Timestamp;@Datapublic class SystemLog {//log idprivateint logid;//日志记录描述private String description;//method request urlprivateString method;//日志类型 user request / errorprivate int type;//IPprivate String ip;//异常代码private String exceptionCode;//ExceptionDetailprivateString exceptionDetail;//Params request参数privateString params;//用户private User createUser;//usernameprivateString usersessionname;//日志时间private String createDate;public int getLogid() {return logid;}public void setLogid(int logid) {this.logid = logid;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}public int getType() {return type;}public void setType(int type) {this.type = type;}public String getIp() {return ip;}public void setIp(String ip) {this.ip = ip;}public String getExceptionCode() {return exceptionCode;}public void setExceptionCode(String exceptionCode) {this.exceptionCode = exceptionCode;}public String getExceptionDetail() {return exceptionDetail;}public void setExceptionDetail(String exceptionDetail) {this.exceptionDetail = exceptionDetail;}public String getParams() {return params;}public void setParams(String params) {this.params = params;}public User getCreateUser() {return createUser;}public void setCreateUser(User createUser) {this.createUser = createUser;}public String getUsersessionname() {return usersessionname;}public void setUsersessionname(String usersessionname) {this.usersessionname = usersessionname;}public String getCreateDate() {return createDate;}public void setCreateDate(String createDate) {this.createDate = createDate;}@Overridepublic String toString() {return "SystemLog{" +"logid=" + logid +", description='" + description + '\'' +", method='" + method + '\'' +", type=" + type +", ip='" + ip + '\'' +", exceptionCode='" + exceptionCode + '\'' +", exceptionDetail='" + exceptionDetail + '\'' +", params='" + params + '\'' +", createUser=" + createUser +", usersessionname='" + usersessionname + '\'' +", createDate='" + createDate + '\'' +'}';}}
controller层
package com.vilicode.controller;import com.vilicode.bean.Page;import com.vilicode.bean.SystemLog;import com.vilicode.service.SystemLogService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;@Controllerpublic class SystemLogController {@Autowiredprivate SystemLogService systemLogService;@RequestMapping("/admin/log_list")public String ShowLogList(int pageNumber, HttpServletRequest request){if(pageNumber=p.getTotalPage()+1){p = systemLogService.queryLogAll(p.getTotalPage());}}request.setAttribute("p", p);return "admin/log_list";}}
Service层
package com.vilicode.service;import com.vilicode.bean.Order;import com.vilicode.bean.Page;import com.vilicode.bean.SystemLog;public interface SystemLogService {public booleansave(SystemLog log);//add SystemLog save itpublic Page queryLogAll(int pageNumber);//page search}
package com.vilicode.service.impl;import com.vilicode.bean.Order;import com.vilicode.bean.Page;import com.vilicode.bean.SystemLog;import com.vilicode.mapper.SystemLogMapper;import com.vilicode.service.SystemLogService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class SystemLogServiceImpl implements SystemLogService {@Autowiredprivate SystemLogMapper systemLogMapper;@Overridepublic boolean save(SystemLog log) {try {systemLogMapper.addlog(log);return true;//添加成功}catch (Exception e){systemLogMapper.deletelog(log.getLogid());System.out.println(e.getMessage());//有错误出现删除改行即可return false;}}@Overridepublic Page queryLogAll(int pageNumber) {//Page 的初始化Page p = new Page();p.setPageNumber(pageNumber);int pageSize = 4;int totalCount = 0;//求得log的综述try {totalCount=systemLogMapper.queryCountOfSystemLog();} catch (Exception e) {System.out.println(e.getMessage());}//设置dao层分页查询的参数size 与totalp.SetPageSizeAndTotalCount(pageSize, totalCount);List list=null;try {//开始查询list=systemLogMapper.querySystemLogAll((pageNumber-1)*pageSize,pageSize);} catch (Exception e) {System.out.println(e.getMessage());}p.setList((List)list);return p;}}
Mapper层
package com.vilicode.mapper;import com.vilicode.bean.Order;import com.vilicode.bean.SystemLog;import org.apache.ibatis.annotations.Param;import java.util.List;public interface SystemLogMapper {public void addlog(SystemLog systemLog);public void deletelog(int logid);public int queryCountOfSystemLog();//count totalpublic List querySystemLogAll(@Param("pageIndex") int pageIndex, @Param("pageSize") int pageSize);public List querySystemLogByUsername( String username);}
select count(*) from systemlog ;select * from systemlog limit #{pageIndex},#{pageSize}insert into `systemlog`(description,method,type,ip,exceptionCode,exceptionDetail,params,usersessionname)values(#{description},#{method},#{type},#{ip},#{exceptionCode},#{exceptionDetail},#{params},#{usersessionname})delete from systemlog where log_id=#{logid}
不要忘记修改相应的xml
第四步:这时候就可以实现效果展示了
创建了新的log_list.jsp,使用户管理员查询展示数据库里面存储的记录日志
审计日志表
Log_Id username createDate Description Method 日志Type(user/error) Ip ExceptionCode ExceptionDetail Params ${systemLog.logid }
${systemLog.usersessionname }
${systemLog.createDate }
${systemLog.description }
${systemLog.method }
${systemLog.type }
${systemLog.ip }
${systemLog.exceptionCode }
${systemLog.exceptionDetail }
${systemLog.params }
展示页面
数据库的记录日志表:
新添加了一条记录
控制台打印的日志记录的操作
页面展示