1、
<aspectj.version>1.7.4.RELEASE</aspectj.version>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
注意:如果JDK1.7的 必须这里也是1.7+
2、
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
注意:必须在spring-mvc.xml里面,且有两个aop配置,下面那个是必须的,上面那个可能不是必须的(上面那个应该是spring aop的,如果有aspectJ了,可以不需要)
3、AOP类
package com.kingen.aop;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.kingen.bean.Log;
import com.kingen.bean.User;
import com.kingen.service.log.LogService;
import com.kingen.util.Constants;
import com.kingen.util.DateUtils;
import com.kingen.util.SpringContextHolder;
import com.kingen.util.StringUtils;
import com.kingen.util.UserUtils;
/**
* 日志AOP
*
* @author wj
* @date 2016-11-16
*
*/
@Aspect
@Component
public class LogAOP {
private static LogService logService = SpringContextHolder.getBean(LogService.class);
// 本地异常日志记录对象
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 在所有标注@LogAnnotation的地方切入
*
* @param joinPoint
*/
@Pointcut("@annotation(com.kingen.aop.LogAnnotation)")
public void logAspect() {
}
// @Around(value = "aApplogic() && @annotation(annotation) &&args(object,..)
// ", argNames = "annotation,object")
// public Object around(ProceedingJoinPoint pj,
// LogAnnotation annotation, Object object) throws Throwable {
// System.out.println("moduleName:"+annotation.moduleName());
// System.out.println("option:"+annotation.option());
// pj.proceed();
// return object;
// }
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint
* 切点
* @throws Exception
*/
// @Around(value = "logAspect() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object")
// public void doAround(ProceedingJoinPoint joinPoint, LogAnnotation annotation, Object object) {
//用@Around 会导致controller不执行,不返回页面
// @After(value = "logAspect() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object")
// public void doAfter(JoinPoint joinPoint, LogAnnotation annotation, Object object) {
@AfterReturning(value = "logAspect() && @annotation(annotation) &&args(object,..) ", argNames = "", returning = "retVal")
public void doAfterReturning(JoinPoint joinPoint, LogAnnotation annotation, Object object, String retVal) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
// String title = getAnnotationValue(joinPoint);
String title = getAnnotationValue(annotation);
saveLog(request, title);
} catch (Exception e) {
e.printStackTrace();
// 记录本地异常日志
logger.error("==异常通知异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
/**
* 异常通知 用于拦截service层记录异常日志
*
* @param joinPoint
* @param e
*/
// 方法 catch住异常的话,这里执行不到
// @AfterThrowing(pointcut = "logAspect()", throwing = "e")
@AfterThrowing(value = "logAspect() && @annotation(annotation) &&args(..) " , throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, LogAnnotation annotation, Exception e) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
// String title = getAnnotationValue(joinPoint);
String title = getAnnotationValue(annotation);
saveLog(request, title, e);
} catch (Exception ex) {
// 记录本地异常日志
logger.error("==异常通知异常==");
logger.error("异常信息:{}", ex.getMessage());
}
}
public static void saveLog(HttpServletRequest request, String title) {
saveLog(request, title, null);
}
/**
* 保存日志
*/
@Transactional
public static void saveLog(HttpServletRequest request, String title, Exception ex) {
User user = UserUtils.getCurrentUser();
if (user != null && user.getUserId() != null) {
Log log = new Log();
log.setCreateDate(DateUtils.getDateTime());
log.setTitle(title);
log.setType(ex == null ? Log.TYPE_ACCESS : Log.TYPE_EXCEPTION);
log.setRemoteAddr(StringUtils.getRemoteAddr(request));
log.setUserAgent(user.getUsername());
// log.setUserAgent(request.getHeader("user-agent"));
log.setRequestUri(request.getRequestURI());
log.setParams(request.getParameterMap());
// 如果有异常,设置异常信息
log.setException(ex == null ? null : ex.getMessage());
// log.setException(ex == null ? null : Exceptions.getStackTraceAsString(ex));
log.setStatus(ex == null ? Constants.StatusEnum.Success.getIndex() : Constants.StatusEnum.Fail.getIndex());
// log.setMethod(request.getMethod());
// 异步保存日志
// new SaveLogThread(log, handler, ex).start();
logService.saveLog(log);
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint
* 切点
* @return 方法描述
* @throws Exception
*/
@Deprecated
public static String getAnnotationValue(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
String moduleName = method.getAnnotation(LogAnnotation.class).moduleName();
String option = method.getAnnotation(LogAnnotation.class).option();
Assert.hasText(moduleName, "模块名字不应为空");
Assert.hasText(option, "操作名字不应为空");
description = moduleName + "-" + option;
break;
}
}
}
return description;
}
public static String getAnnotationValue(LogAnnotation anno) throws Exception {
String moduleName = anno.moduleName();
String option = anno.option();
Assert.hasText(moduleName, "模块名字不应为空");
Assert.hasText(option, "操作名字不应为空");
String description = moduleName + "-" + option;
return description;
}
}
注意这里 @After和 @AfterReturning的区别,总的来说,就是 @After 是无论如何都会执行的,不管有没有异常抛出(这样会导致,在有异常的时候,记录两次日志,after一次、throwing一次);@AfterReturning 在有异常的情况下,不会执行到,因为没有renturn,在retrun之前就throw了。
http://stackoverflow.com/questions/4304676/why-is-afterreturning-only-executed-after-afterthrowing-on-exception 写道
If you want to execute a code after method regardless if there was an exception thrown or not, you need to use @After instead.
http://stackoverflow.com/questions/11859568/how-to-use-one-of-after-and-afterthrowing-in-aspectj/11859621#11859621 写道
Change the @After to an @AfterReturning
http://stackoverflow.com/questions/16522635/spring-and-aop-after-works-but-not-afterreturning 写道
if there is a exception thrown within pointcut methodAnnotatedWithMyService() then @AfterReturning will not be called.. but a @After will be called..
from http://static.springsource.org/spring/docs/2.0.x/reference/aop.html
@AfterReturning advice runs when a matched method execution returns normally
from http://static.springsource.org/spring/docs/2.0.x/reference/aop.html
@AfterReturning advice runs when a matched method execution returns normally
http://stackoverflow.com/questions/38202051/spring-aop-after-or-afterreturning-which-advice-will-invoked-first 写道
Invocation of the advices of different type applied on the same jointpoint(core business related modules) as follows :
1.Around
2.Before and/or After
3.AfterReturning or AfterThrowing
Suppose we applied all five types of advices on the same jointpoint then the flow will be like :
Around type advice will be invoked and code before pjp.proceed() of Around type advice will be executed where pjp is the reference variable of ProceedingJoinPoint interface.
Before type advice will be invoked and executed fully.
code inside jointpoint will be executed fully.
Code after pjp.proceed() of Around type advice will be executed if jointpoint executes successfully otherwise skip this step and go to step 5. If it's modified return value then this new return value will be effected to the followings advice or method invocation.
After type advice will be invoked and executed fully.
AfterReturning type advice will be invoked and executed fully if jointpoint executes successfully else if jointpoint throws any error then AfterThrowing type advice will be invoked and executed fully.
1.Around
2.Before and/or After
3.AfterReturning or AfterThrowing
Suppose we applied all five types of advices on the same jointpoint then the flow will be like :
Around type advice will be invoked and code before pjp.proceed() of Around type advice will be executed where pjp is the reference variable of ProceedingJoinPoint interface.
Before type advice will be invoked and executed fully.
code inside jointpoint will be executed fully.
Code after pjp.proceed() of Around type advice will be executed if jointpoint executes successfully otherwise skip this step and go to step 5. If it's modified return value then this new return value will be effected to the followings advice or method invocation.
After type advice will be invoked and executed fully.
AfterReturning type advice will be invoked and executed fully if jointpoint executes successfully else if jointpoint throws any error then AfterThrowing type advice will be invoked and executed fully.
4、poitcut用注解
package com.kingen.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 日志注解
* @author wj
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
//模块名
String moduleName();
//操作内容
String option();
}
5、使用
@RequestMapping(value="/")
@LogAnnotation(moduleName="用户管理",option="查看")
@RequiresPermissions("user:view")
public String execute(HttpServletResponse response) throws Exception{
try{
service.testException();
}catch(Exception e){
e.printStackTrace();
logger.error(e.getMessage());
throw new Exception("test");
}
return "account/tmanageruser";
}
注意:我的需求是,当正常执行就插入成功日志,如果有异常就插入失败日志,这里controller层的,在catch住后又throw了,可以满足。当然不能用@After了,只能用@AfterReturning 和 @AfterThrowing
-----------
advisor 是管理point cut 和 advice(@before、@AfterReturing…)的容器
@After 不管怎样都会执行,方法肯定会结束啊
@Afterreturning 返回值时执行、有异常当然没有返回值、当然不执行!
@Afterthrowing 异常抛出!时执行,别catch住了 (可以再抛)
@After 不管怎样都会执行,方法肯定会结束啊
@Afterreturning 返回值时执行、有异常当然没有返回值、当然不执行!
@Afterthrowing 异常抛出!时执行,别catch住了 (可以再抛)