zoukankan      html  css  js  c++  java
  • SpringMVC 使用 AOP 拦截 controller、service

    起因:

      因为项目想要监听用户对系统的使用情况,因此想要在用户进出service的时候,打印出log。但是,因为service中方法比较多,结果就是即使只是进入service

    的时候打印log,一次事件结束之后,log也很大,根本就不实用。因此想要既能监听用户的使用情况,又不产生太多的log,就想到了监听controller。

    原本是拦截service:

      事实上,使用AOP拦截service 很简单,并没有什么特殊的,网上例子很多。

      下面先看一下代码中关于配置拦截service的配置:

      Spring的配置文件application.xml包含了 开启AOP自动代理,Service扫描配置(根据自己项目的实际情况找到对应的xml文件,命名可能不同)

    代码1:application.xml
    <!-- 这个是方法,代码在后面 -->

      <bean id="log" class="com.aop.log.AopLog"/>
      <aop:config>
        <aop:pointcut id="pc" expression="execution(* 你的包名.service.impl..*.*(..)) or execution(* 你的包名.api.service.impl..*.*(..))" />
        <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" order="2" />
        <aop:aspect ref="log">
          <aop:before method="before" pointcut-ref="pc"/>
        </aop:aspect>
      </aop:config>

    Spring MVC的配置文件spring-mvc.xml主要内容是Controller层的自动扫描配置.

    代码2:spring-mvc.xml
    <context:component-scan base-package="com.**.controller.**" />

       代码3:AopLog.java

    package com.aop.log;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    import com.AppContext;
    import com.vo.UserVO;
    
    @Component
    @Aspect
    public class AopLog {
        private static Logger logger = Logger.getLogger(AopLog.class);
        public void before(JoinPoint jp) {
            UserVO currentUser = AppContext.getContext().getUser();
            String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            String controllerName = jp.getTarget().getClass().getName();
            String methodName = jp.getSignature().getName();
            if (null != currentUser) {
                logger.info("Current user: " + currentUser.getUserName() + " IN controller: " + controllerName +
                        ", method: " + methodName + ", time: " + currentTime);
            }
        }
    }
    
    

    因为Spring的Bean扫描和Spring-MVC的Bean扫描是分开的, 两者的Bean位于两个不同的Application, 而且Spring-MVC的Bean扫描要早于Spring的Bean扫描, 所以当Controller Bean生成完成后, 再执行Spring的Bean扫描,Spring会发现要被AOP代理的Controller Bean已经在容器中存在, 配置AOP就无效了.

    同样这样的情况也存在于数据库事务中, 如果Service的Bean扫描配置在spring-mvc.xml中, 而数据库事务管理器配置在application.xml中, 会导致数据库事务失效, 原理一样.

    所以这里 ,我们需要把AOP放置在Controller扫描配置的文件中.

    下面是拦截controller

    代码2中spring-mvc.xml中增加如下配置:

        <!-- aop拦截controller -->
        <aop:aspectj-autoproxy/>
        <context:component-scan base-package="com.aop.log"/>
        <context:component-scan base-package="com.controller" /><!--这里改为自己的controller的包名-->
    然后代码1处就不要了,代码3的AopLog.java改为:
    package com.augmentum.performance.aop.log;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    import com.AppContext;
    import com.vo.UserVO;
    
    @Component
    @Aspect
    public class AopLog {
        private static Logger logger = Logger.getLogger(AopLog.class);
        @Pointcut("execution (* com.controller.*.*(..))")//controller的包
        public void pointcut(){
        }
    
        //方法执行前调用
        @Before("pointcut()")
        public void before(JoinPoint jp) {
            UserVO currentUser = AppContext.getContext().getUser();
            String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            String controllerName = jp.getTarget().getClass().getName();
            String methodName = jp.getSignature().getName();
            if (null != currentUser) {
                logger.info("Current user: " + currentUser.getUserName() + " IN controller: " + controllerName +
                        ", method: " + methodName + ", time: " + currentTime);
            }
        }
    }
    记录一下,方便下次参考。
  • 相关阅读:
    最大子序和
    Linux中判断hdfs文件是否存在
    Python判断字符串是否为字母或者数字
    win10+1060显卡安装anaconda+CUDA10.1+pytorch+cuDNN+tensorflow-gpu
    Anaconda3+python3环境下如何创建python2环境(win+Linux下适用,同一个anaconda下py2/3共存)
    教你上传本地代码到github
    vue源码学习-vnode的挂载和更新流程
    学习 vue 源码 -- 响应式原理
    instanceof 操作符实现原理解析
    webapp开发要点记录
  • 原文地址:https://www.cnblogs.com/yunyunde/p/7206014.html
Copyright © 2011-2022 走看看