AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在spring AOP中业务逻辑仅仅只关注业务本身,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
相关注解介绍:
@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行
Spring AOP 中@Pointcut的用法
execution表达式
1)execution(* *(..))
//表示匹配所有方法
2)execution(public * com. xl.service.UserService.*(..))
//表示匹配com.xl.server.UserService中所有的公有方法
3)execution(* com.xl.server..*.*(..))
//表示匹配com.xl.server包及其子包下的所有方法
4)@annotation(com.platform.annotation.SysLog)
//表示匹配注解SysLog
在Spring 2.0中,Pointcut的定义包括两个部分:Pointcut表示式(expression)和Pointcut签名(signature)
//Pointcut表示式
@Pointcut("execution(* com.xl.aop.MessageSender.*(..))")
//Point签名
private void log(){}
然后要使用所定义的Pointcut时,可以指定Pointcut签名
如下:
@Before("log()")
这种使用方式等同于以下方式,直接定义execution表达式使用
@Before("execution(* com.xl.aop.MessageSender.*(..))")
请求日志代码:
package cn.aid.cmsweb.assist.aspect;
import cn.aid.cmsweb.entity.UserDetails;
import cn.aid.cmsweb.handler.OperationLogHandler;
import cn.aid.common.utils.util.HttpUtil;
import cn.aid.data.api.resource.entity.OperationLog;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
@Aspect //使之成为切面类
@Component //把切面类加入到IOC容器中
@Order(1) //控制AOP的加载顺序
public class OperationLogAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
OperationLogHandler operationLogHandler;
private ThreadLocal<Long> startTime = new ThreadLocal<>();
//定义切入点
@Pointcut("execution(* cn.aid.cmsweb.controller..*.*Controller.*(..))")
public void webLog() {
}
//前置通知:目标方法执行之前执行以下方法体的内容
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
}
//返回通知:目标方法正常执行完毕时执行以下代码
@AfterReturning(value = "webLog()", returning = "ret")
public void doAfterReturning(Object ret) throws Throwable {
}
//环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
Object result = null;
try {
//【环绕通知中的--->前置通知】
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Map<String, String[]> parameterMap = request.getParameterMap();
//记录请求信息
OperationLog operationLog = new OperationLog();
result = joinPoint.proceed();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation log = method.getAnnotation(ApiOperation.class);
operationLog.setDescription(log.value());
}
long endTime = System.currentTimeMillis();
UserDetails userDetails = (UserDetails) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT_DETAILS");
if(null != userDetails ){
operationLog.setUsername(userDetails.getUsername());
}
operationLog.setBasePath(HttpUtil.getBasePath(request));
operationLog.setIp(HttpUtil.getClientIp(request));
operationLog.setMethod(request.getMethod());
operationLog.setParameter(getParameterMap(parameterMap));
//operationLog.setResult(result);
operationLog.setSpendTime((int) (endTime - startTime.get()));
operationLog.setOperationTime(startTime.get());
operationLog.setUri(request.getRequestURI());
operationLog.setUrl(request.getRequestURL().toString());
// operationLog.setDescription(getParameterMap(parameterMap));
Map<String,Object> logMap = new HashMap<>();
logMap.put("url",operationLog.getUrl());
logMap.put("method",operationLog.getMethod());
logMap.put("parameter",operationLog.getParameter());
logMap.put("spendTime",operationLog.getSpendTime());
logMap.put("description",operationLog.getDescription());
// LOGGER.info("{}", JsonUtil.objectToJson(webLog));
//logger.info(Markers.appendEntries(logMap),JsonUtil.objectToJson(operationLog));
operationLogHandler.saveLog(operationLog);
// 【环绕通知中的--->返回通知】
return result;
} catch (Exception e) {
//【环绕通知中的--->异常通知】
logger.error("日志记录出错!", e);
return result;
}
//【环绕通知中的--->HOUZ通知】
}
private String getParameterMap(Map<String,String[]> parameterMap) {
if(null == parameterMap){
return null;
}
String result = null;
try{
Map<String,Object> paramsMap = new HashMap<>();
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
entries.forEach( entry -> {
paramsMap.put(entry.getKey(),entry.getValue()[0]);
}
);
Object o = JSONObject.toJSON(paramsMap);
result = o.toString();
}catch (Exception e){
logger.error("日志记录出错! getParameterMap == {}", e.getMessage());
}
return result;
}
/**
* 根据方法和传入的参数获取请求参数
*/
private String getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestParam != null) {
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, args[i]);
argList.add(map);
}else{
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
map.put(key, args[i]);
argList.add(map);
}
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList.toString();
} else {
return argList.toString();
}
}
}