AOP:(Aspect Oriented Programming)面向切面编程;
OOP:(Object Oriented Programming )面向对象编程;
面向切面编程:基于OOP基础之上新的编程思想;
指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式,面向切面编程;
场景:计算器运行计算方法的时候进行日志记录;
加日志记录:
1)、直接编写在方法内部;不推荐,修改维护麻烦;
日志记录:系统的辅助功能;
业务逻辑:(核心功能)
耦合;
2)、我们希望的是;
业务逻辑:(核心功能);日志模块;在核心功能运行期间,自己动态的加上;
运行的时候,日志功能可以加上;
可以使用动态代理来将日志代码动态的在目标方法执行前后先进行执行;
动态代理代码
Calculator.java:
package com.atguigu.inter; /** * @Title: Calculator * @Description: * @Author: * @Version: 1.0 * @create 2020/6/7 19:42 */ public interface Calculator { public int add(int i, int j); public int sub(int i, int j); public int mul(int i, int j); public int div(int i, int j); }
MyMathCalculator.java:
package com.atguigu.impl; import com.atguigu.inter.Calculator; /** * @Title: MyMathCalculator * @Description: * @Author: * @Version: 1.0 * @create 2020/6/7 19:43 */ public class MyMathCalculator implements Calculator { @Override public int add(int i, int j) { //System.out.println("【add】方法开始了,它使用的参数是:【"+i+"】,【"+j+"】"); //考虑方法的兼容性 //LogUtils.logStart(i, j); int result = i + j; // System.out.println("【add】方法运行完成,计算结果是:【"+result+"】"); return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; } }
LogUtils.java:
package com.atguigu.utils; import java.lang.reflect.Method; import java.util.Arrays; /** * @Title: LogUtils * @Description: * @Author: * @Version: 1.0 * @create 2020/6/7 19:55 */ public class LogUtils { public static void logStart(Method method, Object... args) { System.out.println("[" + method.getName() + "]方法开始执行,使用的参数列表" + Arrays.asList(args) ); } public static void logReturn(Method method,Object result){ System.out.println("[" + method.getName() + "]方法正常执行完成了,计算结果是" + result); } public static void logException(Method method, Exception e) { System.out.println("["+method.getName()+"]方法出现异常了,异常信息是:"+e.getCause()+",这个异常已经通知测试测试小组进行排查"); } public static void logEnd(Method method) { System.out.println("["+method.getName()+"]方法最终结束"); } }
CalculatorProxy.java:
package com.atguigu.proxy; import com.atguigu.inter.Calculator; import com.atguigu.utils.LogUtils; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 帮Calculator.java生成代理对象的类 */ public class CalculatorProxy { /** * 为传入的参数对象创建一个动态代理对象 * <p> * Calculator calculator:被代理对象 */ public static Calculator getProxy(final Calculator calculator) { //方法执行器:帮目标对象执行目标方法 InvocationHandler h = new InvocationHandler() { /** * Object proxy:代理对象:给jdk使用,任何时候都不要动这个对象 * Method method:当前将要执行的目标对象的方法 * Object[] args:这个方法调用时外界传入的参数值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //利用反射执行目标方法 //目标方法执行后的返回值 // System.out.println("这是动态代理将要帮你执行方法"); Object result = null; try { LogUtils.logStart(method,args); result = method.invoke(calculator, args); LogUtils.logReturn(method,result); } catch (Exception e) { LogUtils.logException(method,e); }finally { LogUtils.logEnd(method); } //返回值必须返回出去外界才能拿到真正执行后的返回值 return result; } }; Class<?>[] interfaces = calculator.getClass().getInterfaces(); ClassLoader loader = calculator.getClass().getClassLoader(); //Proxy为目标对象创建代理对象 Object proxy = Proxy.newProxyInstance(loader, interfaces, h); return (Calculator) proxy; } }
AOPTest.java:
package com.atguigu.test; import com.atguigu.impl.MyMathCalculator; import com.atguigu.inter.Calculator; import com.atguigu.proxy.CalculatorProxy; import org.junit.Test; import java.util.Arrays; /** * @Title: AOPTest * @Description: * @Author: * @Version: 1.0 * @create 2020/6/7 19:46 */ public class AOPTest { /** * 有了动态代理,日志记录可以做的非常强大,而且与业务逻辑解耦 * * jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为它创建代理对象的 */ @Test public void test() { Calculator calculator = new MyMathCalculator(); // calculator.add(1, 2); // calculator.div(2, 1); // System.out.println("=============="); //如果是拿到了这个对象的代理对象,代理对象将执行加减乘除 Calculator proxy = CalculatorProxy.getProxy(calculator); //代理对象和被代理对象唯一能产生的关联就是实现了同一个接口 System.out.println(Arrays.asList(proxy.getClass().getInterfaces())); proxy.add(2, 1); } }
动态代理:
1)、写起来难;
2)、jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的;
Spring动态代理难;Spring实现了AOP功能;底层就是动态代理;
1)、可以利用Spring一句代码都不写的去创建动态代理;
实现简单,而且没有强制要求目标对象必须实现接口;
将某段代码(日志)动态的切入(不把日志代码写死在业务逻辑方法中)到指定方法(加减乘除)的指定位置(方法的开始、结束、异常。。。)进行运行的这种编程方式(Spring简化了面向切面编程)