相信只要使用过Spring框架的,大家对于AOP都不陌生,尤其提起它就能立刻随口说出,一般用在日志处理、异常处理、权限验证等方面。今天通过例子了解了Spring Aop的配置使用(注意不是AspectJ的配置使用)及通过源码了解了Spring Aop大致的实现过程和原理。
一、相关概念
系统在运行之前,AOP的功能模块需要织入到OOP的功能模块中。要进行这种织入过程,需要知道在系统的哪些功能点上进行织入操作,这些将要在其上进行织入操作的系统功能点就称为JointPoint。如某方法调用的时候或者处理异常的时候,在Spring AOP中,一个连接点总是表示一个方法的执行。常见的几种类型的JoinPoint:
Ø 方法调用:当某个方法被调用的时候所处的程序执行点;
Ø 方法执行:该类型表示的是某个方法内部执行开始时的点,应该与方法调用相区分;
Ø 构造方法调用:程序执行过程中对某个对象调用其构造方法进行初始化时的点;
Ø 构造方法执行:它与构造方法调用关系如同方法调用与方法执行间的关系;
Ø 字段设置:对象的某个属性通过setter方法被设置或直接被设置的执行点;
Ø 字段获取:某个对象相应属性被访问的执行点;
Ø 异常处理执行:某些类型异常抛出后,对应的异常处理逻辑执行点;
Ø 类初始化:类中某些静态类型或静态块的初始化时的执行点。
Pointcut代表的是JoinPoint的表述方式,简单容易理解的说就是JoinPoint的集合。在将横切逻辑织入当前系统的过程中,虽然知道需要在哪些功能点上织入AOP的功能模块,但需要一种表达方法。Pointcut和一个切入点表达式关联,并在满足这个切入点的Joinpoint上运行。目前通常使用的Pointcut方式有以下几种:
Ø 直接指定Joinpoint所在的方法名称;
Ø 正则表达式,Spring的AOP支持该种方式;
Ø 使用特定的Pointcut表述语言,Spring 2.0后支持该方式。
Advice是单一横切关注点逻辑的载体,它代表将会织入到JoinPoint的横切逻辑。在切面的某个特定的连接点上执行的逻辑。根据它在Joinpoint位置执行时机的差异或完成功能的不同,可分为以下几种形式:
Ø Before Advice:在Joinpoint指定位置之前执行的Advice类型,可以采用它来做一些系统的初始化工作,如设置系统初始值,获取必要系统资源等。
Ø After Advice:在相应连接点之后执行的Advice类型,它还可以细分为以下三种:
² After Returning Advice:只有当前Joinpoint处执行流程正常完成后,它才会执行;
² After throwing Advice:在当前Joinpoint执行过程中抛出异常的情况下会执行;
² After Advice:该类型的Advice不管JoinPoint处执行流程是正常还是抛出异常都会执行。
Ø Around Advice:对附加其上的Joinpoint进行包裹,可以在joinpoint之前和之后都指定相应的逻辑,甚至中断或忽略joinpoint处原来程序流程的执行。
它是对系统中横切关注点逻辑进行模块化封装的AOP概念实体,它可以包含多个Pointcut以及相关的Advice定义。
经过织入过程后,以Aspect模块化的横切关注点才会集成到oop的现存系统中,而完成织入过程实体称为织入器。Spring中使用一组类来完成最终的织入操作,ProxyFactory类是Spring AOP最通用的织入器。
符合Pointcut所指定的条件,将在织入过程中被织入横切逻辑的对象,称之为目标对象。
单看上述的概念,可能会觉得有点眼花缭乱,其实通过一个简单的AOP的实例即可以帮助我们很快的了解其内部的机制。其实对于方法拦截有不同的实现方式,常用的即有直接采用Spring提供的各种Advice进行拦截,另一种则是采用MethodInterceptor方式进行拦截。
二、配置使用示例
Spring提供的Advice拦截方式
定义一个逻辑接口IBusinessLogic:
其中有一个BusinessLogicException异常,它用于后面对于ThrowsAdvice进行检验的实例,在此定义为:
通过上述的配置,我们可以看出我们将IBusinessLogic做为代理接口,同时它的真正的目标类是BusinesssLogic。同时会对所有进入方法之前采用TracingBeforeAdvice进行拦截,进行方法前的预处理;对time方法采用TracingAfterAdvice进行拦截,进行方法返回后的处理;对于bar则采用TracingThrowsAdvice进行拦截,当方法返回BusinessLogicException时进行相应的处理。
在配置完上述类的依赖关系及需要拦截的方法后,即可以编写客户端程序来调用,查看它的运行机制。客户端调用代码:
其实通过1、3、7行可以非常清晰的了解到,每个方法在执行前都被TracingBeforeAdvice拦截到,并执行了预处理。5、6行表示当调用的是bar方法时,会被TracingThrowsAdvice拦截,当有异常抛出时,会执行相应的处理;8、9、10行则表示当调用的是time方法,返回时会被TracingAfterAdvice拦截,对其返回值进行处理。
MethodInterceptor拦截方式
采用该种方式进行拦截,需要实现一个继承自MethodInterceptor的类,并将该类注册至spring Context中,具体如下:
对于类的装配,其实和上面的非常类似,示例:
再通过客户端进行调用,可得到运行结果,从结果来分析可以看出它在方法执行的前、后均添加了相应的日志。
至此,采用两种不同方式实现的AOP就结束了,接下去以第一种配置方式讲一下Spring Aop大概的实现原理和过程。
三、Spring Aop实现过程和原理
通过上面的示例我们可以看到通过ProxyFactoryBean产生了目标对象Target(在示例中为BusinessLogic)的代理对象,我们执行该代理对象的foo方法时,事实上方法体中并没有什么具体的逻辑,而是直接执行与其关联的InvocationHandler的invoke方法,在Spring Aop中就是执行JdkDynamicAopProxy的invoke方法(或者Cglib2AopProxy的intercept方法),在JdkDynamicAopProxy的invoke方法中如果没有拦截器的设定,那么直接调用目标对象的相应方法,反之有拦截器会调用ReflectiveMethodInvocation的proceed方法,在该方法中会遍历所有的拦截器,如果当前方法和拦截器中的PoinCut相匹配(Advisor对象里包含Advice和PoinCut对象,而拦截器对象又是由Advisor对象适配过来的),那么则调用相应拦截器的invoke方法,再在invoke方法中调用相应的Advice的方法,如before,afterReturning等,所有拦截器调用完毕之后,则调用被代理对象的方法。
现在还有两个问题代理对象是怎么创建的?拦截器又是从哪里来的?
第一个问题还是得从ProxyFactoryBean说起,IOC容易或者程序会调用该类的getObject方法,那么ProxyFactoryBean会先调用initializeAdvisorChain方法来从配置文件中读取相关的Advisor,接着调用自己的getSingletonInstance方法,在这个方法中会去调用ProxyCreatorSupport(从名字可以看出这是创建代理对象的辅助类),然后会去调用AopProxyFactory这种代理对象工厂的ctreateAopProxy方法去产生代理对象,比如DefaultAopProxyFactory的ctreateAopProxy方法,在这个方法中会去判断目标对象是不是接口的实现类,如果是的话就创建一个AopProxy的子类JdkDynamicAopProxy实例,否则通过Cglib2AopProxy的createCglibProxy方法创建一个AopProxy的子类Cglib2AopProxy实例,之后就可以通过AopProxy的实现类中的getProxy方法返回代理对象了。
第二个问题,拦截器链是在JdkDynamicAopProxy的invoke方法中去获得的,通过AdvisedSupport对象的getInterceptorsAndDynamicInterceptionAdvice方法去获得的,AdvisedSupport这个辅助类从缓存中去获得拦截器链,如果缓存中没有,那么利用DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法去获得。
这就是自己看了Spring Aop相关源码后的理解,如有不对的地方希望指出。
QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE");db.setDatabaseName("mydb");bool ok=db.open();qDebug()QSqlQuery qure(db);/*qure.exec("create table mytab4(id int PRIMARY KEY ,name text)")
1.思科模拟器灯是红色的,实验失败?按老师指导完成实验,都配对了但是还是不行,为什么呢?一看灯是红色的,为啥?原来是端口未打开,在配置交换机路由器常常会遇到这种问题,华三要进入端口undo shutdown,思科则是no shutdown.这是实验前的样子,可以看到灯是红色的,配置很简单,我们只要进入交换机配置界面。输入:enableconfigure terminal...
首先谈谈何为子网掩码:子网掩码——屏蔽一个IP地址的网络部分的“全1”比特模式。对于A类地址来说,默认的子网掩码是255.0.0.0;对于B类地址来说默认的子网掩码是255.255.0.0;对于C类地址来说默认的子网掩码是255.255.255.0。利用子网掩码可以把大的网络划分成子网,即VLSM(可变长子网掩码),也可以把小的网络归并成大的网络即超网。子网掩码的设定必须遵循一定的规则。与二进制I...
Python 之关键字和实例0.0682018.04.09 20:10:28字数 1073阅读 2671一、python关键字Screen Shot 2018-04-09 at 19.50.17.png\1. and :表示逻辑判断 【与】a = '1'b = 1if a and b: print('Hello Python')2.as :单独没有实际意思,常与...
这是第249篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员)本期目录:Unity内置资源如何打包避免冗余 SpriteAtlas的“冗余”问题 关于Mesh占用内存的问题 UGUI.Rendering.UpdateBatches耗时较高 Plugins的DLL是如何影响Package的AssetBundleQ:
Netty中的责任链设计模式 - 责任链模式责任链模式(Chain of Responsibility Pattern)是一种是行为型设计模式,它为请求创建了一个处理对象的链。其链中每一个节点都看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。责任链模...
Erlang shell中,用句号加空格、tab或回车来结束表达式,%表示注释的起点,;隔离子句。模块是.erl 文件,库的头文件.hrl, shell中的编译时c(),外编译命令时erlc, 退出shell用q(),或erlang:halt(). 变量以大写字母开头,且不能重新绑定变量,只能一次性赋值,具有不可变状态......
一、持续集成及Jenkins的介绍1、软件开发的生命周期五个阶段:需求分析——需求设计——项目开发——测试——部署2、软件开发瀑布模型3、软件的敏捷开什么是敏捷开发?敏捷开发的核心是迭代开发与增量开发何为迭代开发?对于大型软件项目,传统的开发方式是采用一个大周期进行开发,整个过程就是以此“大开发”;迭代开发的方式则不一样,它将开发过程拆分成多个小周期,即以此“大开发”编程多个“小开发”,每次小开发都是同样的流程,所以看上去就好像重复在做同样的步骤。举例:某公司想造一个大推力火箭,
这个方法简单粗暴,比昨天的安卓用户秒刷QQ运动步数教程+软件下载更简单。亲测百分百成功,根据教程图片的步骤找到文件进行编辑保存即可。 1、自行百度搜索“安卓SQLite汉化版”下载,然后进入修改便可 2、修改编辑后保存 成功截图
1 s => s.XianWID.StartsWith(str)2 匹配以str开头的3 s => s.XianWID.EndsWith(str)4 匹配以str结尾的5 s => s.XianWID.Contains(str)6 匹配包含str的转载于:https://www.cnblogs.com/iack/p/3506442.html...
用公式π/4=1-1/3+1/5-1/7...求π的近似值,直到发现某一项的绝对值小于10^6为止(该项不累加)解:程序:#include#includeint main(){int sign = 1;double pi = 0.0, n = 1.0, term = 1.0;//term表示当前项while (fabs(term) >= 1e-6){pi += term;n += 2;sig...
“本人向各位股东提呈本集团截至×年×月×日之年度报告,并谨此代表董事会向各位股东对中兴通讯的关心和支持表示诚挚的谢意。”在过去的近20年里,这句每年都会出现在中兴董事长报告书的开场白一直专属于一人,他的名字叫侯为贵。他是中兴的创始元老之一,执掌中兴长达30年。如今,这样的专属却变了。4月7日,中兴发布的2015年业绩报告显示,董事长报告书的落款签名已...