深入源码解析springaop实现的三个过程(代码片段)

davidwang456 davidwang456     2023-04-20     592

关键词:

Spring AOP的面向切面编程,是面向对象编程的一种补充,用于处理系统中分布的各个模块的横切关注点,比如说事务管理、日志、缓存等。它是使用动态代理实现的,在内存中临时为方法生成一个AOP对象,这个对象包含目标对象的所有方法,在特定的切点做了增强处理,并回调原来的方法。

Spring AOP的动态代理主要有两种方式实现,JDK动态代理和cglib动态代理。JDK动态代理通过反射来接收被代理的类,但是被代理的类必须实现接口,核心是InvocationHandler和Proxy类。cglib动态代理的类一般是没有实现接口的类,cglib是一个代码生成的类库,可以在运行时动态生成某个类的子类,所以,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

AOP实现中,可以看到三个主要的步骤,一个是代理对象的生成,然后是拦截器的作用,然后是Aspect编织的实现。

ProxyFactoryBean生成AopProxy

技术图片

 

ProxyFactoryBean生成AOP proxy

 1     /**
 2      * Return a proxy. Invoked when clients obtain beans from this factory bean.
 3      * Create an instance of the AOP proxy to be returned by this factory.
 4      * The instance will be cached for a singleton, and create on each call to
 5      * <code>getObject()</code> for a proxy.
 6      * @return a fresh AOP proxy reflecting the current state of this factory
 7      */
 8     public Object getObject() throws BeansException 
 9         initializeAdvisorChain();
10         if (isSingleton()) 
11             return getSingletonInstance();
12         
13         else 
14             if (this.targetName == null) 
15                 logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
16                         "Enable prototype proxies by setting the ‘targetName‘ property.");
17             
18             return newPrototypeInstance();
19         
20     

 

初始化Advisor chain

 1     /**
 2      * Create the advisor (interceptor) chain. Aadvisors that are sourced
 3      * from a BeanFactory will be refreshed each time a new prototype instance
 4      * is added. Interceptors added programmatically through the factory API
 5      * are unaffected by such changes.
 6      */
 7     private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException 
 8         if (this.advisorChainInitialized) 
 9             return;
10         
11 
12         if (!ObjectUtils.isEmpty(this.interceptorNames)) 
13             if (this.beanFactory == null) 
14                 throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
15                         "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
16             
17 
18             // Globals can‘t be last unless we specified a targetSource using the property...
19             if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
20                     this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) 
21                 throw new AopConfigException("Target required after globals");
22             
23 
24             // Materialize interceptor chain from bean names.
25             for (int i = 0; i < this.interceptorNames.length; i++) 
26                 String name = this.interceptorNames[i];
27                 if (logger.isTraceEnabled()) 
28                     logger.trace("Configuring advisor or advice ‘" + name + "‘");
29                 
30 
31                 if (name.endsWith(GLOBAL_SUFFIX)) 
32                     if (!(this.beanFactory instanceof ListableBeanFactory)) 
33                         throw new AopConfigException(
34                                 "Can only use global advisors or interceptors with a ListableBeanFactory");
35                     
36                     addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
37                             name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
38                 
39 
40                 else 
41                     // If we get here, we need to add a named interceptor.
42                     // We must check if it‘s a singleton or prototype.
43                     Object advice = null;
44                     if (this.singleton || this.beanFactory.isSingleton(this.interceptorNames[i])) 
45                         // Add the real Advisor/Advice to the chain.
46                         advice = this.beanFactory.getBean(this.interceptorNames[i]);
47                     
48                     else 
49                         // It‘s a prototype Advice or Advisor: replace with a prototype.
50                         // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
51                         advice = new PrototypePlaceholderAdvisor(this.interceptorNames[i]);
52                     
53                     addAdvisorOnChainCreation(advice, this.interceptorNames[i]);
54                 
55             
56         
57 
58         this.advisorChainInitialized = true;
59     

 

