仓库源文站点原文


layout: post title: "Spring面向切面编程" categories: Spring tags: AOP

author: 张乘辉

Spring的面向切面编程可以完美解决。面向切面编程指的是在原来代码的基础上,加上增强的部分,生成一个代理的对象,也就是说,程序员可以在不修改源代码的情况下,实现对目标类的增强,其原理就是将一个被AOP动态增强的类,通过Java动态代理模式生成一个代理类。

AOP的一些相关概念

AspectJ语法

"execution(* cn.youyinian.controller.v1.*.*(..))"

AOP增强处理方法

Before

@Aspect
public class LogAspect {
  @Before("execution(* cn.youyinian.test.*.*(..))")
  publc void doBefore() {
    System.out.println("test before");
  }
}

用于目标方法被调用前做一些增强处理。

AfterReturning

@Aspect
public class LogAspect {
  @AfterReturning(returning="rvt", pointcut="execution(* cn.youyinian.test.*.*(..))")
  publc void doAfterReturning(Object rvt) {
    System.out.println("获取返回值:" + rvt);
  }
}

用于访问目标方法返回值,并作相关处理。

After

@Aspect
public class LogAspect {
  @After("execution(* cn.youyinian.test.*.*(..))")
  publc void doAfter() {
    System.out.println("test after");
  }
}

用于目标方法被调用后做一些增强处理。与AfterReturning有些相似,但是也有区别,AfterReturning只有在目标方法成功完成后才会被织入。

Around

@Aspect
public class LogAspect {
  @Around("execution(* cn.youyinian.test.*.*(..))")
  publc void doAround(ProceedingJoinPoint pjp) {
    System.out.println("around before");
    pjp.proceed();
    System.out.println("around after");
  }
}

ProceedingJoinPoint 参数是必须的,因为要使的目标方法要调用,那么必须调用其方法proceed()。

AfterThrowing

@Aspect
public class LogAspect {
  @AfterThrowing(throwing="ex", pointcut="execution(* cn.youyinian.test.*.*(..))")
  publc void doThrowing(Throwable ex) {
    System.out.println("目标方法抛出异常:" + ex);
  }
}

用于处理目标方法抛出的异常。

实战

定时任务中加入日志打印

定义一个切面:

@Aspect
public class LogInterceptor {

  // 定义一个切入点
  @Pointcut("execution(* cn.youyinian.handler.*(..))")
  private void log() {}

  // before增强
  @Before("log() && args(params)")
  private void before(String params) {
    XxlJobLogger.log("[定时任务] " + params + " >>>>>>>>>>>>>>>>>>> 开始");
  }

  // After增强
  @After("log() && args(params)")
  private void after(String params) {
    XxlJobLogger.log("[定时任务] " + params + " >>>>>>>>>>>>>>>>>>> 结束");
  }
}

目标方法:

package cn.youyinian.handler.topic;

import cn.youyinian.remote.TopicRemoteService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.log.XxlJobLogger;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * Created by zch on 2017/8/25.
 */

@JobHander(value="pushAuthHandler")
@Service
public class PushAuthHandler extends IJobHandler {

    @Resource
    private TopicRemoteService topicRemoteService;

    @Override
    public ReturnT<String> execute(String... params) throws Exception {
        topicRemoteService.pushAuthMessage();
        return ReturnT.SUCCESS;
    }
}

定时任务中加入方法锁

定义一个切面:

@Aspect
public class TaskLockAspect {

  // 此处省略部分代码

  @Pointcut("@annotation(cn.youyinian.annotation.LockMethod)")
  public void pointCut() {
  }

  @Around("pointCut()")
  public Object doAround(ProceedingJoinPoint pjp) {
    String targetName = pjp.getTarget().getClass().getName();
    String methodName = pjp.getSignature().getName();
    String lockKey = targetName + "#" + methodName;
    if(!publicLock.getLock(lockKey)) {
      Long time = Long.valueOf(valueRedisTemplate.get(lockKey));
      logger.info("任务已被锁定lockKey:{}, 锁定时间:{}", lockKey, DateUtil.format(new Date(time), "yyyy-MM-dd HH:mm:ss"));
      return null;
    }

    try {
      Object result = pjp.proceed();
      publicLock.releaseLock(lockKey);
      return result;
    } catch (Throwable throwable) {
      throwable.printStackTrace();
      publicLock.releaseLock(lockKey);
    }
    return null;
  }
}

目标方法:

package cn.youyinian.controller.v1.remote;
// 此处省略部分代码

@RestController
@RequestMapping("/remote/topic")
public class TopicRemoteController {

    private final Logger logger = LoggerFactory.getLogger(TopicRemoteController.class);

    @Resource
    private TopicAuthService topicAuthService;

    @LockMethod
    @RequestMapping(method = RequestMethod.GET, value = "task/pushAuthMessage")
    public void pushAuthMessage() {
        topicAuthService.pushAuthMessage();
    }
}