起因:
因为项目想要监听用户对系统的使用情况,因此想要在用户进出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); } } }
记录一下,方便下次参考。