我的aop记录日志,可以记录:【 操作类型、操作描述、参数、登录项目的用户ip】 当然记录什么靠你自己决定。
一.自定义一个注解
@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopLog {
//操作描述
String description() default "";
//操作类型
String operateType() default "";
}
二.定义aop记录日志工具类
- @Before: 前置通知, 在方法执行之前执行。
- @After: 后置通知, 在方法执行之后执行 。
- @AfterRunning: 返回通知, 在方法返回结果之后执行。
- @AfterThrowing: 异常通知, 在方法抛出异常之后。
- @Around: 环绕通知, 围绕着方法执行、
@Aspect
@Component
public class LogAspectUtil {
//这是我自己的接口实现类,就是存入我的数据库需要的接口
@Autowired
private PsLogServiceImpl psLogService;
/**
* service层切点
*/
@Pointcut("@annotation(com.navi.vpx.common.util.AopLog)") //这里是你自定义注解的位置
public void serviceAspect() {
}
/**
* 记录日志,写入数据库
* @param joinPoint 切入点参数
*/
@After("serviceAspect()")//这里设置的是当你的方法执行完之后,才会执行该方法,也可以使用上面不同的注解在不同时间进行记录。
public void doServiceLog(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//这里是获取登录你项目用户的ip地址
try {
String unknown = "unknown";
// String ip0 = request.getHeader("x-forwarded-for");
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// if (LOCAL_IP.equals(ip)) {
// ip = "local";
// }
//将记录写入数据库
PsLog psLog = new PsLog();
psLog.setCreateTime(new Date());//设置当前时间
psLog.setDes( "操作描述:" + getDes(joinPoint) + " 操作类型:" + getOperateType(joinPoint) );//操作描述和操作类型
psLog.setArgs(getArgs(joinPoint));//设置参数
psLog.setIp(ip);//设置当前登录用户ip
psLogService.insertLog(psLog);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取方法的描述信息
* @param joinPoint
* @return
*/
private String getDes(JoinPoint joinPoint) throws ClassNotFoundException {
//获取被代理对象名称
String targetName = joinPoint.getTarget().getClass().getName();
//获取目标方法名称
String methodName = joinPoint.getSignature().getName();
//获取传入目标方法的参数对象
Object[] arguments = joinPoint.getArgs();
//获取class对象
Class targetClass = Class.forName(targetName);
//获取对象中的所有方法
Method[] methods = targetClass.getMethods();
String des = "";
//下面是进行匹配,方法名和目标方法名相同,且参数相同,则获取注解内的相应参数,用来进行日志记录
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
des = method.getAnnotation(ConfigAopLogService.class).description();
break;
}
}
}
return des;
}
/**
* 获取操作表的操作类型
* @param joinPoint
* @return
*/
private String getOperateType(JoinPoint joinPoint) throws ClassNotFoundException {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String operateType = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
if (method.getParameterTypes().length == args.length) {
operateType = method.getAnnotation(ConfigAopLogService.class).operateType();
break;
}
}
}
return operateType;
}
/**
* 获取操作表的参数(你的参数是对象,会查出属性名及其值)
* @param joinPoint
* @return
*/
private String getArgs(JoinPoint joinPoint) throws ClassNotFoundException {
int index = -1; //记录下标
String arg = "";//拼接参数
Object[] args = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();//此处joinPoint的实现类是
MethodSignature methodSignature = (MethodSignature) signature;//获取参数名
for(int i=0;i<methodSignature.getParameterNames().length;i++){
if (methodSignature.getParameterNames()[i].equals("request")){//获取参数名字为request的参数
index = i;
}
}
for (int j = 0; j<args.length; j++) {//循环参数对象
if (index != -1 && index == j){ //不拼接request参数
break;
}
Class<?> aClass = args[j].getClass();
Field[] fields = aClass.getDeclaredFields();// 根据Class对象获得属性 私有的也可以获得
try {
for (Field f : fields) {
f.setAccessible(true); // 设置些属性是可以访问的
Object val = f.get(args[j]); // 得到此属性的值
String name = f.getName(); // 得到此属性的名称
arg += name + ":" + val + ", ";
}
} catch (IllegalAccessException e) {
System.out.println("报错");
}
}
System.out.println(arg);
return arg;
}
}
三.实际操作和数据库的值
1.把注解放在某个方法上
2.然后请求该方法,你的数据库就会存入对应的日志
操作描述和操作类型 没办法自动获取所以我们手打就可以,而参数和当前用户ip可以自动获取到,所以我们之间存入我们的实体类里面,然后直接存入数据库。