Spring中基于XML和注解方式的AOP实现

文章目录

  • 一、AOP介绍
  • 二、基于XML实现AOP
    • 配置步骤
    • 配置四种通知
    • 配置环绕通知
  • 三、基于注解配置

一、AOP介绍

  • 实现方式:动态代理
  • 连接点:一般指Service中的方法;
  • 切入点:需要拦截的连接点
  • 通知类型:前置、后置、异常、最终通知,含义下图。
    Spring中基于XML和注解方式的AOP实现_第1张图片
  • 切面:切入点和通知的结合
  • 环境准备
    • 抽取公共方法为通知:这里以记录日志为例
package com.li.utils;
/*
* 用于提供日志的工具类,抽取公共代码
* */
public class Logger {

   /**
    * @Description: 打印日志,在切入点方法执行之前进行
    * @Param:
    * @return:
    * @Date: 2020/5/14
    */
    public void printLog(){
        System.out.println("开始记录日志了");
    }
}

  • 准备Service层方法
package com.li.aop;

public class AccountServiceImpl implements AccountService {
    public void safeCount() {
        System.out.println("账户存储");
    }

    public void createCount(int i) {
        System.out.println("新建账户");
    }

    public int deleteCount() {
        System.out.println("删除账户");
        return 0;
    }
}

二、基于XML实现AOP

配置步骤

  • ioc配置,包括Service和通知Bean
  <bean id="accountService" class="com.li.aop.AccountServiceImpl">bean>
    
    <bean id="logger" class="com.li.utils.Logger">bean>
  • 使用aop:config标签表明开始AOP的配置
  • 在aop:config内部使用aop:aspect标签表明配置切面
    • id属性:是给切面提供一个唯一标识
    • ref属性:是指定通知类bean的Id。
  • 在aop:aspect标签的内部使用对应标签来配置通知的类型
    • aop:before:表示配置前置通知
    • method属性:用于指定Logger类中哪个方法是前置通知
    • pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
  • 切入点表达式写法见xml文件注释
  
 <bean id="logger" class="com.li.utils.Logger">bean>
    <aop:config>
        <aop:aspect id="logAdvice" ref="logger">
            <aop:before method="printLog" pointcut="execution(public void com.li.aop.AccountServiceImpl.safeCount())">aop:before>
        aop:aspect>
    aop:config>

配置四种通知

  • 丰富记录日志的方法
package com.li.utils;
/*
* 用于提供日志的工具类,抽取公共代码
* */
public class Logger {

   /**
    * @Description: 打印日志,在切入点方法执行之前进行
    * @Param:
    * @return:
    * @Author: Mr.Wang
    * @Date: 2020/5/14
    */
   //前置通知
    public void beforePrintLog(){
        System.out.println("前置通知开始记录日志了");
    }
    //后置通知
    public void afterReturningPrintLog(){
        System.out.println("后置通知记录日志了");
    }
    //异常通知
    public void afterThrowingPrintLog(){
        System.out.println("异常通知开始记录日志了");
    }
    //最后通知
    public void afterPrintLog(){
        System.out.println("最后通知开始记录日志了");
    }
}

  • xml配置:
<bean id="logger" class="com.li.utils.Logger">bean>
    <aop:config>
        <aop:aspect id="logAdvice" ref="logger">
            <aop:before method="beforePrintLog" pointcut-ref="service">aop:before>
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="service">aop:after-returning>
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="service">aop:after-throwing>
            <aop:after method="afterPrintLog" pointcut-ref="service">aop:after>
            <aop:pointcut id="service" expression="execution(* com.li.aop.*.*(..))">aop:pointcut>
        aop:aspect>
    aop:config>
  • 可以看到,后置通知和异常通知只会发生一个。

配置环绕通知

如果环绕通知方法仅如下编写,则运行时不会运行切入点。

 public void arroundPrintLog(){
        System.out.println("环绕通知开始记录日志了");
    }
 <aop:around method="arroundPrintLog" pointcut-ref="service">aop:around>
  • 在环绕通知方法中,明确何时执行切入点!
  • Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),明确调用切入点方法。
    *环绕通知方法改正:
 public Object arroundPrintLog(ProceedingJoinPoint point){
        Object rtvalue = null;
        try {
            Object[] args = point.getArgs();//得到方法执行所需的参数
            System.out.println("前置通知开始记录日志了");
            rtvalue = point.proceed(args);
            System.out.println("后置通知记录日志了");
            return rtvalue;
        } catch (Throwable throwable) {
            System.out.println("异常通知开始记录日志了");
           throw new RuntimeException(throwable);
        }finally {
            System.out.println("最终通知开始记录日志了");
        }

    }

三、基于注解配置

  • aop:aspect标签被@Aspect代替
  • aop:pointcut标签被@Pointcut代替
  • aop:before被@Before代替
  • 扫描配置包@Configuration
    *最后加上 @EnableAspectJAutoProxy
package com.li.utils;

import com.li.aop.AccountServiceImpl;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;

/*
* 用于提供日志的工具类,抽取公共代码
* */
@Configuration
@Import(AccountServiceImpl.class)
@EnableAspectJAutoProxy
@Component("logger")
@Aspect
public class Logger {

    @Pointcut("execution(* com.li.aop.*.*(..))")
    private void serviceMethod(){};
 /*  //前置通知
   @Before("serviceMethod()")
    public void beforePrintLog(){
        System.out.println("前置通知开始记录日志了");
    }
    //后置通知
    @AfterReturning("serviceMethod()")
    public void afterReturningPrintLog(){
        System.out.println("后置通知记录日志了");
    }
    //异常通知
    @AfterThrowing("serviceMethod()")
    public void afterThrowingPrintLog(){
        System.out.println("异常通知开始记录日志了");
    }
    //最后通知
    @After("serviceMethod()")
    public void afterPrintLog(){
        System.out.println("最后通知开始记录日志了");
    }*/
    //环绕通知
    @Around("serviceMethod()")
    public Object arroundPrintLog(ProceedingJoinPoint point){
    	Object rtvalue = null;
        try {

             Object[] args = point.getArgs();//得到方法执行所需的参数
            System.out.println("前置通知开始记录日志了");
            rtvalue = point.proceed(args);
            System.out.println("后置通知记录日志了");
            return rtvalue;
        } catch (Throwable throwable) {
            System.out.println("异常通知开始记录日志了");
           throw new RuntimeException(throwable);
        }finally {
            System.out.println("最终通知开始记录日志了");
        }

    }
}

你可能感兴趣的:(Spring中基于XML和注解方式的AOP实现)