浅析Spring AOP
在正常的业务流程中,往往存在着一些业务逻辑,例如安全审计、日志管理,它们存在于每一个业务中,然而却和实际的业务逻辑没有太强的关联关系。
        
图1
这些逻辑我们称为横切逻辑。如果把横切的逻辑代码写在业务代码中,散落在各个地方,则会变得非常难以维护,代码也会显得过于臃肿。
Spring AOP为处理这些问题提供了一种很好的方法。
1 AOP术语
1 通知(advice)。通知是指真正执行的目标逻辑。例如为系统记日志,那么通知做的事情,就是记录日志。
2 连接点(joinpoit)。连接点指何时执行advice的代码。系统中如果想要为所有的start()方法执行之前,记录一条"our system is starting"的日志,那么,start()方法被调用的时机,称为一个连接点。
3 切点(pointcut)。切点指何处执行advice的代码。例如指定特定的一个路径下的start()方法被调用时,执行advice的代码,那么,这个特定的路径可以理解为切点。通常我们使用正则表达式定义匹配的类和方法来确定切点。
4 切面(aspect)。 切面是通知、切点和连接点的全部内容。它定义了何时在何处,完成什么功能。
5 织入(weaving)。织入指实际代码执行到切点时,完成对目标对象代理的过程。织入有如下三种方式:
a.编译器织入。需要特殊编译器支持,例如AspectJ的织入编译器,在编译期间将代码织入。
b.类加载时。目标类在载入JVM时将切面引入。需要特殊的classLoader支持,改变目标类的字节码,增加新的动作。
c.运行期。在运行期运用动态代理技术,代理目标类。Spring AOP就是使用这种方式。
2 Spring对AOP的支持
Spring对AOP提供了多种支持,大体上分为2种思路。
1 使用动态代理,在运行期对目标类生成代理对象。代理类封装了目标类,并拦截被通知的方法的调用,再将调用转发给真正的目标类方法调用。在代理类中,可以执行切面逻辑。基于这种模式,仅限于对方法的拦截。如果想要实现更细粒度的拦截,例如特定的字段的修改,则需要使用第2种思路了。
2 使用AspectJ特有的AOP语言。这种方式的成本在于,需要学习AspectJ的语法。
大多数情况下,第一种思路已经可以满足我们的需求。
3 Demo
我们可以使用execution指示器来指示切点。如图2
  
图2
execution指示器具体指定了哪个包下面哪个类执行什么方法时,会被织入横切逻辑。Spring AOP的demo网上有很多,但是原理其实都一样。这里贴出其中一种。
我们需要2个类,一个类(Biz)用作正常的业务逻辑,一个类用作横切(AdviceTest)。同时写一个类Main测试
1.Biz
 public class Biz {
     public void doBiz(){
         System.out.println("doing biz");
     }
 }
2.AdviceTest
 public class AdviceTest implements Advice, MethodInterceptor {
     @Override
     public Object invoke(MethodInvocation invocation) throws Throwable {
         advice();
         invocation.proceed();
         return null;
     }
     public void advice(){
         System.out.println("this is a advice");
     }
 }
3.Main
 public class Main {
     public static void main(String[] args) {
         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"config/spring/spring-aop.xml"});
         Biz biz = (Biz)applicationContext.getBean("biz");
         biz.doBiz();
     }
 }
4.Spring配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
"> <bean id="adviceTest" class="aop.AdviceTest"/> <bean id="biz" class="aop.Biz"/> <aop:config>
<aop:pointcut id="bizPointCut" expression="execution(* aop.Biz.*(..))"/>
<aop:advisor pointcut-ref="bizPointCut" advice-ref="adviceTest"/>
</aop:config> </beans>
执行Main函数代码,输出如下
this is a advice
doing biz
可见,实际的biz逻辑已经经过增强。
4 源码分析
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。在我们的例子中,显然使用的是Cglib。如图3
  
图3
为什么我们配置了Biz的bean,得到的却不是Biz的实例呢?这和Spring对xml文件的标签解析策略有关。对于AOP相关的bean的解析,在AopNamespaceHandler类里面有相关代码,感兴趣的同学可以去研究下。
我们继续往下面单步调试,发现进入了CglibAopProxy类里面。
 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
             Object oldProxy = null;
             boolean setProxyContext = false;
             Class<?> targetClass = null;
             Object target = null;
             try {
                 if (this.advised.exposeProxy) {
                     // Make invocation available if necessary.
                     oldProxy = AopContext.setCurrentProxy(proxy);
                     setProxyContext = true;
                 }
                 // May be null. Get as late as possible to minimize the time we
                 // "own" the target, in case it comes from a pool...
                 target = getTarget();
                 if (target != null) {
                     targetClass = target.getClass();
                 }
                 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                 Object retVal;
                 // Check whether we only have one InvokerInterceptor: that is,
                 // no real advice, but just reflective invocation of the target.
                 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                     // We can skip creating a MethodInvocation: just invoke the target directly.
                     // Note that the final invoker must be an InvokerInterceptor, so we know
                     // it does nothing but a reflective operation on the target, and no hot
                     // swapping or fancy proxying.
                     Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                     retVal = methodProxy.invoke(target, argsToUse);
                 }
                 else {
                     // We need to create a method invocation...
                     retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                 }
                 retVal = processReturnType(proxy, target, method, retVal);
                 return retVal;
             }
             finally {
                 if (target != null) {
                     releaseTarget(target);
                 }
                 if (setProxyContext) {
                     // Restore old proxy.
                     AopContext.setCurrentProxy(oldProxy);
                 }
             }
         }
