spring aop source implementation analysis

AOP Aspect Oriented Programming is, we can achieve AOP from several levels.

Modify the source code compiler, code modifications bytecode or bytecodes before the loading operation of dynamically created proxy class bytecode After loading, the following is a comparison of various implementation mechanism.

Spring Framework is spring AOP aspect-oriented programming ideas, called the AOP uses a "cross" technique, the multi-functional business processes involve general extraction and individually packaged to form a separate section, at the right time these transverse section cut into business processes specified location.
AOP in the end what can we do? AOP can do very much.

  • Performance monitoring, call recording before and after method invocation time, the method of execution is too long or timeout alarm.
  • Caching proxy, caching method returns a value, the next time the method is performed directly from the cache.
  • Software cracks, is determined using the logic verification class AOP software modifications.
  • Logging, logging system before and after method execution.
  • Workflow system, workflow system needs to mix code and business process execution engine code together, then we can use AOP to separate, and dynamically articulated business.
  • Rights verification, validation is performed before the method has permission to execute the current method, there is no permission to perform an exception thrown by the business code capture.

This article deals combined with dynamic proxy source code to explain the implementation principles:

1. First Analysis Advice

before the implementation of the intercept method Cglib2AopProxy:


/**
     * General purpose AOP callback. Used when the target is dynamic or when the
     * proxy is not frozen.
     */
    private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

        private AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            MethodInvocation invocation = null;
            Object oldProxy = null;
            boolean setProxyContext = false;
            Class targetClass = null;
            Object target = null;
            try {
                Object retVal = null;
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                // May be <code>null</code>. 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 chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                // 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.
                    retVal = methodProxy.invoke(target, args);
                }
                else {
                    // We need to create a method invocation...
                    invocation = new CglibMethodInvocation(proxy, target, method, args,
                            targetClass, chain, methodProxy);
                    // If we get here, we need to create a MethodInvocation.
                    retVal = invocation.proceed();
                }

                retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
                return retVal;
            }
            finally {
                if (target != null) {
                    releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }

The first step: Get target

target.getClass();

Step Two: Get interceptors and advice, the definition of a good return

org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor示例

/**
     * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
     * for the given method, based on this configuration.
     * @param method the proxied method
     * @param targetClass the target class
     * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
     */
    public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
        MethodCacheKey cacheKey = new MethodCacheKey(method);
        List cached = (List) this.methodCache.get(cacheKey);
        if (cached == null) {
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this, method, targetClass);
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }

The third step is to create a method invocation

                   // We need to create a method invocation...
                    invocation = new CglibMethodInvocation(proxy, target, method, args,
                            targetClass, chain, methodProxy);

The fourth step before the implementation of the method aop

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println(" Before method!");
    }

The fifth step is triggered MethodBeforeAdviceInterceptor invoke method

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

Step Six: Trigger ReflectiveMethodInvocation the process method

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);
        }
    }

The seventh step, packing the return value Cglib2AopProxy


/**
     * Wrap a return of this if necessary to be the proxy
     */
    private static Object massageReturnTypeIfNecessary(Object proxy, Object target, Method method, Object retVal) {
        // Massage return value if necessary
        if (retVal != null && retVal == target &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this".
            // Note that we can't help if the target sets a reference
            // to itself in another returned object.
            retVal = proxy;
        }
        return retVal;
    }

The last execution method finanly

finally {
                if (target != null) {
                    releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }

before,after,around,throw基本相似,不一一赘述

2.PointCut和Advisor为例

2.1 创建代理的过程

首先是ProxyFactoryBean获取对象代理

/**
     * Return a proxy. Invoked when clients obtain beans from this factory bean.
     * Create an instance of the AOP proxy to be returned by this factory.
     * The instance will be cached for a singleton, and create on each call to
     * <code>getObject()</code> for a proxy.
     * @return a fresh AOP proxy reflecting the current state of this factory
     */
    public Object getObject() throws BeansException {
        initializeAdvisorChain();
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }

获取过程如下:

 /**
     * Return the singleton instance of this class's proxy object,
     * lazily creating it if it hasn't been created already.
     * @return the shared singleton proxy
     */
    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // Rely on AOP infrastructure to tell us what interfaces to proxy.
                Class targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    } 

父类创建代理的过程

   /**
     * 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);
    }

调用代理工厂创建代理的过程

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);
        }
    }

Guess you like

Origin blog.csdn.net/woshinidadaye_/article/details/91044041