spring-aop

前提设置:

声明计算器接口Calculator,包含加减乘除的抽象方法:

public interface Calculator {
  int add(int i, int j);
  int sub(int i, int j);
  int mul(int i, int j);
  int div(int i, int j);
}

创建实现类:

public class CalculatorPureImpl implements Calculator {
   @Override
   public int add(int i, int j) {
     int result = i + j;
     System.out.println("方法内部 result = " + result);
     return result;
}
   @Override
   public int sub(int i, int j) {
     int result = i - j;
     System.out.println("方法内部 result = " + result);
     return result;
}
   @Override
   public int mul(int i, int j) {
     int result = i * j;
     System.out.println("方法内部 result = " + result);
     return result;
}
   @Override
   public int div(int i, int j) {
     int result = i / j;
     System.out.println("方法内部 result = " + result);
     return result;
}
}

创建带日志功能的实现类:

public class CalculatorLogImpl implements Calculator {
   @Override
   public int add(int i, int j) {
     System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
     int result = i + j;
     System.out.println("方法内部 result = " + result);
     System.out.println("[日志] add 方法结束了,结果是:" + result);
     return result;
    }
   @Override
   public int sub(int i, int j) {
     System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
     int result = i - j;
     System.out.println("方法内部 result = " + result);
     System.out.println("[日志] sub 方法结束了,结果是:" + result);
     return result;
    }
   @Override
   public int mul(int i, int j) {
     System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
     int result = i * j;
     System.out.println("方法内部 result = " + result);
     System.out.println("[日志] mul 方法结束了,结果是:" + result);
     return result;
    }
   @Override
   public int div(int i, int j) {
     System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
     int result = i / j;
     System.out.println("方法内部 result = " + result);
     System.out.println("[日志] div 方法结束了,结果是:" + result);
     return result;
    }
}

代理模式:

  1. 概念: 二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

    • 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
    • 目标:被代理“套用”了非核心逻辑代码的类、对象、方法。
  2. 静态代理: 创建静态代理类:

    public class CalculatorStaticProxy implements Calculator{
     private CalculatorImpl target;
     public CalculatorStaticProxy(CalculatorImpl calculatorImpl) {
         this.target = calculatorImpl;
     }
     @Override
     public int add(int i, int j) {
         System.out.println("日志,方法:add,参数"+i+","+j);
         int result = target.add(i, j);
         System.out.println("日志,方法:add,结果:"+result);
         return result;
     }
     @Override
     public int sub(int i, int j) {
         System.out.println("日志,方法:sub,参数"+i+","+j);
         int result = target.sub(i, j);
         System.out.println("日志,方法:sub,结果:"+result);
         return result;
     }
     @Override
     public int mul(int i, int j) {
         System.out.println("日志,方法:mul,参数"+i+","+j);
         int result = target.mul(i, j);
         System.out.println("日志,方法:mul,结果:"+result);
         return result;
     }
     @Override
     public int div(int i, int j) {
         System.out.println("日志,方法:div,参数"+i+","+j);
         int result = target.div(i, j);
         System.out.println("日志,方法:div,结果:"+result);
         return result;
     }
    }
    
    
  3. 动态代理

    生产代理对象的工厂类:

    newProxyInstance():创建一个代理实例

    其中有三个参数:

    • ClassLoader loader:指定加载动态生成的代理类类加载器
    • Class[] interfaces:获取目标对象实现的所有的接口的class对象的数组
    • InvocationHandler h:设置代理类中的抽象方法如何重写
    public class ProxyFactory {
     private Object target;
     public ProxyFactory(Object target) {
         this.target = target;
     }
     public Object getProxy() {
     
         ClassLoader classLoader = ProxyFactory.class.getClassLoader();
         Class<?>[] interfaces = target.getClass().getInterfaces();
         InvocationHandler handler = new InvocationHandler() {
             @Override
             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                 Object result = null;
                 try {
                     System.out.println("日志,方法:"+method.getName() + ",参数"+ Arrays.toString(args));
                     //proxy:表示代理对象,method:表示要执行的方法,args:表示要执行的方法到的参数列表
                     result = method.invoke(target, args);
                     System.out.println("日志,方法:"+method.getName() + ",结果"+ result);
                     return result;
                 } catch (IllegalAccessException e) {
                     e.printStackTrace();
                     System.out.println("日志,方法:"+method.getName() + ",异常:"+e);
                 } finally {
                     System.out.println("日志,方法:"+method.getName() + ",方法执行完毕");
                 }
                 return result;
             }
         };
         return Proxy.newProxyInstance(classLoader,interfaces,handler);
     }
    }
    

测试类:

@Test
    public void test2(){
        ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
        Calculator proxy = (Calculator) proxyFactory.getProxy();
        proxy.add(1,4);
    }

动态代理:

1.jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口在com.sun.proxy包下,类名为$proxy2

2.cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下

AOP概念及相关术语

概述:

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

横切关注点:

从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

通知:

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

  • 前置通知:在被代理的目标方法前执行
  • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
  • 异常通知:在被代理的目标方法异常结束后执行(死于非命)
  • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
  • 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

切面:封装通知方法的类

目标:被代理的对象

代理:向目标对象应用通知之后创建的代理对象

连接点:把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点

切入点:定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句

aop的作用:

简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。

代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。


本文章使用limfx的vscode插件快速发布