怎样设计个人网站,长沙网络营销外包,wordpress 建站公司,电话销售做网站的术语文章目录 一、代理模式静态代理动态代理代理模式与AOP 二、Spring AOPSping AOP用来处理什么场景jdk 动态代理cglib 动态代理面试题#xff1a;讲讲Spring AOP的原理与执行流程 总结 一、代理模式
代理模式是一种结构型设计模式#xff0c;它允许对象提供替代品或占位符讲讲Spring AOP的原理与执行流程 总结 一、代理模式
代理模式是一种结构型设计模式它允许对象提供替代品或占位符以控制对这个对象的访问。代理对象通常充当客户端和实际服务对象之间的中介以实现对服务对象的间接访问。 代理模式的实现有许多种方式其中最常见的方式是静态代理和动态代理。
静态代理
静态代理是指在编译期间就已经确定了代理类和目标类的关系代理类和目标类的关系在程序运行之前就已经确定。下面是一个简单的静态代理模式示例
假设有一个接口 IPrinter 表示打印机它有一个 print 方法
public interface IPrinter {void print(String document);
}现在有一个实现了 IPrinter 接口的类 Printer
public class Printer implements IPrinter {Overridepublic void print(String document) {System.out.println(打印机正在打印 document);}
}现在我们想通过代理来记录打印机打印了哪些文件我们可以创建一个代理类 PrinterProxy
public class PrinterProxy implements IPrinter {private IPrinter printer;public PrinterProxy(IPrinter printer) {this.printer printer;}Overridepublic void print(String document) {System.out.println(打印机开始工作正在打印 document);printer.print(document);System.out.println(打印机打印完成。);}
}PrinterProxy 类实现了 IPrinter 接口并在 print 方法中调用真正的打印机的 print 方法同时在这个方法之前和之后打印一些信息来记录打印机工作的情况。
现在我们可以使用以下代码来测试代理类的工作
IPrinter printer new Printer();
IPrinter printerProxy new PrinterProxy(printer);printerProxy.print(茶叶蛋的前端简历);运行程序后输出的结果将是
打印机开始工作正在打印茶叶蛋的前端简历
打印机正在打印茶叶蛋的前端简历
打印机打印完成。从输出结果可以看出代理类 PrinterProxy 确实在调用真正的打印机类 Printer 的 print 方法之前和之后打印了一些信息。这是静态代理模式的基本实现方式。
结合我们生活来理解的话最常见就是在大城市里租房了房源太多你忙于工作此时你找了个代理人上面的代理类 帮你处理找房子这个事你的业务逻辑。这里时候代理人就可以在找房子这个事的前前后后defore after around 等做文章了。例如在找房子之前给你索取代理费织入的逻辑。找到房子帮你办理入住手续。
动态代理
Java提供了动态代理的支持通过Java反射机制可以实现动态代理。我们可以使用Java自带的 java.lang.reflect.Proxy 类来实现动态代理。
以静态代理的打印机为例我们可以使用动态代理来生成代理类。需要实现一个 InvocationHandler 接口该接口包含一个 invoke 方法我们可以在这个方法中实现代理的逻辑。
示例代码如下
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface IPrinter {void print(String document);
}class Printer implements IPrinter {Overridepublic void print(String document) {System.out.println(打印机正在打印 document);}
}class PrinterHandler implements InvocationHandler {private Object target;public PrinterHandler(Object target) {this.target target;}Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println(打印机开始工作正在打印 args[0]);Object result method.invoke(target, args);System.out.println(打印机打印完成。);return result;}
}public class Main {public static void main(String[] args) {Printer printer new Printer();PrinterHandler handler new PrinterHandler(printer);IPrinter printerProxy (IPrinter) Proxy.newProxyInstance(printer.getClass().getClassLoader(),printer.getClass().getInterfaces(),handler);printerProxy.print(茶叶蛋的简历);}
}在此示例中我们实现了 InvocationHandler 接口并在 invoke 方法中实现了代理逻辑。在 main 方法中我们首先创建了一个真正的打印机类 Printer 的实例然后创建了一个 PrinterHandler 对象并将其传递给 Proxy.newProxyInstance 方法该方法返回一个实现了 IPrinter 接口的代理类的实例。
在运行过程中当使用代理类的 print 方法时它将被拦截并调用 PrinterHandler 的 invoke 方法该方法将在真实的打印机类 Printer 的 print 方法之前和之后执行一些逻辑。
从输出结果可以看出动态代理确实实现了和静态代理相同的代理逻辑。
打印机开始工作正在打印茶叶蛋的简历
打印机正在打印茶叶蛋的简历
打印机打印完成。代理模式与AOP
代理模式和AOP面向切面编程是两个不同的概念但在某些方面存在相似之处。
代理模式是一种结构型设计模式用于为其他对象提供一个替代或协助的代理对象控制对原始对象的访问。代理对象通常具有与原始对象相同的接口因此可以无缝地替换原始对象但在访问原始对象时代理对象可以执行额外的逻辑或限制比如缓存对象或限制访问。
AOP是一种编程范式用于将通用功能与应用程序的业务逻辑相分离。AOP通过在程序中定义切面横切关注点并在运行时将它们与各种连接点方法调用、异常处理等连接起来实现了针对具体业务逻辑之外的通用功能的重用。
虽然代理模式可以在一定程度上实现AOP但AOP是更高级别的概念涵盖了更广泛的应用包括动态代理、依赖注入、解耦等方面。因此代理模式只是AOP的一种实现方式而AOP更多地考虑了程序的整体结构和可维护性。
切面理解
二、Spring AOP
2使用AOP需要的一些概念。
1.通知(Advice)
通知定义了在切入点代码执行时间点附近需要做的工作。支持五种类型的通知
Before(前) org.apringframework.aop.MethodBeforeAdvice After-returning(返回后) org.springframework.aop.AfterReturningAdvice After-throwing(抛出后) org.springframework.aop.ThrowsAdvice Arround(周围) org.aopaliance.intercept.MethodInterceptor Introduction(引入) org.springframework.aop.IntroductionInterceptor
2.连接点(Joinpoint) 程序能够应用通知的一个“时机”这些“时机”就是连接点例如方法调用时、异常抛出时、方法返回后等等。
3.切入点(Pointcut)
通知定义了切面要发生的“故事”连接点定义了“故事”发生的时机那么切入点就定义了“故事”发生的地点例如某个类或方法的名称Spring中允许我们方便的用正则表达式来指定。
4.切面(Aspect)
通知、连接点、切入点共同组成了切面时间、地点和要发生的“故事”。
5.引入(Introduction)
引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能。
6.目标(Target)
即被通知的对象如果没有AOP那么通知的逻辑就要写在目标对象中有了AOP之后它可以只关注自己要做的事解耦合
7.代理(proxy) 应用通知的对象详细内容参见设计模式里面的动态代理模式。
8.织入(Weaving) 把切面应用到目标对象来创建新的代理对象的过程织入一般发生在如下几个时机:
(1)编译时当一个类文件被编译时进行织入这需要特殊的编译器才可以做的到例如AspectJ的织入编译器
(2)类加载时使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
(3)运行时切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的原理应该是使用了JDK的动态代理技术。
Sping AOP用来处理什么场景
下面是一个简单的基于Spring AOP的例子
首先定义一个接口UserService和实现类UserServiceImpl
public interface UserService {void addUser(User user);User getUser(int id);
}Service
public class UserServiceImpl implements UserService {private MapInteger, User users new HashMap();Overridepublic void addUser(User user) {users.put(user.getId(), user);}Overridepublic User getUser(int id) {return users.get(id);}
}然后定义一个切面LoggingAspect来记录方法的执行时间
Aspect
Component
public class LoggingAspect {private Logger logger LoggerFactory.getLogger(getClass());Around(execution(* com.example.demo.UserService.*(..)))public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start System.currentTimeMillis();Object proceed joinPoint.proceed();long executionTime System.currentTimeMillis() - start;logger.info(joinPoint.getSignature() executed in executionTime ms);return proceed;}
}在这个切面中我们定义了一个Around通知用于环绕目标方法的执行。在执行方法前记录当前时间执行方法后计算时间差并将结果输出到日志中。
最后在Spring配置文件中开启AOP
beanscontext:component-scan base-packagecom.example.demo/aop:aspectj-autoproxy/
/beans使用aop:aspectj-autoproxy标签Spring会自动查找所有被Aspect注解标记的切面并为它们创建代理对象。
现在我们可以注入UserService并调用它的方法来测试AOP是否生效
Controller
public class UserController {Autowiredprivate UserService userService;GetMapping(/user/{id})ResponseBodypublic String getUser(PathVariable int id) {userService.getUser(id);return User retrieved;}PostMapping(/user)ResponseBodypublic String addUser(RequestBody User user) {userService.addUser(user);return User added;}
}当我们访问/user/{id}或/user时控制台会输出类似于下面的日志
com.example.demo.UserServiceImplxxxx executed in 10 msjdk 动态代理
JDK动态代理是一种在运行时动态生成代理类的机制所以也被称为运行时代理。它主要涉及到以下两个核心类
java.lang.reflect.Proxy该类是实现动态代理的关键类它提供了构造代理类实例所需的方法。java.lang.reflect.InvocationHandler该接口是实际处理代理对象方法调用的地方通过调用它的 invoke() 方法实现代理对象的方法调用。
JDK动态代理的实现原理
定义一个接口或者抽象类指定了需要被代理的方法。创建一个实现 InvocationHandler 接口的类它需要实现 invoke() 方法该方法是代理类的调用处理器负责处理被代理对象的方法调用。通过 Proxy 类的静态方法 newProxyInstance() 获取代理对象实例该方法接收三个参数ClassLoader、Class[] 和 InvocationHandler。其中ClassLoader 是代理类的 ClassLoaderClass[] 是指定被代理类实现的接口列表InvocationHandler 是实现了 invoke() 方法的调用处理器对象。当代理对象的方法被调用时代理对象会调用 InvocationHandler 实例的 invoke() 方法。该方法使用 Method 对象来调用实际的被代理对象的方法并返回结果。
总体来说JDK动态代理的实现过程涉及到反射和动态生成类的机制它可以在运行时创建代理对象并将代理对象的方法调用转发给 InvocationHandler 接口的实现类处理。
cglib 动态代理
CGLIBCode Generation Library是一个基于ASM一个 Java 字节码操作框架的代码生成库它可以在运行时动态生成字节码从而实现动态代理、AOP 等功能。
相比于 Java 中的 JDK 动态代理CGLIB 动态代理具有以下特点
JDK 动态代理只能代理接口而 CGLIB 可以代理普通类JDK 动态代理调用代理方法时需要通过反射调用而 CGLIB 利用字节码技术直接调用方法性能更高。
CGLIB 动态代理的基本原理是继承原始类或实现接口并在子类中重写原始类或接口的方法在方法中添加前置、后置等代理逻辑。CGLIB 动态代理一般使用 Enhancer 类来实现其核心 API 包括
setSuperclass设置被代理类的父类setCallback设置回调对象create创建代理对象。
使用 CGLIB 动态代理可以实现更加灵活的代理逻辑但是代理对象的创建和调用会消耗更多的资源需要根据实际情况进行使用。
面试题讲讲Spring AOP的原理与执行流程
Spring AOP面向切面编程是一种通过动态代理或字节码增强等技术在程序运行期间对指定方法进行增强的技术。Spring AOP的原理是基于动态代理技术利用Java的反射机制在不改变原有代码的情况下对指定方法进行增强。它提供了一种使程序横向通用化的能力比如事务管理、日志记录等功能。在Spring AOP中切面是应用横向关注点的一种特殊对象而横向关注点是指跨越应用程序多个接口的功能或行为比如安全事务日志等。
执行流程如下
首先程序通过配置文件或注解定义好需要被增强的方法以及增强的方式。Spring AOP提供了五个增强类型分别是前置增强Before Advice后置增强After Advice环绕增强Around Advice异常抛出增强After-Throwing Advice和最终增强After-Finally Advice
然后Spring框架在程序运行期间根据这些定义动态生成一个代理对象。
当程序调用被增强的方法时代理对象会先调用相应的增强方法然后再执行被增强的方法。
在增强方法中可以进行一些额外的处理例如记录日志、验证权限、性能统计等。
最后程序返回执行结果。 总结
我们一开始简单的初步认识了下代理模式其中常见的实现方式有静态代理与动态代理同时写了打印机️的调用时机代码。接着我们思考代理模式和我们使用spring aop有什么联系简单的了解aop 与spring aop的实现方式。 最后我们比较了jdk 与cglib 的代理模式 接着简单过一遍Spring aop 的原理与执行流程。