## 为什么要使用切片(AOP)编程 *** ### 场景代码 ```java @Override public int add(int a, int b) { int result = a + b; System.out.println(STR."方法内部 result = \{result}"); return result; } @Override public int subtract(int a, int b) { int result = a - b; System.out.println(STR."方法内部 result = \{result}"); return result; } @Override public int multiply(int a, int b) { int result = a * b; System.out.println(STR."方法内部 result = \{result}"); return result; } @Override public double divide(int a, int b) { double result = (double) a / b; System.out.println(STR."方法内部 result = \{result}"); return result; } ``` 如上方代码所示,如果需要给这些代码都进行日志记录,那么就会变成: ```java public class CalculatorImpl implements Calculator { @Override public int add(int a, int b) { int result = a + b; System.out.println("日志记录中"); System.out.println(STR."方法内部 result = \{result}"); System.out.println("日志记录结束"); return result; } @Override public int subtract(int a, int b) { int result = a - b; System.out.println("日志记录中"); System.out.println(STR."方法内部 result = \{result}"); System.out.println("日志记录结束"); return result; } @Override public int multiply(int a, int b) { int result = a * b; System.out.println("日志记录中"); System.out.println(STR."方法内部 result = \{result}"); System.out.println("日志记录结束"); return result; } @Override public double divide(int a, int b) { double result = (double) a / b; System.out.println("日志记录中"); System.out.println(STR."方法内部 result = \{result}"); System.out.println("日志记录结束"); return result; } } ``` #### 解决方法 可以使用AOP的思想,统一为所有方法添加日志记录。 ## 代理模式 *** #### 概念 >代理模式是一种常见的设计模式,它允许通过代理对象来控制对原始对象的访问。代理模式可以在不改变原始对象的情况下,为其添加额外的功能。代理可以用于许多不同的场景,例如远程访问、安全性控制、缓存、延迟加载等等。在代理模式中,通常有三个角色:抽象主题、真实主题和代理主题。抽象主题是定义了真实主题和代理主题共同遵循的接口或抽象类,真实主题是实际执行业务逻辑的对象,而代理主题则是用于控制对真实主题的访问,并可能添加额外功能的对象。代理模式可以使代码更加模块化、可扩展和易于维护。. ##### 使用代理前: ![](../assest/Pasted%20image%2020240401093846.png) ##### 使用代理后: ![](../assest/Pasted%20image%2020240401093934.png) ### 静态代理 *** #### 示例代码 ```java public class CalculatorStaticProxy implements Calculator { // 被代理目标对象传递过来 private Calculator calculator; public CalculatorStaticProxy(Calculator calculator) { this.calculator = calculator; } @Override public int add(int a, int b) { // 输出日志 System.out.println(STR."[日志]: add方法开始了,参数是:\{a}, \{b}"); // 核心业务 int result = calculator.add(a, b); System.out.println(STR."[日志]: add方法结束了,结果是:\{result}"); return result; } @Override public int subtract(int a, int b) { // 输出日志 System.out.println(STR."[日志]: subtract方法开始了,参数是:\{a}, \{b}"); // 核心业务 int result = calculator.subtract(a, b); System.out.println(STR."[日志]: subtract方法结束了,结果是:\{result}"); return result; } @Override public int multiply(int a, int b) { // 输出日志 System.out.println(STR."[日志]: multiply方法开始了,参数是:\{a}, \{b}"); // 核心业务 int result = calculator.multiply(a, b); System.out.println(STR."[日志]: multiply方法结束了,结果是:\{result}"); return result; } @Override public double divide(int a, int b) { // 输出日志 System.out.println(STR."[日志]: divide方法开始了,参数是:\{a}, \{b}"); // 核心业务 double result = calculator.divide(a, b); System.out.println(STR."[日志]: divide方法结束了,结果是:\{result}"); return result; } } ``` #### 缺点 >静态代理虽然可以在不修改源代码的情况下增加额外的功能,但它也存在一些缺陷。 >首先,静态代理中**代理类和被代理类需要实现相同的接口或继承相同的父类**,这就限制了被代理类的选择范围。 >其次,在使用静态代理时,每一个需要**被增强的方法都需要在代理类中进行实现**,如果被增强的方法很多,会导致代码量大大增加,使得代码难以维护。 >此外,静态代理还存在着性能问题,因为在每次调用被代理对象的方法时都需要经过一层额外的代理对象进行处理,这会带来额外的开销。 >最后,在程序需要动态地改变代理行为时,静态代理无法满足需求。 >因此,在实际应用中需要根据具体情况选择合适的设计模式来解决问题。 ### 动态代理 *** #### 示意图 ![](../assest/Pasted%20image%2020240401095524.png) #### 核心对象方法 ##### Proxy - `newInstace()`: Java中用于创建动态代理对象的方法 参数: - `ClassLoader loader`:加载动态生成代理类的类加载器 - `Class\\[] interfaces`:获取对象实现的所有接口的class类型数组 - `java.lang.reflect.InvocationHandler h`:设置代理对象实习那目标对象方法的 #### 示例代码 ```java public class ProxyFactory { // 目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } // 返回代理对象 public Object getProxy() { // 加载动态生成代理类的类加载器 ClassLoader classLoader = target.getClass().getClassLoader(); // 获取目标对象的所有接口 Class[] interfaces = target.getClass().getInterfaces(); // 设置代理对象实现目标对象方法的过程 InvocationHandler invocationHandler = new InvocationHandler() { Object result = null; /** * @param proxy 代理对象 * @param method 目标对象方法 * @param args 目标对象方法参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 方法调用之前 System.out.println(STR."[动态代理][日志]\{method.getName()}, 参数:\{Arrays.toString(args)}"); // 调用目标方法 result = method.invoke(target, args); // 调用方法之后 System.out.println(STR."[动态代理][日志]\{method.getName()}, 结果:\{result}"); return result; } }; return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); } } ``` ## 相关术语 *** 参考:[AOP相关术语](AOP相关术语.md) ## 基于注解的AOP *** ![基于注解开发AOP](基于注解开发AOP.md) ## 基于XML开发AOP *** ![基于XML开发AOP](基于XML开发AOP.md)