AOP日志

获取以下内容的日志信息

访问时间
操作者用户名
访问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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。