AOP简单说明:
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。在日常开发当中经常用来记录日志,方法跟踪、事务,权限等
切面方法说明:
@Aspect -- 作用是把当前类标识为一个切面供容器读取
@Pointcut -- (切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
@Before -- 标识一个前置增强方法,相当于BeforeAdvice的功能
@AfterReturning -- 后置增强,相当于AfterReturningAdvice,方法退出时执行
@AfterThrowing -- 异常抛出增强,相当于ThrowsAdvice
@After -- final增强,不管是抛出异常或者正常退出都会执行
@Around -- 环绕增强,相当于MethodInterceptor
一、springboot开启aop
(1)pom依赖:
<!-- aop依赖 校验注解jar包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 注解jar包 -->
二、SpringMvc开启aop
(1)pom依赖:
<!-- 校验注解jar包 -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- 注解jar包 -->
(2)配置文件:
<!-- 开启asepctj注解扫描 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- AOP的XML的aspectj配置 -->
<bean id="requestParamValidAspect" class="com.grainRain.common.utils.RequestParamValidAspect"/>
三、AOP工具类
package com.macro.mall.portal.util;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSON;
import com.macro.mall.portal.domain.CommonResult;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author YJX
* 参数校验切面和日志记录
*/
@Aspect //@Aspect注解会使Spring IoC容器认为这是一个切面
@Component //让切面成为Spring管理的bean
public class RequestParamValidAspect {
private static final Logger LOG = LoggerFactory.getLogger(RequestParamValidAspect.class);
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private final ExecutableValidator validator = factory.getValidator().forExecutables();
//定义切点
@Pointcut("execution(public * com.macro.mall.*.controller.*.*(..))")
public void valid() {
}
//环绕通知,环绕增强,相当于MethodInterceptor
@Around("valid()")
public Object arround(ProceedingJoinPoint pjp) {
System.out.println("方法环绕start.....");
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
//获取方法名称
String methodname = pjp.getSignature().getName();
//获取类名
String className = pjp.getTarget().getClass().getName();
//方法开始调用时间
long begin = System.currentTimeMillis();
try {
//获取请求参数,如果没参数,那肯定不校验了
Object[] requestParams = pjp.getArgs();
if (requestParams.length == 0) {
Object result = pjp.proceed();
LOG.info("func<doAround> method<" + className + "." + methodname + "> cost time <"
+ (System.currentTimeMillis() - begin) + ">ms");
return result;
}
Enumeration en = request.getParameterNames();
StringBuffer param = new StringBuffer();
Map map = new HashMap<>();
while(en.hasMoreElements()){
String parameterName = (String) en.nextElement();
String valueArray[] = request.getParameterValues(parameterName);
if (valueArray.length == 1) {
String paramValue = valueArray[0];
if (paramValue.length() != 0) {
map.put(parameterName, paramValue);
}
}
}
System.out.println();
LOG.info("接口调用记录 --- ip :" + request.getRemoteHost()+ ";接口名 :" + className + "." + methodname +";接口入参 :" + JSON.toJSONString(map));
/**************************校验封装好的javabean**********************/
//寻找带BindingResult参数的方法,然后判断是否有error,如果有则是校验不通过
for (Object object : requestParams) {
if (object instanceof BeanPropertyBindingResult) {
//有校验
BeanPropertyBindingResult result = (BeanPropertyBindingResult) object;
if (result.hasErrors()) {
List<ObjectError> list = result.getAllErrors();
for (ObjectError error : list) {
LOG.error("参数校验错误:"+(error.getCode() + "---" + error.getArguments() + "--" + error.getDefaultMessage()));
//返回第一条校验失败信息。也可以拼接起来返回所有的
return new CommonResult().failed(error.getDefaultMessage());
}
}
}
}
/**************************校验普通参数*************************/
// 获得切入目标对象
Object target = pjp.getThis();
// 获得切入的方法
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 执行校验,获得校验结果
Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, requestParams);
//如果有校验不通过的
if (!validResult.isEmpty()) {
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method); // 获得方法的参数名称
for(ConstraintViolation<Object> constraintViolation : validResult) {
PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath(); // 获得校验的参数路径信息
int paramIndex = pathImpl.getLeafNode().getParameterIndex(); // 获得校验的参数位置
String paramName = parameterNames[paramIndex]; // 获得校验的参数名称
//校验信息
LOG.error("参数"+paramName+":"+constraintViolation.getMessage());
// System.out.println(paramName);
// System.out.println(constraintViolation.getMessage());
}
//返回第一条
return new CommonResult().failed("校验失败:"+validResult.iterator().next().getMessage());
}
Object result = pjp.proceed();
LOG.info("接口返回结果 统计method<" + className + "." + methodname +">:"+ JSON.toJSONString(result));
LOG.info("接口耗时 统计method<" + className + "." + methodname + "> cost time <"
+ (System.currentTimeMillis() - begin) + ">ms");
System.out.println("方法环绕end.....");
return result;
} catch (Throwable e) {
e.printStackTrace();
return e;
}
}
private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object[] params) {
return validator.validateParameters(obj, method, params);
}
}