【学校类型论坛源码】【搜索源码软件下载】【短线伴侣指标源码】aspect实现原理源码解析

时间:2024-11-06 14:31:03 编辑:期货资金方向源码 来源:开源家政源码

1.spring aop代理对象创建以及调用invoke源码
2.aspect切面是实现什么
3.springboot异常处理机制?
4.增强Spring改造@CacheEvict,支持缓存批量模糊删除!原理源码
5.简述SpringAOP的解析实现原理(列举spring实现aop的几种方式)
6.76 张图,剖析 Spring AOP 源码,实现小白居然也能看懂,原理源码大神,解析学校类型论坛源码请收下我的实现膝盖!

aspect实现原理源码解析

spring aop代理对象创建以及调用invoke源码

       深入解析Spring AOP代理对象创建及调用invoke源码

       一、原理源码代理对象创建与invoke源码概览

       1.1 代理对象创建源码概览

       Spring AOP代理对象的解析创建时机主要在实例化后或初始化后。具体流程涉及BeanPostProcessor.postProcessAfterInitialization()方法。实现正常情况下,原理源码代理对象创建与单例池内的解析代理对象一致,确保方法调用实际指向代理对象。实现

       1.2 invoke执行目标方法源码概览

       目标对象方法调用后,原理源码因为代理对象存储于单例池,解析实际调用的是代理对象的增强方法。这种方式实现了方法调用的动态代理。

       1.3 exposeProxy = true使用说明

       1.3.1 不使用(exposeProxy = true)

       不使用配置时,目标方法内部调用被拦截增强的方法,不会再次触发AOP。

       1.3.2 使用(exposeProxy = true)

       启用此配置后,执行目标方法时,AOP增强将再次激活,从而触发重复执行。

       1.3.3 cglib与JDK代理区别

       cglib通过继承实现代理,方法调用指向代理对象,因此内嵌方法会重复调用增强逻辑;

       JDK代理通过反射,方法调用直接指向目标对象,内嵌方法不会重复调用。

       关于Spring中cglib不会重复调用的解释:测试表明,使用Spring5.版本,强制使用cglib配置时,案例中方法调用与代理对象方法调用之间并无重复,原因是Spring调用的是目标方法而非代理对象的方法。

       二、代理对象创建及invoke源码分析图

       代理创建流程始于@EnableAspectJAutoProxy注解注册的AspectJAutoProxyRegistrar,此注册器在解析import注解时执行registerBeanDefinitions方法。该方法注册了在bean实例化前调用的InstantiationAwareBeanPostProcessor类型的bean后置处理器,此处理器在实例化前解析AOP,非循环依赖在初始化后调用postProcessAfterInitialization()创建动态代理。

       匹配Advisor集合:首先筛选Advisor列表,匹配规则涉及类级别和方法级别的筛选,通过Aspect匹配实现。同时,Advisor排序确保调用顺序遵循通知类型。创建代理对象遵循ProxyTargetClass参数与目标类接口的配置,选择JDK或cglib动态代理。

       代理方法调用:由于存储的是代理对象,方法调用实际指向代理。exposeProxy = true配置下,搜索源码软件下载代理对象暴露到线程变量中。代理对象执行方法调用遵循责任链模式,按顺序执行前置、目标方法、后置等通知。

aspect切面是什么

       aspect切面是面向切面编程,是一种思想,也是一套规则,像用户验证之类的都是可以这么理解,如果项目中很多地方需要验证用户是否登录,那么进行统一设置就可以不需要单独写代码。

       可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。

