获取以下内容的日志信息
访问时间
操作者用户名
访问ip
访问资源url
执行时长
访问方法
package ssm.controller;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import ssm.domain.SysLog;
import ssm.service.SysLogService;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;
//@Component
//@Aspect//切面类
public class LogAop {
@Autowired
private HttpServletRequest request;
@Autowired
private SysLogService sysLogService;
private Date visitTime;//开始访问的时间
private Class clazz;//要访问的类
private Method method;//要访问的方法
//前置通知
//@Before("execution(* ssm.controller.*.*(..))")
public void doBefore(JoinPoint jp) throws NoSuchMethodException {
visitTime = new Date();//当前时间就是开始访问的时间
clazz = jp.getTarget().getClass();//获取要访问的类
String methodName = jp.getSignature().getName();//获取要访问的方法的名称
Object[] args = jp.getArgs();//获取传入目标方法的参数对象
//获取要访问的方法
if (args==null||args.length==0) {
method = clazz.getMethod(methodName);//无参方法
}else{
Class[] classArge = new Class[args.length];
for (int i = 0; i < args.length ; i++) {
classArge[i] = args[i].getClass();
}
method = clazz.getMethod(methodName,classArge);//有参方法
}
}
//后置通知
//@After("execution(* ssm.controller.*.*(..))")
public void doAfter() {
long time = new Date().getTime() - visitTime.getTime();//获取访问时长
//获取url(即Controller类上的访问路径和方法上的访问路径)
String url = "";
//判断获取到的类不为空且方法不为空且不是当前日志类
if (clazz!=null&&method!=null&&clazz!=LogAop.class){
//获取类上RequestMapping注解的参数
RequestMapping clazzAnnotation = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
if (clazzAnnotation!=null) {
String[] value1 = clazzAnnotation.value();
String s1 = value1[0];
//获取方法上RequestMapping注解的参数
RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
if (methodAnnotation!=null) {
String[] value2 = methodAnnotation.value();
String s2 = value2[0];
url = s1+s2;
}
}
}
//获取ip地址
String ip = request.getRemoteAddr();
//获取操作者用户名
SecurityContext context = SecurityContextHolder.getContext();//从上下文获取当前登录的用户
User user = (User) context.getAuthentication().getPrincipal();
String username = user.getUsername();
//分装获取到的信息到SysLog对象
SysLog sysLog = new SysLog();
sysLog.setExecutionTime(time);
sysLog.setIp(ip);
sysLog.setMethod("[类名]:"+clazz.getName()+"[方法名]:"+method.getName());
sysLog.setUrl(url);
sysLog.setUsername(username);
sysLog.setVisitTime(visitTime);
//写入数据库
sysLogService.save(sysLog);
}
}
在 springmvc.xml 配置文件中配置AOP(不能配置在 applicationContext.xml 中)
<!-- 配置日志AOP -->
<bean id="logAop" class="ssm.controller.LogAop"></bean>
<aop:config>
<aop:pointcut id="logPt" expression="execution(* ssm.controller.*.*(..))"></aop:pointcut>
<aop:aspect id="logAdvice" ref="logAop">
<aop:before method="doBefore" pointcut-ref="logPt"></aop:before>
<aop:after-returning method="doAfter" pointcut-ref="logPt"></aop:after-returning>
</aop:aspect>
</aop:config>
1.获取访问时间
就是当前方法执行的时间
在前置通知中new一个时间就可以
Date visitTime = new Date();
2.访问方法
1.获取要访问的类
2.获取要访问的方法的名称
3.获取传入目标方法的参数对象
3.1通过目标方法的参数对象获取参数的类
4.通过以上获取的三个参数获取方法
ps:最后封装到 SysLog 对象中的是方法名,所以获取方法这一步没必要,
[ String methodName = jp.getSignature().getName(); ]
这一步已近获取到方法名了
//前置通知
@Before("execution(* ssm.controller.*.*(..))")
public void doBefore(JoinPoint jp) throws NoSuchMethodException {
Class clazz = jp.getTarget().getClass();//获取要访问的类
String methodName = jp.getSignature().getName();//获取要访问的方法的名称
Object[] args = jp.getArgs();//获取传入目标方法的参数对象
//获取要访问的方法
if (args==null||args.length==0) {
method = clazz.getMethod(methodName);//无参方法
}else{
Class[] classArge = new Class[args.length];
for (int i = 0; i < args.length ; i++) {
classArge[i] = args[i].getClass();//获取参数的类
}
method = clazz.getMethod(methodName,classArge);//有参方法
}
}
3.执行时长
就是方法执行完所用的时长
在后置通知new一个时间减去前置通知的时间就是所用时长
long time = new Date().getTime() - visitTime.getTime();//获取访问时长
4.访问资源url
需要反射来完成操作
1.获取类上的注解
2.获取类上注解的参数(因为注解上就只有一个value参数,且value的参数只有一个,所以取注解参数数组的[0])
3.获取方法上的注解
4.获取方法上注解的参数(因为注解上就只有一个value参数,且value的参数只有一个,所以取注解参数数组的[0])
//获取url(即Controller类上的访问路径和方法上的访问路径)
String url = "";
//判断获取到的类不为空且方法不为空且不是当前日志类
if (clazz!=null&&method!=null&&clazz!=LogAop.class){
//获取类上RequestMapping注解的参数
RequestMapping clazzAnnotation = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
if (clazzAnnotation!=null) {
String[] value1 = clazzAnnotation.value();
String s1 = value1[0];
//获取方法上RequestMapping注解的参数
RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
if (methodAnnotation!=null) {
String[] value2 = methodAnnotation.value();
String s2 = value2[0];
url = s1+s2;
}
}
}
5.访问ip
通过request对象获取
在web.xml文件中配置一个Listener:RequestContextListener
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
给LogAop注入request对象
用request对象的getRemoteAddr();方法获取ip地址
//获取ip地址
String ip = request.getRemoteAddr();
6.操作者用户名
通过 SecurityContext 获取
//获取操作者用户名
SecurityContext context = SecurityContextHolder.getContext();//从上下文获取当前登录的用户
User user = (User) context.getAuthentication().getPrincipal();
String username = user.getUsername();
也可以从request.getSession中获取
request.getSession().getAttribute("SPRING_SECURITY_CONTEXT")
将获取的信息封装到 SysLog 对象,调用 Service、Dao 写入数据库
涉及的知识点
1.Spring JoinPoint
JoinPoint 对象
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
常用API
方法名 | 功能 |
---|---|
Signature getSignature(); | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object[] getArgs(); | 获取传入目标方法的参数对象 |
Object getTarget(); | 获取被代理的对象 |
Object getThis(); | 获取代理对象 |
2.反射
方法名 | 功能 |
---|---|
getClass(); | 获取调用对象的Class |