Spring一个重要的特性就是提供了AOP,使得我们可以在原有的基础上增加我们自己的系统业务逻辑。使得我们系统业务逻辑与应用业务逻辑相分离,耦合性降低,并且大大的提高了开发的效率。Spring的AOP利用的就是动态代理方式,在Spring的AOP中,有两种实现方式。第一种,就是利用JDK的Proxy,另外一种就是采用CGLIB来实现的。

基本概念:

Advice:

通知,制定在连接点做什么,在Sping 中,他主要描述Spring 围绕方法调用注入的额外的行为,具有增强的功能 ,只能应用于所有方法,主要是由aopalliance.jar中包含aop定义中的接口,按这个接口实现aop标准。

PointCut

切点,其决定一个 advice 应该应用于哪个连接点,也就是需要插入额外处理的地方的集合,例如,被某个advice 作为目标的一组方法。Spring pointcut 通常意味着标示方法,可以选择一组方法调用作为pointcut。

Advisor:

通知器,把Advice封闭起来,加入Pointcut(切入点),Spring就可以知道在什么方法上拦截,以及拦截后所要做的具体行为了。Advisor有两种实现NameMatchMethodPointcutAdvisor按方法名字匹配。RegexpMethodPointcutAdvisor,按正则匹配。

AOP创建:

AOP的创建主要分为两种一种是JDK的Proxy还有一种是CGLIB的,整个创建AOP的时序图一样,只不过差异在于最终AOPProxy产生AOP的过程。

首先AOP的具体创建的时序图如下:

以该时序图来分析我们AOP的创建过程。当我们使用AOP需要对某一个对象进行切面系统业务的时候,Spring会为该对象生成一个代理。具体是使用ProxyFactoryBean来配置我们的代理对象和方面行为。而具体的代理实现是通过JDK的Proxy或者CGLIB来完成的。ProxyFactoryBean是FactoryBean,调用该getObject方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
public Object
getObject() 
throws BeansException
{
                //初始化通知器链,实际上就是注册拦截器
    initializeAdvisorChain();
                if (isSingleton())
{
                    //返回生成的一个单件Proxy
      return getSingletonInstance();
    }
    else {
                    ....
                  //返回生成的Prototype的Proxy
    return newPrototypeInstance();
    }
}

整个方法包含了拦截器的初始化,以及获取代理对象的代理的过程。

第一行主要就是初始化通知器链,注册被代理对象上的拦截器。

当判断是一个单例对象的时候,就会通过getSingletonInstance()来获取单件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private synchronized Object
getSingletonInstance()
{
      if (this.singletonInstance
== 
null)
{
     this.targetSource
= freshTargetSource();
      if (this.autodetectInterfaces
&& getProxiedInterfaces().length == 
0 &&
!isProxyTargetClass())
          {
             //获取要代理的类
            Class
targetClass = getTargetClass();
            ...设置该类的接口类型
             setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass,this.proxyClassLoader));
           }
          super.setFrozen(this.freezeProxy);
            //这里才是真正的获取Proxy,
             this.singletonInstance
= getProxy(createAopProxy());
             }
    return this.singletonInstance;
}

在获取单件的时候,首先要做的就是要获取目标对象的接口,然后再次创建一个AopProxy来用于创建我们的AOP对象,这里createAopProxy的调用就是调用具体父类ProxyCreatorSupport来完成创建一个DefaultAopProxyFactory。当我们new一个ProxyFactoryBean的时候,它会调用父类的无参构造器,这里面就会默认的创建一个DefaultAopProxyFactory。这个就是我们默认的AOP代理工厂,最终是由它来决定我们到底使用JDK的Proxy还是CGLIB来创建代理的过程。在该类的成员属性中cglibAvailable初始化的时候会监测

ClassUtils.isPresent("net.sf.cglib.proxy.Enhancer", DefaultAopProxyFactory.class.getClassLoader());是否当前路径有cglib2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public AopProxy
createAopProxy(AdvisedSupport config) 
throws AopConfigException
{
if (config.isOptimize()
|| config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    Class
targetClass = config.getTargetClass();
                //代理类为空的时候
    if (targetClass
== 
null)
{
                ....
    }
               //
代理对象为接口的时候
    if (targetClass.isInterface())
{
         return new JdkDynamicAopProxy(config);
    }
    if (!cglibAvailable)
{
                                                                                                                                                                                                                                   
    }
    eturn
CglibProxyFactory.createCglibProxy(config);
    }
         else {
    return new JdkDynamicAopProxy(config);
 }

当我们的AopProxy产生后,接着就可以调用getProxy来返回产生的代理对象,这里以JdkDynamicAopProxy为例来分析其创建的过程。

1
2
3
4
5
6
7
public Object
getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled())
{
            ....}
    Class[]
proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(
this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader,
proxiedInterfaces, 
this);
}

当我们的Spring创建了代理对象后,当调用目标对象上的方法时,将都会被代理到InvocationHandler类的invoke方法中执行。在这里JdkDynamicAopProxy类实现了InvocationHandler接 口。

AOP拦截器

当使用JDK和CGLIB会生成不同的AopProxy代理对象,从而构造了不同的回调方法来启动对拦截器链的调用,比如在JdkDynamicAopProxy中的invoke方法,以及Cglib2AopProxy中使用DynamicAdvisedInterceptor的intercept方法。它们都使用了不同的AopProxy代理对象,但最终对AOP拦截的处理都是基本一样:它们对拦截器链的调用都是在ReflectiveMethodInvocation中通过proceed方法实现的。在这个proceed方法里,会逐个运行拦截器的拦截方法。在运行拦截器的拦截方法之前,需要对代理方法完成一个匹配判断,通过这个匹配判断来决定拦截器是否满足切面增强的要求。

AOP拦截器的实现的时序图如下:

对目标的对象都会首先被代理至代理对象的invoke来调用,不管使用哪种创建代理对象的过程

下面是invoke的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public Object
invoke(Object proxy, Method method, Object[] args) 
throws Throwable
{
    MethodInvocation
invocation;
    Object
oldProxy = 
null;
    boolean setProxyContext
false;
    TargetSource
targetSource = 
this.advised.targetSource;
    Class
targetClass = 
null;
    Object
target = 
null;
           try {
    if (!this.equalsDefined
&& AopUtils.isEqualsMethod(method)) {
        return equals(args[0]);
    }
    if (!this.hashCodeDefined
&& AopUtils.isHashCodeMethod(method)) {
       return hashCode();
    }
    if (!this.advised.opaque
&& method.getDeclaringClass().isInterface() &&
    method.getDeclaringClass().isAssignableFrom(Advised.class))
{
    //
利用Proxy配置来调用服务,直接调用目标方法
    return AopUtils.invokeJoinpointUsingReflection(this.advised,
method, args);
    }
             Object
retVal;
             if (this.advised.exposeProxy)
{
             oldProxy
= AopContext.setCurrentProxy(proxy);
             setProxyContext
true;
    }
                 //获取所要代理的对象
    target
= targetSource.getTarget();
    if (target
!= 
null)
{
           targetClass
= target.getClass();
    }
    //
获得该方法上的拦截器链
    List<Object>
chain = 
this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,
targetClass);
     //是否定义拦截器,否则直接调用目标对象的方法
    if (chain.isEmpty())
{
                 //直接调用目标对象的方法
                    retVal
= AopUtils.invokeJoinpointUsingReflection(target, method, args);
    }
    else {
          //如果不为空,则就创建一个ReflectiveMethodInvocation对象来先调用拦截器后调用目标方法
              invocation
new ReflectiveMethodInvocation
            (proxy,
target, method, args, targetClass, chain);
    //
处理切入点上的拦截器的方法
    retVal
= invocation.proceed();
    }
    if (retVal
!= 
null &&
retVal == target && method.getReturnType().isInstance(proxy) &&
    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass()))
{
        retVal
= proxy;
    }
           return retVal;
    }
    finally {
    if (target
!= 
null &&
!targetSource.isStatic()) {           
        targetSource.releaseTarget(target);
    }
    if (setProxyContext)
{   
        AopContext.setCurrentProxy(oldProxy);
        }
    }

对目标对象的方法直接调用是利用AopUtils.invokeJoinpointUsingReflection,该方法内部直接调用method.invoke(target, args),利用jdk自身的反射机制实现的;对拦截器链的调用时由ReflectiveMethodInvocation的proceed方法来实现的。

在proceed方法中,依然会首先判断是否拦截器结束了,否则就从拦截器链获取一个个的拦截器来执行前置行为,最后调用完了才真正调用我们的目标方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public Object
proceed() 
throws Throwable
{
//直接调用目标方法,可能是拦截器调用结束或者无拦截器
//interceptorsAndDynamicMethodMatchers这个其实就是目标方法上的拦截器链的大小
if (this.currentInterceptorIndex
== 
this.interceptorsAndDynamicMethodMatchers.size()
1)
{
            return invokeJoinpoint();
}
//调用拦截器链上的对象,依次
Object
interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher)
{
    //
这里获得相应的拦截器,如果拦截器可以匹配的上的话,那就调用拦截器的invoke 方法    
    InterceptorAndDynamicMethodMatcher
dm = (InterceptorAndDynamicMethodMatcher)     interceptorOrInterceptionAdvice;
     if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments))
{
        return dm.interceptor.invoke(this);
      }else {
           //调用拦截器链中的下一个拦截器
    return proceed();
      }
}
else {
    //
It's an interceptor, so we just invoke it: The pointcut will have
    //
been evaluated statically before this object was constructed.
    return ((MethodInterceptor)
interceptorOrInterceptionAdvice).invoke(
this);
      }
}