springboot异常处理机制?

       springboot的四种拦截机制

       当我们在某些情况下需要对客户端发送来的请求进行拦截分析的时候,就需要用到拦截机制,比如,我们需要对一个请求进行计时,又或者需要知道当前请求需要进入哪个控制器,哪一个方法,该请求的参数是什么等等场景下都需要用到拦截机制来处理。下面,我们来讲解一下SpringBoot的几种拦截方式以及如何使用它们来处理一定的场景需求。

       过滤器(filter)

       拦截器(interceptor)

       全局异常处理器(ControllerAdvice)

       切片(aspect)

       如上图所示,当一个请求发送来的时候,filter在最外层,也最先拦截到请求,接下来就是interceptor,依次是ControllerAdvice(处理controller层异常)、aspect,最后才进入controller层去处理请求。相应的,当controller内部发生错误,抛出异常的时候,aspect最先接收到该异常,如果不对抛出的异常继续处理继续往外抛的话依次会抛到ControllerAdvice、interceptor、filter。

       请求的顺序:从先到后经过FilterInterceptorControllerAdviceAspect–Controller。

       过滤器(filter)可以拦截发送请求的状态码以及信息,拦截器(interceptor)除了可以拦截filter可以拦截的,还可以得到当前请求进入了哪一个controller,以及映射到哪一个方法,切片(aspect),它具有上面的所有功能外,还可以得到当前请求的参数的值。全局异常处理器(ControllerAdvice)只是用于处理controller层抛出的异常

       Springboot的Filter,HandlerInterceptor,Aspect与异常处理

       不知你在Springboot应用开发中有没有遇到过这样的情况,mon的项目,要暴露出去给依赖的短线伴侣指标源码项目使用,在文件src\main\resources\META-INF\spring.factories中添加最后一行

       可以被全局异常捕捉并处理成json

       访问接口,如果无数据,则输出异常信息

       { "data":"packageid为:BZ-的indexpackage无记录","flag":false,"code":null,"msg":"未查到数据"}

       全局异常类可以用@RestControllerAdvice,替代@ControllerAdvice,因为这里返回的主要是json格式,这样可以少写一个@ResponseBody。

springboot怎么取消同意的异常处理

       使用ErrorController类来实现。

       系统默认的错误处理类为BasicErrorController,将会显示如上的错误页面。

       这里编写一个自己的错误处理类,上面默认的处理类将不会起作用。

       getErrorPath()返回的路径服务器将会重定向到该路径对应的处理类,本例中为error方法。

增强Spring改造@CacheEvict,支持缓存批量模糊删除!

       在开发中,遇到一个需要批量模糊删除特定租户缓存的需求。常规的使用方式包括使用@CacheEvict注解清除指定的缓存key或全部缓存。然而,当需要按照租户的唯一TelnetID进行缓存分离时,常规的allEntries = true清空namespace下的所有元素方式已不再适用。因为这种操作将导致所有缓存数据清空,不符合需求。

       为了实现批量模糊删除,需要深入理解@CacheEvict注解的实现原理。通常,@CacheEvict是通过AOP(面向切面编程)实现的,核心类是CacheAspectSupport,其内部方法负责缓存的清理。通过对CacheAspectSupport类的源码分析,我们可以发现缓存清理的逻辑主要在processCacheEvicts方法中,该方法通过调用performCacheEvict方法进行实际的缓存清理。

       在processCacheEvicts方法中,performCacheEvict方法负责调用RedisCache类的evict和clear方法进行缓存清理。当allEntries为true时,调用的是clear方法,该方法以namespace:: *作为key规则进行模糊删除。这一发现为实现批量模糊删除提供了可能,我们可以通过修改evict方法,在namespace *中插入特定的TelnetID,从而实现目标。

       为了实现这一需求,需要重写RedisCache类中的evict方法,并集成RedisCache。同时,为了确保定制的RedisCacheResolver生效,需要将其注入到RedisCacheManager中。这涉及定义一个自定义的RedisCacheManagerResolver类,集成RedisCacheManagerResolver实现。

       在完成RedisCacheResolver的Linuxfree命令源码详解实现后,只需要在RedisConfig中管理自定义的RedisCacheManagerResolver。至此,批量模糊删除特定租户缓存的需求已得到解决。

       通过以上步骤,不仅实现了使用自定义注解进行模糊删除缓存,还深入理解了Spring Cache的实现机制,进一步优化了缓存管理流程。这一技术进阶策略不仅适用于当前问题的解决,也为未来的缓存优化提供了参考。

简述SpringAOP的实现原理(列举spring实现aop的几种方式)

       java编程,spring里ioc和aop用什么原理实现的

       1、spring中ioc使用的是“生产工厂”,aop使用的是“动态代理”想知道更多建议去看一下马士兵的系列视频教程。

       2、ioc:java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,在使用其他的合作对象时,均要使用像newobject()这样的语法来完成合作对象的申请工作。

       3、IOC:控制反转也叫依赖注入。利用了工厂模式\x0d\将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。

       4、aop和ioc原理是:AOP,面向切面(方便)编程,可以为某一类对象进行监督和控制,也就是在调用这类对象的具体方法的前后去调用你指定的功能模块,从而达到对一个模块扩充的功能,可以通过注解或者XML文档进行实现。

       5、ioc原理:在传统的实现中,由程序内部代码来控制组件之间的关系。需要使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。