增加advisor chain(AdvisedSupport.java)

 1     private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException 
 2         Assert.notNull(advisor, "Advisor must not be null");
 3         if (isFrozen()) 
 4             throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
 5         
 6         if (pos > this.advisors.size()) 
 7             throw new IllegalArgumentException(
 8                     "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
 9         
10         this.advisors.add(pos, advisor);
11         updateAdvisorArray();
12         adviceChanged();
13     

 

Spring AOP中拦截器链

1.开始步骤--获取AopProxy主流程

ProxyCreatorSupport.java

    /**
     * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
     * create an AOP proxy with <code>this</code> as an argument.
     */
    protected final synchronized AopProxy createAopProxy() 
        if (!this.active) 
            activate();
        
        return getAopProxyFactory().createAopProxy(this);
    

2.获取AopProxy实现 --DefaultAopProxyFactory.java

ProxyFactoryBean类继承了AdvisedSupport类,后者继承了ProxyConfig类并定义了操作advisor 和interceptor的接口,以支持AOP。当BeanFactory实例化ProxyFactoryBean时,根据配置文件的定义将关于 advice,pointcut,advisor,所代理的接口和接口实现类的所有信息传给ProxyFactoryBean。

当客户程序调用BeanFactory的getBean方法时,ProxyFactory使用JdkDynamicAopProxy实例化 BeanImpl类,并用JdkDynamicAopProxy的invoke方法执行advice。至于执行advice的时机,由 ProxyFactoryBean调用RegexpMethodPointcutAdvisor进行判断。

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException 
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) 
            Class targetClass = config.getTargetClass();
            if (targetClass == null) 
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            
            if (targetClass.isInterface()) 
                return new JdkDynamicAopProxy(config);
            
            if (!cglibAvailable) 
                throw new AopConfigException(
                        "Cannot proxy target class because CGLIB2 is not available. " +
                        "Add CGLIB to the class path or specify proxy interfaces.");
            
            return CglibProxyFactory.createCglibProxy(config);
        
        else 
            return new JdkDynamicAopProxy(config);
        
    

 

3.获取AopProxy的执行路径

    public Object getProxy(ClassLoader classLoader) 
        if (logger.isDebugEnabled()) 
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    

 

4.激发拦截器链主过程

 1     /**
 2      * Implementation of <code>InvocationHandler.invoke</code>.
 3      * <p>Callers will see exactly the exception thrown by the target,
 4      * unless a hook method throws an exception.
 5      */
 6     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
 7         MethodInvocation invocation = null;
 8         Object oldProxy = null;
 9         boolean setProxyContext = false;
10 
11         TargetSource targetSource = this.advised.targetSource;
12         Class targetClass = null;
13         Object target = null;
14 
15         try 
16             if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) 
17                 // The target does not implement the equals(Object) method itself.
18                 return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE);
19             
20             if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) 
21                 // The target does not implement the hashCode() method itself.
22                 return new Integer(hashCode());
23             
24             if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
25                     method.getDeclaringClass().isAssignableFrom(Advised.class)) 
26                 // Service invocations on ProxyConfig with the proxy config...
27                 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
28             
29 
30             Object retVal = null;
31 
32             if (this.advised.exposeProxy) 
33                 // Make invocation available if necessary.
34                 oldProxy = AopContext.setCurrentProxy(proxy);
35                 setProxyContext = true;
36             
37 
38             // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
39             // in case it comes from a pool.
40             target = targetSource.getTarget();
41             if (target != null) 
42                 targetClass = target.getClass();
43             
44 
45             // Get the interception chain for this method.
46             List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
47 
48             // Check whether we have any advice. If we don‘t, we can fallback on direct
49             // reflective invocation of the target, and avoid creating a MethodInvocation.
50             if (chain.isEmpty()) 
51                 // We can skip creating a MethodInvocation: just invoke the target directly
52                 // Note that the final invoker must be an InvokerInterceptor so we know it does
53                 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
54                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
55             
56             else 
57                 // We need to create a method invocation...
58                 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
59                 // Proceed to the joinpoint through the interceptor chain.
60                 retVal = invocation.proceed();
61             
62 
63             // Massage return value if necessary.
64             if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
65                     !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) 
66                 // Special case: it returned "this" and the return type of the method
67                 // is type-compatible. Note that we can‘t help if the target sets
68                 // a reference to itself in another returned object.
69                 retVal = proxy;
70             
71             return retVal;
72         
73         finally 
74             if (target != null && !targetSource.isStatic()) 
75                 // Must have come from TargetSource.
76                 targetSource.releaseTarget(target);
77             
78             if (setProxyContext) 
79                 // Restore old proxy.
80                 AopContext.setCurrentProxy(oldProxy);
81             
82         
83     

 

