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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,941评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,397评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,345评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,851评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,868评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,688评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,414评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,319评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,775评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,945评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,096评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,789评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,437评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,993评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,107评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,308评论 3 372
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,037评论 2 355