aop原理是什么?

       aop:利用一种称为“横切”的技术,解剖封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,这样就能减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

       aop原理:AOP将业务逻辑组件和切面类都加入到容器中,负责在业务逻辑运行的时候将日志进行打印,切面类负责动态感知MathCalculator.div运行到哪里然后执行。通过@Aspect通知注解给切面类的目标方法标注何时何地运行。

       原理:该接口是对象和它的代理共用的接口;RealSubject:真实主题角色,是实现抽象主题接口的类;Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。Bc源码网下载

       原理:spingaop是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

       AOP的主要原理:动态代理Spring工作原理Spring已经用过一段时间了,感觉Spring是个很不错的框架。

       我们开始研究一下如何实现一下相关的AOP容器代理机制的。实现的基本实现原理就是后置处理器:BeanPostProcessor机制,实现动态化植入机制。bean在初始化的时候会进行调用对应的BeanPostProcessor的对应的方法会进行织入。

       aop的实现原理

       AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

       原理:该接口是对象和它的代理共用的接口;RealSubject:真实主题角色,是实现抽象主题接口的类;Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。

       我们开始研究一下如何实现一下相关的AOP容器代理机制的。实现的基本实现原理就是后置处理器:BeanPostProcessor机制,实现动态化植入机制。bean在初始化的时候会进行调用对应的BeanPostProcessor的对应的方法会进行织入。

