概括

面向切面编程全称Aspect Oriented Programming,其可以通过切面插入一段代码,在切面执行之前或之后对其进行数据处理。AOP可以实现业务逻辑和关注点代码分离,比如每一次的业务处理之前都要进行日志的打印,要是在每个业务逻辑中都添加打印日志的内容,那代码就重复冗余且耦合度高,我们进行业务的编写最好就是明确各自的职责,各项业务分离开,实现解耦。通常aop适用于日志记录、权限控制、事务处理等方面。

AOP的五种增强方式

  1. before advice。这种增强方式是在join point前被执行。
  2. after return advice。这种情况是join point正常执行成功后再执行。
  3. after throwing advice。这种情况是joint point执行过程中抛出异常的执行结果。
  4. after advice。这种情况无论join point执行过程正不正常都会执行。
  5. around advice。这种情况在join point前和join point后都会执行一遍。

AOP在spring项目中的实现

  1. 首先来看看没有使用aop时候进行日志打印的方式:

    private final Logger logger= LoggerFactory.getLogger(BookServiceImpl.class);
    @Autowired
    private BookMapper mapper;
    @Override
    public List<Book> queryAll() {
    logger.info("====当前日期"+new Date(System.currentTimeMillis())+"====");
    return mapper.queryAll();
    }

    可以看出,日志打印的代码跟业务代码耦合在一起,因此要使用aop来将打印日志的事务插入到该切面中实现解耦。

  2. aop的实现。建一个类,使用@Aspect注解将其指定为切面类,在其方法中使用@Pointcut设置切入点,然后再根据切入点使用@Before@AfterReturning@AfterThrowing@Around等进行代码的插入。

    //将其声明为切面类
    @Aspect
    @Component
    public class LogAspect {
    private final Logger logger= LoggerFactory.getLogger(LogAspect.class);
    //设置切面
    @Pointcut(value = "execution(public * service.impl.BookServiceImpl.queryAll(..))")
    public void setPointCut(){
    }

    //切面事务执行前
    @Before("setPointCut()")
    public void beforeTransaction(JoinPoint joinPoint){
    logger.info("========="+new Date(System.currentTimeMillis()) +"=========");
    logger.info("@Before事务执行前:方法路径"+joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName());
    logger.info("============================");
    }

    //切面事务执行后返回值
    @AfterReturning(pointcut = "setPointCut()",returning = "response")
    public void afterReturning(Object response){
    logger.info("========="+new Date(System.currentTimeMillis()) +"=========");
    logger.info("@AfterReturning事务执行后返回正常:返回数据:"+response);
    logger.info("============================");
    }

    //切面事务抛出异常
    @AfterThrowing("setPointCut()")
    public void afterThrowing(){
    logger.info("========="+new Date(System.currentTimeMillis()) +"=========");
    logger.info("@AfterThrowing事务执行后抛出异常!");
    logger.info("============================");
    }

    //环绕切点
    @Around(value = "setPointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
    logger.info("事务环绕!");
    logger.info("@Around前置通知!");
    Object result = null;
    try {
    result = joinPoint.proceed();
    } catch (Throwable throwable) {
    throwable.printStackTrace();
    }
    logger.info("@Around后置通知!");
    return result;
    }

    }
  3. 配置文件的配置。因为Spring中AOP是使用了动态代理的方式实现,因此除了使用@Aspect注解声明了切面类外,还要在配置文件中进行切面的代理设置。

applicationContext.xml中的代理声明方式:

<!--切面代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

带有@Configuration的配置类:

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class WebConfig {
}

aop打印日志结果