Spring-AOP

简介

面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

作用及优势
  • 作用:在程序运行期间,在不修改源码的情况下对方法功能进行增强
  • 优势:减少重复代码、提高开发效率、便于维护
AOP底层实现

AOP底层是通过Spring提供的动态代理技术实现的,在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强

AOP动态代理技术
  • JDK代理:基于接口的动态代理技术
  import Proxy_JDK.Impl.TargetInterfaceImpl;
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;
  /**
   * @author: Thorn
   * @Date: 2020/9/19 21:41
   * @Description:
   */
  public class ProxyTest {
      public static void main(String[] args) {

          final TargetInterface tar = new TargetInterfaceImpl();

          //增强对象
          final Advice ad = new Advice();

          //返回值就是动态代理生成的对象
          TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                  tar.getClass().getClassLoader(),//目标对象类加载器
                  tar.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
                  new InvocationHandler() {
              //调用代理对象的任何方法,实质执行的都是invoke方法
              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  //前置增强
                  ad.before();
                  //执行目标方法
                  Object invoke = method.invoke(tar,args);
                  //后置增强
                  ad.after();
                  return invoke;
              }
          });
          //调用代理对象的方法
          proxy.save();
      }
  }
  • cglib代理:基于父类的动态代理技术
  import org.springframework.cglib.proxy.Enhancer;
  import org.springframework.cglib.proxy.MethodInterceptor;
  import org.springframework.cglib.proxy.MethodProxy;
  import java.lang.reflect.Method;
  /**
   * @author: Thorn
   * @Date: 2020/9/20 15:14
   * @Description:
   */
  public class tProxyTes {
      public static void main(final String[] args) {
          //目标对象
          final TargetInterface tar = new TargetInterfaceImpl();
          //增强对象
          final Advice ad = new Advice();

          //返回值就是动态代理的生成对象,基于cglib
          //1.创建增强器
          Enhancer en = new Enhancer();
          //2.设置父类
          en.setSuperclass(TargetInterface.class);
          //3.设置回调
          en.setCallback(new MethodInterceptor() {
              public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                  //执行前置
                  ad.before();

                  Object invoke = method.invoke(tar, args);

                  //执行后置
                  ad.after();
                  return invoke;
              }
          });
          //4.创建代理对象
          TargetInterface proxy = (TargetInterface) en.create();
          proxy.save();
      }
  }
AOP相关概念
  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在Spring中,这些点指的就是方法,因为Spring只支持方法类型的连接点
  • Pointcut(切入点):所谓切入点是指我们要对那些Joinpoint进行拦截的定义(被增强的方法)
  • Advice(通知增强):所谓通知是指拦截到Joinpoint之后所要做的事就是通知(封装业务逻辑的方法)
  • Aspect(切面):是切入点和通知的结合(切点+通知)
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入 (将切点与通知结合的过程)
AOP开发明确的事项
  1. 需要编写的内容

    • 编写核心业务代码(目标类的目标方法)
    • 编写切面类,切面类中有通知(增强功能方法)
    • 在配置文件中,配置织入关系,即将那些通知与那些连接点结合
  2. AOP技术实现的内容

    Spring框架监控切入点的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行

  3. AOP底层使用哪种代理方式

    Spring判断目标类是否实现了接口

    • 实现了接口:JDK
    • 没有实现:Cglib

基于XML的AOP开发

快速入门
  1. 导入相关坐标

  2. 创建目标接口和目标类(内部有切点)

  3. 创建切面类(内部有增强方法)

  4. 将目标类和切面类的对象创建权交给Spring

  5. 在applicationContext.xml中配置织入关系

  6. 测试代码

   <?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
   <!--配置目标对象-->
       <bean id="target" class="aop.TargetInterfaceImpl"/>
   <!--切面-->
       <bean id="myaspect" class="aop.MyAspect"/>
   <!--配置织入:告诉Spring框架那些方法(切点)需要进行那些增强-->
       <aop:config>
   <!--声明切面-->
           <aop:aspect ref="myaspect">
   <!--切面=切点+通知-->
               <aop:before method="before" pointcut="execution(public void aop.TargetInterfaceImpl.save())" />
           </aop:aspect>
       </aop:config>
   </beans>
  1. pointcut(切点表达式的写法)

    • execution([修饰符] 返回值类型 包名.类名.方法名(参数))
    • 访问修饰符可以省略
    • 返回值类型、包名、类名、方法名可以使用*代表任意
    • 包名与类名之间一个点.代表当前包下类,两个点..代表当前包及其子包下的类
    • 参数列表可以使用两个..表示任意个数,任意类型的参数列表
  2. 通知类型

    • 语法

      <aop:通知类型 method="切面类中方法名" pointcut="切点表达式" />

    • <aop:before>:前置通知,在切入点的方法之前执行

    • <aop:after-returning>:后置通知,在切入点的方法之后执行

    • <aop:around>:环绕通知,在切入点的方法之前之后都执行

      //ProceedingJoinPoint : 正在执行的连接点 == 切点
      public Object around(ProceedingJoinPoint pjp) throws Throwable {
         System.out.println("环绕前增强。。。。。");
         Object proceed = pjp.proceed();//切点方法
         System.out.println("环绕后增强。。。。。");
         return proceed;
      }
    • <aop:throwing>:异常抛出通知,在切入点的方法出现异常时执行

    • <aop:after>:最终通知,无论增强方法执行是否有异常都执行

  3. 切点表达式的抽取

    • 语法:

      • 将poincut代替为poincut-ref
    • 步骤:

      • 在最上层抽取
      • 调用
      <!--配置织入:告诉Spring框架那些方法(切点)需要进行那些增强-->
      <aop:config>
      <!--声明切面-->
         <aop:aspect ref="myaspect">
      <!--切面=切点+通知-->
             <aop:pointcut id="mypoin" expression="execution(public void aop.TargetInterfaceImpl.save())"/>
             <aop:around method="around" pointcut-ref="mypoin" />
         </aop:aspect>
      </aop:config>

基于注解AOP开发

快速入门
  1. 创建目标接口和目标类(内部有切点)

  2. 创建切面类(内部有增强方法)

  3. 将目标类和切面类的对象创建权交给Spring

    @Component("myaspect")
    @Aspect//标注此类时切面类
    public class MyAspect
  4. 在切面类中使用注解配置织入关系

   //配置前置增强
   @Before("execution(* anno.TargetInterfaceImpl.save())")
   public void before(){
      System.out.println("前置增强。。。。。");
   }
  1. 在配置文件中开启组件扫描和AOP的自动代理

    <!--组件扫描-->
    <context:component-scan base-package="anno"/>
    <!--AOP自动代理-->
    <aop:aspectj-autoproxy/>
  2. 测试代码

注解的通知类型及切点表达式的抽取
  • 通知类型

    • @Before:前置通知
    • @AfterReturning:后置通知
    • @Around:环绕通知
    • @AfterThrowing:异常抛出通知
    • @After:最终通知
  • 表达式抽取

    • @Pointcut注解
      //抽取注解表达式
      @Pointcut("execution(* anno.TargetInterfaceImpl.save())")
      public void Pointcut(){};
    
      //通过方法名引用
      @After("Pointcut()")
      public void after(){}
    
      //通过类名.方法名引用
      @AfterReturning("MyAspect.Pointcut()")
      public void returning(){}
Last modification:September 27th, 2020 at 09:10 am
如果觉得我的文章对你有用,请随意赞赏