5.获取拦截器链DefaultAdvisorChainFactory.java

    public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class targetClass) 
        // This is somewhat tricky... we have to process introductions first,
        // but we need to preserve order in the ultimate list.
        List interceptorList = new ArrayList(config.getAdvisors().length);
        boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        Advisor[] advisors = config.getAdvisors();
        for (int i = 0; i < advisors.length; i++) 
            Advisor advisor = advisors[i];
            if (advisor instanceof PointcutAdvisor) 
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) 
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) 
                        if (mm.isRuntime()) 
                            // Creating a new object instance in the getInterceptors() method
                            // isn‘t a problem as we normally cache created chains.
                            for (int j = 0; j < interceptors.length; j++) 
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j], mm));
                            
                        
                        else 
                            interceptorList.addAll(Arrays.asList(interceptors));
                        
                    
                
            
            else if (advisor instanceof IntroductionAdvisor) 
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) 
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                
            
            else 
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            
        
        return interceptorList;
    
6.激发拦截链工作实现 ---ReflectiveMethodInvocation.java

    public Object proceed() throws Throwable 
        //    We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) 
            return invokeJoinpoint();
        

        Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) 
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) 
                return dm.interceptor.invoke(this);
            
            else 
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                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);
        
    

 

Spring AOP中Aspect编织的实现

1.前面我们谈到拦截器起作用时,实现代码(ReflectiveMethodInvocation.java)如下:

    public Object proceed() throws Throwable 
        //    We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) 
            return invokeJoinpoint();
        

        Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) 
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) 
                return dm.interceptor.invoke(this);
            
            else 
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                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);
        
    

 

2.前置Advice MethodBeforeAdviceInterceptor.java

    public Object invoke(MethodInvocation mi) throws Throwable 
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    

 

3.后置Advice AfterReturningAdviceInterceptor.java

    public Object invoke(MethodInvocation mi) throws Throwable 
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    

 

总结

没图没真相

技术图片

 

技术图片

 Spring框架的AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。

 

深入浅出reentrantreadwritelock源码解析(代码片段)

...具备一些基本的知识理解AQS的实现原理之前有写过一篇《深入浅出AQS源码解析》关于AQS的文章,对AQS原理不了解的同学可以先看一下理解ReentrantLock的实现原理Reentra 查看详情

springaop源码解析(代码片段)

...的有点长,看完需要点耐心。很多读者希望能写一写SpringAOP的源码分析文章,这样读者看完IOC+AOP也就对Spring会有比较深的理解了。今天终于成文了,可能很多读者早就不再等待了,不过主要为了后来者吧。本... 查看详情

深入解析node.jssettimeout方法的执行过程(代码片段)

深入了解setTimeout源码之前,本有两个选择。一是通过chromium源码分析,二是通过Node.js源码分析。后来发现第一种方案的源码获取成本太大,于是从Node官网获取了几十兆的代码用来了解。当前的Node版本为:v10.16.0se... 查看详情

爱上源码,重学springaop深入(代码片段)

