要使用AOP必须知道AOP的原理,这篇文章不做详述, 如需了解, 请直接坐电梯看我的另一片博文, 电梯在此!!!
一般@Aspect 注解的切面, 通常可以用切面表达式, 和注解切面来完成我们的切面编程.
首先必须引入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
---------------------------------------------------- 直接上代码 -----------------------------------------------------------------------------------------
首先基于注解形式的切面,下面代码,是本人写的RedisLock,分布式锁的
@Aspect
@Component
public class RedisLockAspect {
private static final Integer MAX_RETRY_COUNT = 3;
private static final String LOCK_PRE_FIX = "lockPreFix";
private static final String LOCK_KEY = "lockKey";
private static final String TIME_OUT = "timeOut"; // second
private static final int PROTECT_TIME = 2 << 11;//4096
private static final Logger log = LoggerFactory.getLogger(RedisLockAspect.class);
@Autowired
private CommonRedisHelper commonRedisHelper;
@Pointcut("@annotation(com.shuige.components.cache.annotation.RedisLock)")
public void redisLockAspect() {
}
@Around("redisLockAspect()")
public void lockAroundAction(ProceedingJoinPoint proceeding) throws Exception {
//获取redis锁
Boolean flag = this.getLock(proceeding, 0, System.currentTimeMillis());
if (flag) {
try {
proceeding.proceed();
Thread.sleep(PROTECT_TIME);
} catch (Throwable throwable) {
throw new RuntimeException("分布式锁执行发生异常" + throwable.getMessage(), throwable);
} finally {
// 删除锁
}
} else {
log.info("其他系统正在执行此项任务");
}
}
/**
* 获取锁
*
* @param proceeding
* @return
*/
private boolean getLock(ProceedingJoinPoint proceeding, int count, long currentTime) {
//获取注解中的参数
Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
String lockPrefix = (String) annotationArgs.get(LOCK_PRE_FIX);
String key = (String) annotationArgs.get(LOCK_KEY);
long expire = (long) annotationArgs.get(TIME_OUT);
//String key = this.getFirstArg(proceeding);
if (StringUtils.isEmpty(lockPrefix) || StringUtils.isEmpty(key)) {
// 此条执行不到
throw new RuntimeException("RedisLock,锁前缀,锁名未设置");
}
if (commonRedisHelper.setNx(lockPrefix, key, expire)) {
return true;
} else {
// 如果当前时间与锁的时间差, 大于保护时间,则强制删除锁(防止锁死)
long createTime = commonRedisHelper.getLockValue(lockPrefix, key);
if ((currentTime - createTime) > (expire * 1000 + PROTECT_TIME)) {
count ++;
if (count > MAX_RETRY_COUNT){
return false;
}
commonRedisHelper.delete(lockPrefix, key);
getLock(proceeding,count,currentTime);
}
return false;
}
}
/**
* 删除锁
*
* @param proceeding
*/
private void delLock(ProceedingJoinPoint proceeding) {
Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
String lockPrefix = (String) annotationArgs.get(LOCK_PRE_FIX);
String key = (String) annotationArgs.get(LOCK_KEY);
commonRedisHelper.delete(lockPrefix, key);
}
/**
* 获取锁参数
*
* @param proceeding
* @return
*/
private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
Class target = proceeding.getTarget().getClass();
Method[] methods = target.getMethods();
String methodName = proceeding.getSignature().getName();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Map<String, Object> result = new HashMap<String, Object>();
RedisLock redisLock = method.getAnnotation(RedisLock.class);
result.put(LOCK_PRE_FIX, redisLock.lockPrefix());
result.put(LOCK_KEY, redisLock.lockKey());
result.put(TIME_OUT, redisLock.timeUnit().toSeconds(redisLock.timeOut()));
return result;
}
}
return null;
}
/**
* 获取第一个String类型的参数为锁的业务参数
*
* @param proceeding
* @return
*/
@Deprecated
public String getFirstArg(ProceedingJoinPoint proceeding) {
Object[] args = proceeding.getArgs();
if (args != null && args.length > 0) {
for (Object object : args) {
String type = object.getClass().getName();
if ("java.lang.String".equals(type)) {
return (String) object;
}
}
}
return null;
}
}
使用的时候, 则需要在方法上面加上该注解即可;
----------------------------------------------------------------------------------------------------
下面是基于切面表达式的注解, 我这边用日志打印来作为例子
@Aspect
@Component
public class RequestLogAspect {
private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);
private ThreadLocal<OperatorLog> threadLocal = new ThreadLocal<OperatorLog>();
private static final int maxLength = 5000;
// @Autowired
// private OptLogService optLogService;
@Pointcut("execution(public * com.shuige..*.controller..*.*(..))")
public void webRequestLog() {
}
// @Order(5)
@Before("webRequestLog()")
public void doBefore(JoinPoint joinPoint) {
try {
long beginTime = System.currentTimeMillis();
//获取操作系统等信息
StringBuffer sb = new StringBuffer();
sb.append("操作系统名称:" + System.getProperty("os.name"));//操作系统名称
sb.append("操作系统构架" + System.getProperty("os.arch"));//操作系统构架
sb.append("操作系统版本" + System.getProperty("os.version"));//操作系统版本
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String beanName = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
String uri = request.getRequestURI();
String remoteAddr = getIpAddr(request);
String sessionId = request.getSession().getId();
String user = (String) request.getSession().getAttribute("user");
String httpMethod = request.getMethod();
String params = "";
String paramsAll = "";
// if ("POST".equals(httpMethod)) {
try {
Object[] paramsArray = joinPoint.getArgs();
ObjectMapper mapper = new ObjectMapper();
params = mapper.writeValueAsString(paramsArray);
paramsAll = params;
if (!StringUtils.isEmpty(params) && params.length() > maxLength) {
params = params.substring(0, maxLength);
params = "[参数太长截取 " + maxLength + " 显示]" + params;
}
} catch (Exception e) {
logger.warn("params转换失败,程序不需要处理: {}", joinPoint.getArgs());
}
// } else {
// Map<?, ?> paramsMap = (Map<?, ?>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
// params = paramsMap.toString();
// }
logger.info("uri=" + uri + "; method=" + httpMethod + "; beanName=" + beanName + "; remoteAddr=" + remoteAddr + "; user=" + user
+ "; methodName=" + methodName + "; params=" + params);
OperatorLog optLog = new OperatorLog();
optLog.setBeanName(beanName);
optLog.setCurUser(user);
optLog.setHttpMethod(httpMethod);
optLog.setMethod(methodName);
optLog.setParams("" + paramsAll);
optLog.setRemoteAddr(remoteAddr);
optLog.setSessionId(sessionId);
optLog.setRequestUrl(uri);
optLog.setRequestStartTime(beginTime);
optLog.setCreateDate(new Date());
optLog.setUserAgent(sb.toString());
// 目标方法不为空
if (!StringUtils.isEmpty(methodName)) {
// set与get方法除外
if (!(methodName.startsWith("set") || methodName.startsWith("get"))) {
Class targetClass = joinPoint.getTarget().getClass();
// Method method = targetClass.getMethod(methodName);
Method method = null;
Method[] methods = targetClass.getMethods();
if (methods != null) {
for (Method ele : methods) {
if (methodName.equals(ele.getName())) {
method = ele;
break;
}
}
}
if (method != null) {
boolean hasAnnotation = method.isAnnotationPresent(APILog.class);
if (hasAnnotation) {
optLog.setHasAnnotation(true);
APILog annotation = method.getAnnotation(APILog.class);
String methodDescp = annotation.comment();
}
}
}
}
threadLocal.set(optLog);
} catch (Exception e) {
logger.error("***操作请求日志记录失败doBefore()***", e);
}
}
// @Order(5)
@AfterReturning(returning = "result", pointcut = "webRequestLog()")
public void doAfterReturning(Object result) {
try {
// 处理完请求,返回内容
OperatorLog optLog = threadLocal.get();
optLog.setResult(result == null ? null : result.toString());
long beginTime = optLog.getRequestStartTime();
long endTime = System.currentTimeMillis();
long requestTime = (endTime - beginTime);
optLog.setRequestEntTime(endTime);
optLog.setRequestTime(requestTime);
String strResult = optLog.getResult();
if (!StringUtils.isEmpty(strResult) && strResult.length() > maxLength) {
strResult = strResult.substring(0, maxLength);
strResult = "[参数太长截取 " + maxLength + " 显示]" + strResult;
}
String params = optLog.getParams();
if (!StringUtils.isEmpty(params) && params.length() > maxLength) {
params = params.substring(0, maxLength);
params = "[参数太长截取 " + maxLength + " 显示]" + params;
}
String log = "\n请求耗时:" + optLog.getRequestTime() + "ms " + optLog.getRequestUrl() + " " + optLog.getHttpMethod() + " ** " + optLog.getMethod() + " ** " + params
+ "\nRESPONSE : " + strResult;
logger.info(log);
// TODO 保存日志
logger.warn("TODO 还未保存API日志: {}", "★★★★★★★★★★★★★★★★★★★★★★★★");
if (optLog.isHasAnnotation()) {
// optLogService.saveLog(optLog);
try {
String lineSeparator = System.lineSeparator();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
String filePath = "apilogs/" + simpleDateFormat.format(new Date()) + ".log";
createDir(filePath);
FileOutputStream fos = new FileOutputStream(filePath, true);
fos.write((optLog.getRequestUrl() + " " + optLog.getRemoteAddr() + " " + optLog.getBeanName() + "." + optLog.getMethod() + lineSeparator + " " + optLog.getParams() + lineSeparator).getBytes("UTF-8"));
fos.flush();
fos.close();
} catch (Exception e) {
System.out.println(e.getCause());
}
}
} catch (Exception e) {
logger.error("***操作请求日志记录失败doAfterReturning()***", e);
}
}
/**
* 获取登录用户远程主机ip地址
*
* @param request
* @return
*/
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
// 创建目录
private static boolean createDir(String filePath) {
File file = new File(filePath);
if (file.exists()) {// 判断文件是否存在
System.out.println("目标文件已存在" + filePath);
return false;
}
if (filePath.endsWith(File.separator)) {// 判断文件是否为目录
System.out.println("目标文件不能为目录!");
return false;
}
if (!file.getParentFile().exists()) {// 判断目标文件所在的目录是否存在
// 如果目标文件所在的文件夹不存在,则创建父文件夹
System.out.println("目标文件所在目录不存在,准备创建它!");
if (!file.getParentFile().mkdirs()) {// 判断创建目录是否成功
System.out.println("创建目标文件所在的目录失败!");
return false;
}
}
try {
if (file.createNewFile()) {// 创建目标文件
System.out.println("创建文件成功:" + filePath);
return true;
} else {
System.out.println("创建文件失败!");
return false;
}
} catch (IOException e) {// 捕获异常
e.printStackTrace();
return false;
}
}
}