无耻的把代码直接贴出来。我们重点关注下下面这一行
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
在本文例子的场景下,会在此创建一个CglibMethodInvocation执行上下文,并执行proceed()方法。

图4
最终执行AdviceTest中invoke()方法的调用。在AdviceTest的invoke方法中,我们可以自定义自己想要执行的增强逻辑invocation.proceed()来执行目标类的方法。
  
图5
5 总结
Spring AOP为许多与业务无关的逻辑的执行,提供了一种很好的解决思路。本文也只是抛砖引玉,在实际的Spring源码中,还是比较复杂的,还需要细细研究才行。
参考文献:
《Spring In Action》
《Spring源码深度解析》
浅析Spring AOP的更多相关文章
- 浅析 Spring Aop
		aop也是Spring里比较重要的一个点,最近把源码看了下,这里总结一下 使用上主要就下面的点注意下: 相关的Annotaion Around Before After AfterReturning ... 
- 做一个合格的程序员之浅析Spring AOP源代码(十八) Spring AOP开发大作战源代码解析
		事实上上一篇文章价值非常小,也有反复造轮子的嫌疑,网上AOP的实例非常多,不胜枚举,事实上我要说的并非这个,我想要说的就是上一节中spring的配置文件: 我们这边并没实用到我们上几节分析的哪几个AO ... 
- Spring Boot -- Spring AOP原理及简单实现
		一.AOP基本概念 什么是AOP,AOP英语全名就是Aspect oriented programming,字面意思就是面向切面编程.面向切面的编程是对面向对象编程的补充,面向对象的编程核心模块是类, ... 
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
		原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ... 
- Spring Aop 应用实例与设计浅析
		0.代码概述 代码说明:第一章中的代码为了突出模块化拆分的必要性,所以db采用了真实操作.下面代码中dao层使用了打印日志模拟插入db的方法,方便所有人运行demo. 1.项目代码地址:https:/ ... 
- 02 浅析Spring的AOP(面向切面编程)
		1.关于AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.O ... 
- 深入浅析Spring的AOP实现原理
		转载来源:https://www.jb51.net/article/81788.htm AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Or ... 
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
		原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ... 
- 浅析Spring中AOP的实现原理——动态代理
		一.前言 最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ... 
随机推荐
- 数论 - SGU 107 987654321 problem
			987654321 problem Problem's Link Mean: 略 analyse: 这道题目是道简单题. 不过的确要好好想一下: 通过简单的搜索可以知道,在N<9时答案一定为0, ... 
- linux 测试工具
			最近在寻找linux的测试工具,试用了一些.记录如下. memtester 内存测试工具,通过对内存进行读写进行测试.可以对同一块空间进行多次的读写. 源码分析 http://www.cnblogs. ... 
- [ASK] brew install nginx
			.......... .......... Error: Permission denied - /usr/local/etc/openssl .......... .......... Cannot ... 
- MFC 单选按钮Radio使用注意
			使用MFC Radio时遇到问题:数据交换时出现断言崩溃框 定位于: 解决方法: 1.按CTRL+D,保证同一组内的radio的tab序号是连续的: 2.同一组内,设置 radio1的属性: gro ... 
- Spring_day01--注入对象类型属性(重点)_P名称空间注入_注入复杂类型属性_IOC和DI区别_Spring整合web项目原理
			注入对象类型属性(重点) Action要new一个service对象,Service中又要new一个Dao对象,现在把new的过程交给spring来操作 1 创建service类和dao类 (1)在s ... 
- 【BZOJ2879】[Noi2012]美食节 动态加边网络流
			[BZOJ2879][Noi2012]美食节 Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食 ... 
- quartz任务调度配置 解决jobDetail身份标识存在问题
			applicationContext.xml配置:每五秒轮询一次.注意:周和日期不能同时为通配符”*“ <bean id="planInfoServiceImpl" clas ... 
- 解决Atom的 gpp compiler,编译后在Windows的命令行终端运行,中文乱码
			按下快捷键Win+R,输入regedit打开注册变编辑器,依次找到 HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\,右键新建一个字符串 ... 
- session------>防表单重复提交
			方法一:用js控制表单提交--->但是容易在客户端被篡改代码,还是要加的 方法二:session 先给每一个表带上唯一的标志,再把标志存入session 当session中标志和表上标志都不为空 ... 
- Cookies and Session Tracking Client Identification  cookie与会话跟踪 客户端识别
			w HTTP The Definitive Guide Cookies can be used to track users as they make multiple transactions to ... 