...法运行时间)、权限控制等也就是对业务方法做了增强1.1SpringAOP环境介绍**目标:**认识AOP基础环境,后面讲使用这个基础环境进行源码讲解1)引入起步依赖compile(project( 查看详情

springaop源码解析(代码片段)

...的有点长,看完需要点耐心。很多读者希望能写一写SpringAOP的源码分析文章,这样读者看完IOC+AOP也就对Spring会有比较深的理解了。今天终于成文了,可能很多读者早就不再等待了,不过主要为了后来者吧。本... 查看详情

springaop源码—<aop:config/>aop配置标签解析一万字(代码片段)

  基于最新Spring5.x,对SpringAOP中的<aop:config/>标签的解析源码进行了详细分析,这是SpringAOP源码的入口!  此前我们已经详细学习了SpringAOP的基本使用,现在我们来学习SpringAOP的源码,学习AOP源... 查看详情

做一个合格的程序猿之浅析springaop源码(十八)springaop开发大作战源码解析

其实上一篇文章价值很小,也有重复造轮子的嫌疑,网上AOP的实例很多,不胜枚举,其实我要说的并不是这个,我想要说的就是上一节中spring的配置文件:我们这边并没有用到我们上几节分析的哪几个AOP的主要实现类:ProxyFa... 查看详情

rxswift之深入解析dispose源码的实现(代码片段)

一、前言任何对象都有生命周期,有创建就要销毁。OC中有init和dealloc,swift有init和deinit,RxSwift也不例外,有create和dispose。RxSwift有两种清除方式:订阅产生的可清除资源(Disposable)对象,调用dispos... 查看详情

nativejsbridge源码解析深入理解jsbridge(代码片段)

最近项目中使用了HyBrid框架,但是在使用过程中遇到了不少问题,因此花时间来研究了一下其中原理!在平时开发过程中,不管是可复用性非常高,可以跨平台开发的HyBrid,还是半Native半web浅尝辄止的HyBrid,... 查看详情

rxswift之深入解析特殊序列deallocating与deallocated的源码实现(代码片段)

一、引言在RxSwfit中,有两个特殊序列:deallocating与deallocated,deinit等价于dealloc。在deallocating与deallocated两个序列被订阅时,那么当deinit调用时会触发这两个序列发送信号,它们的执行顺序为:deallocating->d... 查看详情

androidhandler机制源码解析(代码片段)

...行过程中的消息有序进行传递和处理。此文将在Android6.0源码层面对Handler的运行机制进行简要剖析。1.总览Handler的内部实现主要涉及到这三个类:Thread、MessageQueue和Looper。它们之间的关系可以用如下的图来简单说明:Thread是最基础... 查看详情

springaop源码解析

以编程的方式使用spring提供的AOP功能,下面是一个简单的例子:package com.zws.spring.core.aop.springAop;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;import org.springframe 查看详情

对springaop的进一步深入理解(代码片段)

...xff0c;最后增强方法的调用整个流程做一个整理和理解。将SpringAOP功能整体串联起来。@EnableAspectJAutoProxy开启AOP功能前面已经研究过这个注解原理:Spring之@EnableAspectJAutoProxy开启AOP功能原理简单来说,就是这个注解通... 查看详情

elasticsearch源码解析:环境搭建(代码片段)

...可以帮助我们快速了解某个功能,阅读源码有助于我们更深入的理解这一功能的实现及思想。因此,在学习Elasticsearch的时候我也准备采用这样的模式。所以,从今天开始,我会定期更新学习Elasticsearch源码的收获。在开始学习之... 查看详情

spring框架系列(10)-springaop实现原理详解之aop代理的创建

上文我们介绍了SpringAOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor)。本文在此基础上继续介绍,代理(cglib代理和JDK代理)的创... 查看详情

spring原理篇(14)--springaop源码的实现<一>(代码片段)

@TOC#Spring系列记录在程序走的每一步___auth:huf非常感谢各位同学们;各位同事以及开发好友的关注与支持;上一篇文章非常荣幸的冲到了热榜第一;Spring连载内容剩余内容不多了;一个就是AOP的源码实现;还有一个就是想说Spring事务... 查看详情

基于jdk动态代理实现的springaop源码学习(代码片段)

最近花了点时间学习了一下SpringAop的内容,到此简单做一下笔记。如果存在错误还望多多指教。 概念所谓的Aop就是AspectOrientedProgramming,即面向切面编程。对于Aop存在一个组织AOPAlliance,该组织制定了一下跟Aop有关的... 查看详情

java集合hashtable源码深入解析

概要前面,我们已经系统的对List进行了学习。接下来,我们先学习Map,然后再学习Set;因为Set的实现类都是基于Map来实现的(如,HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的)。首先,我们看看Map架构。如上图:(01)Map是映... 查看详情