代理模式与AOP
文章目录系列文章目录前言一、pandas是什么二、使用步骤1.引入库2.读入数据总结一、代理模式是什么它的作用就是通过提供一个代理类让我们在调用目标方法的时候不再是直接对目标方法进行调用而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法减少对目标方法的调用和打扰同时让附加功能能够集中在一起也有利于统一维护。1.相关术语代理将非核心逻辑剥离出来以后封装这些非核心逻辑的类、对象、方法。对象: 被代理“套用”了非核心逻辑代码的类、对象、方法。二、案例分析1.计算器案例package com.springcloud.inter; 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); }package com.springcloud.inter.imp; import com.springcloud.inter.Calculator; 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; } }问题:它违背了面向对象设计中的“单一职责原则”将非核心业务逻辑日志与核心业务逻辑加减乘除计算严重耦合在了一起。2.解决方案2.1 静态代理在程序编译期由程序员手动编写或通过工具生成一个代理类这个代理类和目标类实现相同的接口并在内部持有一个目标对象的引用。public class CalculatorStaticProxy implements Calculator { // 将被代理的目标对象声明为成员变量 private Calculator target; public CalculatorStaticProxy(Calculator target) { this.target target; } Override public int add(int i, int j) { // 附加功能由代理类中的代理方法来实现 System.out.println([日志] add 方法开始了参数是 i , j); // 通过目标对象来实现核心业务逻辑 int addResult target.add(i, j); System.out.println([日志] add 方法结束了结果是 addResult); return addResult; } }缺点静态代理确实实现了解耦但是由于代码都写死了完全不具备任何的灵活性。就拿日志功能来说将来其他地方也需要附加日志那还得再声明更多个静态代理类那就产生了大量重复的代码日志功能还是分散的没有统一管理。2.2 动态代理在程序运行期间由 JVM 根据接口在内存中“凭空”自动生成一个代理类并将所有方法的调用统一拦截交由开发者编写的InvocationHandler调用处理器来集中处理。核心要素目标接口Interface规定了代理对象能干什么。JDK 动态代理强依赖接口。目标对象Target包含真实业务逻辑的实例。调用处理器InvocationHandler代理对象的“大脑”。它定义了当方法被调用时到底要执行什么额外逻辑前置、后置以及如何调用真实对象。package com.springcloud; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; /** * 创建动态代理 * **/ public class ProxyFactory { //目标对象 private Object target; public ProxyFactory(Object target) { this.target target; } //代理对象 /** * ClassLoader loader, 类加载器,加载动态生成代理类的类加载器--用哪个类加载器来加载即将在内存中动态生成的那个“代理类”。 * Class?[] interfaces, 目标对象实现的所有的接口的Class类型的数组--这个新生成的代理类长什么样它能干什么 * InvocationHandler h 设置代理对象实现目标对象方法的过程 * * **/ public Object getProxy(){ //参数1 ClassLoader classLoadertarget.getClass().getClassLoader(); //参数2 Class?[] interfaces target.getClass().getInterfaces(); //生成代理对象 return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { /** * proxy 代理对象--它的作用是负责把 JVM 在内存中动态生成的那个“代理类”加载到内存中。 * method:当前正在被调用的方法对象 * args:调用方法时传入的参数 * **/ Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println([动态代理][日志] method.getName()参数 Arrays.toString(args)); //method.invoke(目标对象, 参数) Object result method.invoke(target, args); System.out.println([动态代理][日志] method.getName()结果 result); return result; } }); } }2.3 测试package com.springcloud; import com.springcloud.inter.Calculator; import com.springcloud.inter.imp.CalculatorLogImpl; public class TestProxy { public static void main(String[] args) { //创建代理对象 ProxyFactory proxyFactorynew ProxyFactory(new CalculatorLogImpl()); Calculator proxy (Calculator) proxyFactory.getProxy(); proxy.add(1,2); } }三、AOPAOPAspect Oriented Programming是一种设计思想是软件设计领域中的面向切面编程它是面向对象编程的一种补充和完善它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性同时提高了开发的效率。3.1 相关概念横切关注点分散在每个各个模块中解决同一样的问题如用户验证、日志管理、事务处理、数据缓存都属于横切关注点。也就是非核心业务功能。通知(增强) :增强就是你要增强的功能比如日志、事务等;每一个横切关注点上要做的事情都需要写一个方法来实现这样的方法就叫通知方法- 前置通知在被代理的目标方法**前**执行- 返回通知在被代理的目标方法**成功结束**后执行**寿终正寝**- 异常通知在被代理的目标方法**异常结束**后执行**死于非命**- 后置通知在被代理的目标方法**最终结束**后执行**盖棺定论**-环绕通知使用try...catch...finally结构围绕**整个**被代理的目标方法包括上面四种通知对应的所有位置。切面:封装通知方法的类。目标:被代理的对象。连接点:通俗说就是spring允许你使用通知的地方。切入点:通俗说要实际去增强的方法。3.2 作用- 简化代码把方法中固定位置的重复的代码**抽取**出来让被抽取的方法更专注于自己的核心功能提高内聚性。- 代码增强把特定的功能封装到切面类中看哪里有需要就往上套被**套用**了切面逻辑的方法就被切面给增强了。3.3 案例-计算器1.maven 依赖dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version6.0.2/version /dependency !-- AOP-- !--spring aop依赖-- !-- dependency-- !-- groupIdorg.springframework/groupId-- !-- artifactIdspring-aop/artifactId-- !-- version6.0.2/version-- !-- /dependency-- !--spring aspects依赖-- dependency groupIdorg.springframework/groupId artifactIdspring-aspects/artifactId version6.0.2/version /dependencySpring-context里边存在aop的包所以不需要去再加2.配置类package com.springcloud.AOP.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * ComponentScan(basePackages com.springcloud.AOP)开启扫描 *EnableAspectJAutoProxy :开启 AspectJ 的自动代理功能 * AspectJ 是一个专门用来实现 AOP面向切面编程的框架。 * **/ Configuration EnableAspectJAutoProxy ComponentScan(basePackages com.springcloud.AOP) public class AspectConfig { }3.切面package com.springcloud.AOP.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; import java.util.Arrays; Component Aspect //代表切面类 public class LogAspect { //设置切入点和通知类型每种通知类型都需要加一个方法 /** * - 前置通知使用Before注解标识在被代理的目标方法**前**执行 * - 返回通知使用AfterReturning注解标识在被代理的目标方法**成功结束**后执行**寿终正寝** * - 异常通知使用AfterThrowing注解标识在被代理的目标方法**异常结束**后执行**死于非命** * - 后置通知使用After注解标识在被代理的目标方法**最终结束**后执行**盖棺定论** * - 环绕通知使用Around注解标识使用try...catch...finally结构围绕**整个**被代理的目标方法包括上面四种通知对应的所有位置 * **/ /*** * execution(public int com.springcloud.AOP.inter.imp.CalculatorImpl.add(int,int)) * execution参数: * 访问修饰符 增强方法返回值类型 增强方法所在类的全路径 方法名称(方法参数) * 里边可以用*之类的代替 * add(..) 代表任意参数 *比如:public int com.springcloud.AOP.inter.imp.CalculatorImpl.*(..)代表CalculatorImpl所有方法 * * **/ Before(execution(public int com.springcloud.AOP.inter.imp.CalculatorImpl.add(..))) public void beforeMethod(){ System.out.println(------前置通知----); } /** * *可以表示 访问修饰符 增强方法返回值类型是任意的 * JoinPoint切入点可以得到一些方法的信息 * */ After(execution( * com.springcloud.AOP.inter.imp.CalculatorImpl.*(..))) public void afterMethod(JoinPoint joinPoint){ String name joinPoint.getSignature().getName(); Object[] args joinPoint.getArgs(); System.out.println(------后置通知--方法名称:name方法参数: Arrays.toString(args)); } /** * 返回通知可以得到方法的·返回值 * returning result需要与Object result名称一致 * **/ AfterReturning(value execution( * com.springcloud.AOP.inter.imp.CalculatorImpl.*(..)),returning result) public void afterReturning(JoinPoint joinPoint,Object result){ String name joinPoint.getSignature().getName(); Object[] args joinPoint.getArgs(); System.out.println(------返回通知--方法名称:name方法参数: Arrays.toString(args)方法返回值:result); } /** * 没有异常就不会执行 * 异常就不会执行返回通知 * **/ AfterThrowing(value pointCut(),throwing ex) public void afterThrowing(JoinPoint joinPoint,Throwable ex){ String name joinPoint.getSignature().getName(); Object[] args joinPoint.getArgs(); System.out.println(------异常通知--方法名称:name方法参数: Arrays.toString(args)异常信息:ex); } /** * 想“掌控全局”比如做性能监控、动态代理、事务控制那就必须用环绕通知。它是 Spring AOP 中最强大、最灵活的通知类型。 *可以修改传入的参数,修改返回值,直接拦截不让执行 * **/ Around(execution( * com.springcloud.AOP.inter.imp.CalculatorImpl.*(..))) public Object around(ProceedingJoinPoint joinPoint){ String name joinPoint.getSignature().getName(); Object[] args joinPoint.getArgs(); Object resultnull; try { System.out.println(------环绕通知目标方法之前执行-------); //执行目标方法 result joinPoint.proceed(); System.out.println(------环绕通知目标方法返回值之后执行-------); } catch (Throwable e) { System.out.println(------环绕通知目标方法执行异常之后执行-------); }finally { System.out.println(------环绕通知目标方法执完行毕执行-------); } return result; } /** * 复用切入点表达式 * * **/ Pointcut(execution( * com.springcloud.AOP.inter.imp.CalculatorImpl.*(..))) public void pointCut(){ } }4.测试public class AopTest { public static void main(String[] args) { //读取配置类 ApplicationContext context new AnnotationConfigApplicationContext(AspectConfig.class); // 2. 从容器中获取 Bean (和以前一样) Calculator calculator context.getBean(Calculator.class); // 3. 调用方法触发 AOP calculator.add(2, 3); } }