本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1251718

基于Spring源码分析AOP的实现机制的更多相关文章

  1. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

  2. Spring源码分析之AOP从解析到调用

    正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...

  3. 【Spring源码分析】配置文件读取流程

    前言 Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spri ...

  4. spring源码分析系列 (1) spring拓展接口BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.BeanFactoryPostProcessor.BeanDefinitionRegistryPostProcessor简述与demo示例 ...

  5. spring源码分析系列 (3) spring拓展接口InstantiationAwareBeanPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.InstantiationAwareBeanPostProcessor简述与demo示例 二.InstantiationAwareBean ...

  6. spring源码分析系列 (2) spring拓展接口BeanPostProcessor

    Spring更多分析--spring源码分析系列 主要分析内容: 一.BeanPostProcessor简述与demo示例 二.BeanPostProcessor源码分析:注册时机和触发点 (源码基于 ...

  7. Spring源码分析之循环依赖及解决方案

    Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...

  8. spring源码分析之spring-core总结篇

    1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...

  9. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

  10. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

随机推荐

  1. uniapp中,getApp()返回的实例到底是什么?为什么getApp()返回的实例无法访问vuex的$store

    按uniapp官方手册中说,getApp()函数用于获取当前应用实例.当前应用,也就是说当前应用程序.因为getApp()返回的实例可以用于访问app.vue中的globaldata,因此这个当前应用 ...

  2. vue动态绑定样式

    每次点击方块时通过三元表达式,改变对应的class,每一个不同的class对应不同的样式,从而通过改变class实现样式的切换. 实现代码 <template> <div class ...

  3. DOM – Web Components

    前言 Web Components 已经听过很多年了, 但在开发中用纯 DOM 来实现还是比较少见的. 通常我们是配搭 Angular, React, Vue, Lit 来使用. 这篇就来讲讲纯 We ...

  4. 记录一次BOOST库相关的使用包含互斥量、条件变量的类,引发的编译报错

    1. 工作中的代码: 2. 使用指针作为形参,不会造成编译报错,我是可以理解的. 那么请讨论下为什么使用值传递和引用作为形参,会造成编译报错? 3. 答案揭晓 boost 的mutex源码: 最终原因 ...

  5. MySQL存储引擎:InnoDB与MyISAM

    InnoDB和MyISAM是MySQL数据库中两种常用的存储引擎,它们在数据存储结构.事务支持.锁的支持.外键支持.性能等方面存在显著的差异.下面将详细介绍这两种存储引擎的特点和优势. 什么是存储引擎 ...

  6. map&unordered_map<key,value>key使用自定义类的要求

    std::unordered_map 的键要求: std::unordered_map 是基于哈希表的数据结构. 它要求键类型必须支持哈希计算,也就是必须有对应的 std::hash 函数. 另外,键 ...

  7. const` 关键字位于函数签名的末尾

    在 C++ 中,const 关键字可以应用于成员函数,表示该函数不会修改对象的成员变量. const 出现在 operator->() 成员函数的末尾,这意味着该成员函数在调用时不会修改对象的任 ...

  8. 日干算命api接口_json数据_性格/爱情/事业/财运/健康运势免费接口

    ​ 该API接口基于传统的八字学原理,通过用户提供的日干信息,为用户提供性格.爱情.事业.财运和健康等多方面的运势分析和建议.以下是该接口的详细介绍: ‌一.功能概述‌ ‌性格分析‌:根据用户的日干信 ...

  9. 2022年2月国产数据库排行榜:冠军宝座面临挑战,OceanBase 重返 TOP3

    大家好!文章开始本是用"新春快乐!虎年吉祥!"和大家打个招呼,无奈时间过得太快而文章整理得很慢,眼看崭新的三月还有几天就到来,那就在这里祝屏幕前的你在三月比二月更优秀! 月初,20 ...

  10. 谈谈你对 vue 的理解

    vue 是创建用户界面的 js 框架 ,是创建 spa 应用的框架 :使用 mvvm 模式,数据驱动视图模型 ,业务逻辑和页面解构分离开发:使用高效的 diff 算法渲染页面结构 : 采用组件化模式, ...