springaop原理

       1、AOP的主要原理:动态代理。Spring工作原理Spring已经用过一段时间了,感觉Spring是个很不错的框架。

       2、实现的基本实现原理就是后置处理器:BeanPostProcessor机制,实现动态化植入机制。bean在初始化的时候会进行调用对应的BeanPostProcessor的对应的方法会进行织入。

       3、简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。

       4、spring中ioc使用的是“生产工厂”,aop使用的是“动态代理”想知道更多建议去看一下马士兵的系列视频教程。

       5、IOC的注入类型有几种?主要可以划分为三种:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入面向切面(AOP)(面向切面编程,AOP其实只是OOP的补充而已,AOP基本上是通过代理机制实现的。

       6、spring原理spring的最大作用ioc/di,将类与类的依赖关系写在配置文件中,程序在运行时根据配置文件动态加载依赖的类,降低的类与类之间的藕合度。

aop原理和机制

       1、我们开始研究一下如何实现一下相关的AOP容器代理机制的。实现的基本实现原理就是后置处理器:BeanPostProcessor机制,实现动态化植入机制。bean在初始化的时候会进行调用对应的BeanPostProcessor的对应的方法会进行织入。

       2、面向切面(AOP)(面向切面编程,AOP其实只是OOP的补充而已,AOP基本上是通过代理机制实现的。)我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。

       3、aop原理:AOP将业务逻辑组件和切面类都加入到容器中,负责在业务逻辑运行的时候将日志进行打印,切面类负责动态感知MathCalculator.div运行到哪里然后执行。通过@Aspect通知注解给切面类的目标方法标注何时何地运行。

       4、原理:spingaop是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

       5、其次就是我们最长使用的,将记录这个方法抽离出来,其他的增删改调用这个记录函数即可,显然代码重复度降低,但是这样的调用还是没有降低耦合性。

       6、AOP:面向切面编程,它主要关注的是程序的执行过程。具体解析:在java方法调用时,AOP机制能自动进行方法拦截,允许在方法调用之前,调用后,以及执行异常时添加特点的代码来完成需要的功能。

张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!

       本文将简要介绍AOP(面向切面编程)的基础知识与使用方法,并深入剖析Spring AOP源码。首先,我们需要理解AOP的基本概念。

       1. **基础知识

**

       1.1 **什么是AOP?

**

       AOP全称为Aspect Oriented Programming,即面向切面编程。AOP的思想中,周边功能(如性能统计、日志记录、事务管理等)被定义为切面,核心功能与切面功能独立开发,然后将两者“编织”在一起,这就是AOP的核心。

       AOP能够将与业务无关、却为业务模块共同调用的逻辑封装,减少系统重复代码,降低模块间的耦合度,有利于系统的可扩展性和可维护性。

       1.2 **AOP基础概念

**

       解释较为官方,以下用“方言”解释:AOP包括五种通知分类。

       1.3 **AOP简单示例

**

       创建`Louzai`类,添加`LouzaiAspect`切面,并在`applicationContext.xml`中配置。程序入口处添加`"睡觉"`方法并添加前置和后置通知。接下来,我们将探讨Spring内部如何实现这一过程。

       1.4 **Spring AOP工作流程

**

       为了便于理解后面的源码,我们将整体介绍源码执行流程。整个Spring AOP源码分为三块,结合示例进行讲解。

       第一块是前置处理,创建`Louzai`Bean前,遍历所有切面信息并存储在缓存中。第二块是后置处理,创建`Louzai`Bean时,主要处理两件事。第三块是执行切面,通过“责任链+递归”执行切面。

       2. **源码解读

**

       注意:Spring版本为5.2..RELEASE,否则代码可能不同!这里,我们将从原理部分开始,逐步深入源码。

       2.1 **代码入口

**

       从`getBean()`函数开始,进入创建Bean的逻辑。

       2.2 **前置处理

**

       主要任务是遍历切面信息并存储。

       这是重点!请务必注意!获取切面信息流程结束,后续操作都从缓存`advisorsCache`获取。

       2.2.1 **判断是否为切面

**

       执行逻辑为:判断是否包含切面信息。

       2.2.2 **获取切面列表

**

       进入`getAdvice()`,生成切面信息。

       2.3 **后置处理

**

       主要从缓存拿切面,与`Louzai`方法匹配,创建AOP代理对象。

       进入`doCreateBean()`,执行后续逻辑。

       2.3.1 **获取切面

**

       首先,查看如何获取`Louzai`的切面列表。

       进入`buildAspectJAdvisors()`,方法用于存储切面信息至缓存`advisorsCache`。随后回到`findEligibleAdvisors()`,从缓存获取所有切面信息。

       2.3.2 **创建代理对象

**

       有了`Louzai`的切面列表,开始创建AOP代理对象。

       这是重点!请仔细阅读!这里有两种创建AOP代理对象方式,我们选择使用Cglib。

       2.4 **切面执行

**

       通过“责任链+递归”执行切面与方法。

       这部分逻辑非常复杂!接下来是“执行切面”最核心的逻辑,简述设计思路。

       2.4.1 **第一次递归

**

       数组第一个对象执行`invoke()`,参数为`CglibMethodInvocation`。

       执行完毕后,继续执行`CglibMethodInvocation`的`process()`。

       2.4.2 **第二次递归

**

       数组第二个对象执行`invoke()`。

       2.4.3 **第三次递归

**

       数组第三个对象执行`invoke()`。

       执行完毕,退出递归,查看`invokeJoinpoint()`执行逻辑,即执行主方法。回到第三次递归入口,继续执行后续切面。

       切面执行逻辑已演示,直接查看执行方法。

       流程结束时,依次退出递归。

       2.4.4 **设计思路

**

       这部分代码研究了大半天,因为这里不是纯粹的责任链模式。

       纯粹的责任链模式中,对象内部有一个自身的`next`对象,执行当前对象方法后,启动`next`对象执行,直至最后一个`next`对象执行完毕,或中途因条件中断执行,责任链退出。

       这里`CglibMethodInvocation`对象内部无`next`对象,通过`interceptorsAndDynamicMethodMatchers`数组控制执行顺序,依次执行数组中的对象,直至最后一个对象执行完毕,责任链退出。

       这属于责任链,实现方式不同,后续会详细剖析。下面讨论类之间的关系。

       主对象为`CglibMethodInvocation`,继承于`ReflectiveMethodInvocation`,`process()`的核心逻辑在`ReflectiveMethodInvocation`中。

       `ReflectiveMethodInvocation`的`process()`控制整个责任链的执行。

       `ReflectiveMethodInvocation`的`process()`方法中,包含一个长度为3的数组`interceptorsAndDynamicMethodMatchers`,存储了3个对象,分别为`ExposeInvocationInterceptor`、`MethodBeforeAdviceInterceptor`、`AfterReturningAdviceInterceptor`。

       注意!这3个对象都继承了`MethodInterceptor`接口。

       每次`invoke()`调用时,都会执行`CglibMethodInvocation`的`process()`。

       是否有些困惑?别着急,我将再次帮你梳理。

       对象与方法的关系:

       可能有同学疑惑,`invoke()`的参数为`MethodInvocation`,没错!但`CglibMethodInvocation`也继承了`MethodInvocation`,可自行查看。

       执行逻辑:

       设计巧妙之处在于,纯粹的责任链模式中,`next`对象需要保证类型一致。但这里3个对象内部没有`next`成员,不能直接使用责任链模式。怎么办呢?就单独设计了`CglibMethodInvocation.process()`,通过无限递归`process()`实现责任链逻辑。

       这就是我们为什么要研究源码,学习优秀的设计思路!

       3. **总结

**

       本文首先介绍了AOP的基本概念与原理,通过示例展示了AOP的应用。之后深入剖析了Spring AOP源码,分为三部分。

       本文是Spring源码解析的第三篇,感觉是难度较大的一篇。图解代码花费了6个小时,整个过程都沉浸在代码的解析中。

       难度不在于抠图,而是“切面执行”的设计思路,即使流程能走通,将设计思想总结并清晰表达给读者,需要极大的耐心与理解能力。

       今天的源码解析到此结束,有关Spring源码的学习,大家还想了解哪些内容,欢迎留言给楼仔。

ood/oop_ood和oop思想_什么是ooa ood oop

       面向对象编程(OOP)的核心在于封装、继承和多态,通过抽象实体及其属性和行为,形成清晰高效的逻辑单元。

       与之相比,面向方面编程(AOP)聚焦于业务处理过程中的“切面”,即处理过程中的某个步骤或阶段,以实现逻辑过程各部分间的低耦合性。通过AOP,可以在不修改源代码的情况下,给程序动态添加功能,实现灵活性与可扩展性。

       AOP的名称容易产生误导,实际上,Aspect(方面)指的是逻辑过程的外在特性在不同观察角度下的体现,而非传统意义上的“方面”。因此,更准确的译法是“面向切面编程”。与OOP相比,AOP关注点在于处理过程中的特定环节,而OOP则侧重于实体及其属性和行为的抽象。

       在实际应用中,AOP和OOP可以互补。例如,对于“雇员”实体的封装,OOP能有效实现;而对于“权限检查”这一动作,AOP能提供更高效的支持。OOP通过接口实现功能,但修改接口可能引发连锁问题;AOP则通过修改Aspect,实现动态功能添加,维护系统稳定性。

       应用AOP的场景,如实现并发访问共享数据,可以借助数据对象(Data Class)和访问类。通过引入锁(Lock)机制,确保同一时刻只有一个访问类能访问数据对象。由于Java的单继承限制,具体访问类难以同时继承数据对象和其它父类,这时AOP能提供解决方案,通过Aspect实现动态功能添加,灵活管理访问逻辑。

AOP的名称含义

       Aspect Oriented Programming(AOP)是较为热门的一个话题。AOP,国内大致译作“面向切面编程”。

       â€œé¢å‘方面编程”,这样的名字并不是非常容易理解,且容易产生一些误导。笔者不止一次听到类似“OOP/OOD即将落伍,AOP是新一代软件开发方式”这样的发言。显然,发言者并没有理解AOP的含义。Aspect,没错,的确是“方面”的意思。不过,华语传统语义中的“方面”,大多数情况下指的是一件事情的不同维度、或者说不同角度上的特性,比如我们常说:“这件事情要从几个方面来看待”,往往意思是:需要从不同的角度来看待同一个事物。这里的“方面”,指的是事物的外在特性在不同观察角度下的体现。而在AOP中,Aspect的含义,可能更多的理解为“切面”比较合适。所以笔者更倾向于“面向切面编程”的译法。

       å¯ä»¥é€šè¿‡é¢„编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。

       åœ¨Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。 AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

       è€ŒAOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

       ä¸Šé¢çš„陈述可能过于理论化,举个简单的例子,对于“雇员”这样一个业务实体进行封装,自然是OOP/OOD的任务,我们可以为其建立一个“Employee”类,并将“雇员”相关的属性和行为封装其中。而用AOP设计思想对“雇员”进行封装将无从谈起。

       åŒæ ·ï¼Œå¯¹äºŽâ€œæƒé™æ£€æŸ¥â€è¿™ä¸€åŠ¨ä½œç‰‡æ–­è¿›è¡Œåˆ’分,则是AOP的目标领域。而通过OOD/OOP对一个动作进行封装,则有点不伦不类。

       æ¢è€Œè¨€ä¹‹ï¼ŒOOD/OOP面向名词领域,AOP面向动词领域。 很多人在初次接触 AOP 的时候可能会说,AOP 能做到的,一个定义良好的 OOP 的接口也一样能够做到,我想这个观点是值得商榷的。AOP和定义良好的 OOP 的接口可以说都是用来解决并且实现需求中的横切问题的方法。但是对于 OOP 中的接口来说,它仍然需要我们在相应的模块中去调用该接口中相关的方法,这是 OOP 所无法避免的,并且一旦接口不得不进行修改的时候,所有事情会变得一团糟;AOP 则不会这样,你只需要修改相应的 Aspect,再重新编织(weave)即可。 当然,AOP 也绝对不会代替 OOP。核心的需求仍然会由 OOP 来加以实现,而 AOP 将会和 OOP 整合起来,以此之长,补彼之短。

搜索关键词:海报制作网站源码