深入理解SpringAOP之代理對象


  本篇文章主要帶大家簡單分析一下AOP的代理對象,至於AOP是什么,如何配置等基礎性知識,不在這里討論。閱讀前請先參考:代理模式,在這之前我們需要了解springframework的三個核心接口與getBean方法

一、FactoryBean&BeanFactory&ObjectFactory

  這三個接口都為Springframework的核心接口,雖然這三個名字很像,但是意義卻千差萬別。面試的時候也常問它們之間的區別。BeanFactory本身就是一個bean的工廠,同時也是我們的IOC容器,而FactoryBean是一個特殊的Bean,我們可以來看看這個接口:

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

/**
 * Interface to be implemented by objects used within a {@link BeanFactory} which
 * are themselves factories for individual objects. If a bean implements this
 * interface, it is used as a factory for an object to expose, not directly as a
 * bean instance that will be exposed itself.
 *
 * <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
 * A FactoryBean is defined in a bean style, but the object exposed for bean
 * references ({@link #getObject()}) is always the object that it creates.
 *
 * <p>FactoryBeans can support singletons and prototypes, and can either create
 * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
 * interface allows for exposing more fine-grained behavioral metadata.
 *
 * <p>This interface is heavily used within the framework itself, for example for
 * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
 * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
 * custom components as well; however, this is only common for infrastructure code.
 *
 * <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
 * supposed to rely on annotation-driven injection or other reflective facilities.</b>
 * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
 * the bootstrap process, even ahead of any post-processor setup. If you need access
 * other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
 *
 * <p>Finally, FactoryBean objects participate in the containing BeanFactory's
 * synchronization of bean creation. There is usually no need for internal
 * synchronization other than for purposes of lazy initialization within the
 * FactoryBean itself (or the like).
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 08.03.2003
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.aop.framework.ProxyFactoryBean
 * @see org.springframework.jndi.JndiObjectFactoryBean
 */
public interface FactoryBean<T> {

    /**
     * Return an instance (possibly shared or independent) of the object
     * managed by this factory.
     * <p>As with a {@link BeanFactory}, this allows support for both the
     * Singleton and Prototype design pattern.
     * <p>If this FactoryBean is not fully initialized yet at the time of
     * the call (for example because it is involved in a circular reference),
     * throw a corresponding {@link FactoryBeanNotInitializedException}.
     * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
     * objects. The factory will consider this as normal value to be used; it
     * will not throw a FactoryBeanNotInitializedException in this case anymore.
     * FactoryBean implementations are encouraged to throw
     * FactoryBeanNotInitializedException themselves now, as appropriate.
     * @return an instance of the bean (can be {@code null})
     * @throws Exception in case of creation errors
     * @see FactoryBeanNotInitializedException
     */
    @Nullable
    T getObject() throws Exception;

    /**
     * Return the type of object that this FactoryBean creates,
     * or {@code null} if not known in advance.
     * <p>This allows one to check for specific types of beans without
     * instantiating objects, for example on autowiring.
     * <p>In the case of implementations that are creating a singleton object,
     * this method should try to avoid singleton creation as far as possible;
     * it should rather estimate the type in advance.
     * For prototypes, returning a meaningful type here is advisable too.
     * <p>This method can be called <i>before</i> this FactoryBean has
     * been fully initialized. It must not rely on state created during
     * initialization; of course, it can still use such state if available.
     * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
     * {@code null} here. Therefore it is highly recommended to implement
     * this method properly, using the current state of the FactoryBean.
     * @return the type of object that this FactoryBean creates,
     * or {@code null} if not known at the time of the call
     * @see ListableBeanFactory#getBeansOfType
     */
    @Nullable
    Class<?> getObjectType();

    /**
     * Is the object managed by this factory a singleton? That is,
     * will {@link #getObject()} always return the same object
     * (a reference that can be cached)?
     * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
     * the object returned from {@code getObject()} might get cached
     * by the owning BeanFactory. Hence, do not return {@code true}
     * unless the FactoryBean always exposes the same reference.
     * <p>The singleton status of the FactoryBean itself will generally
     * be provided by the owning BeanFactory; usually, it has to be
     * defined as singleton there.
     * <p><b>NOTE:</b> This method returning {@code false} does not
     * necessarily indicate that returned objects are independent instances.
     * An implementation of the extended {@link SmartFactoryBean} interface
     * may explicitly indicate independent instances through its
     * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
     * implementations which do not implement this extended interface are
     * simply assumed to always return independent instances if the
     * {@code isSingleton()} implementation returns {@code false}.
     * <p>The default implementation returns {@code true}, since a
     * {@code FactoryBean} typically manages a singleton instance.
     * @return whether the exposed object is a singleton
     * @see #getObject()
     * @see SmartFactoryBean#isPrototype()
     */
    default boolean isSingleton() {
        return true;
    }

}
View Code

  這里面有三個方法,分別為:getObject,getObjectType,isSingleton。根據文檔解釋,它只是一個生產對象的工廠,被Spring管理 。這個工廠負責提供我們需要的對象。當需要特殊的方式創建Bean時,則考慮實現該接口。我舉個例子來說明:

package org.hzgj.spring.study;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class WaterFactoryBean implements FactoryBean<Water> {

    @Override
    public Water getObject() throws Exception {
        Water water=new Water();
        water.setCapacity(20);
        return water;
    }

    @Override
    public Class<?> getObjectType() {
        return Water.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

//.....

package org.hzgj.spring.study;

@Deprecated
public class Water {

    private int capacity;

    public int getCapacity() {
        return capacity;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    public void test() {
        System.out.println("test");
    }

    @Deprecated
    public void test1() {
        System.out.println("test1");
    }
}
//.....

  ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        Water water = applicationContext.getBean(Water.class);
        water.test1();
// ....能夠成功獲取到對象

  在上面例子里,我們本身是要獲得Water對象,那么此時Water對象實際上是通過FactoryBean創建的,因此我們在獲取對象時可以添加我們自己的邏輯。

  下面我們根據源代碼來追溯一下getBean與BeanFactory關聯,具體可以參考一下AbstractBeanFactory的doGetBean方法,那么在這里簡單的說明一下執行過程:

  1) 如果是單例對象的Bean會去緩存中獲取

    我們先看一下getSinglelone方法:

/** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);


/** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);


/**
     * Return the (raw) singleton object registered under the given name.
     * <p>Checks already instantiated singletons and also allows for an early
     * reference to a currently created singleton (resolving a circular reference).
     * @param beanName the name of the bean to look for
     * @param allowEarlyReference whether early references should be created or not
     * @return the registered singleton object, or {@code null} if none found
     */
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

    在這里我們獲取單例對象時一定和ObjectFactory有關系

  2)從它的parentBeanFactory中獲取

// Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }

 

  3)處理bean的dependsOn

final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        getBean(dep);
                    }
                }

 

  4)根據bean的scope類型來獲取對應的bean

    // Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                // Explicitly remove instance from singleton cache: It might have been put there
                                // eagerly by the creation process, to allow for circular reference resolution.
                                // Also remove any beans that received a temporary reference to the bean.
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                beforePrototypeCreation(beanName);
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                finally {
                                    afterPrototypeCreation(beanName);
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

  5)  根據需要做類型轉換

// Check if required type matches the type of the actual bean instance.
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            catch (TypeMismatchException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }

  6)getObjectForBeanInstance

  通過源代碼我們可以發覺 getObjectForBeanInstance方法調用頻率異常之高,那么我們就來看一看,它到底是做什么的:

/**
     * Get the object for the given bean instance, either the bean
     * instance itself or its created object in case of a FactoryBean.
     * @param beanInstance the shared bean instance
     * @param name name that may include factory dereference prefix
     * @param beanName the canonical bean name
     * @param mbd the merged bean definition
     * @return the object to expose for the bean
     */
    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

        // Don't let calling code try to dereference the factory if the bean isn't a factory.
        if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }

        // Now we have the bean instance, which may be a normal bean or a FactoryBean.
        // If it's a FactoryBean, we use it to create a bean instance, unless the
        // caller actually wants a reference to the factory.
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

        Object object = null;
        if (mbd == null) {
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }

  這段代碼最主要是看看是否需要從FactoryBean獲取對象的。

  最后我們在聊聊ObjectFactory:

public interface ObjectFactory<T> {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return an instance of the bean (should never be {@code null})
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

  該接口和FactoryBean很像,根據文檔說明其getObject方法的返回值不建議為null,另外我們可以發現Bean為singlone時會大量的使用ObjectFactory處理,代碼示例:

package org.hzgj.spring.study;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.stereotype.Component;

@Component
public class WaterFactory implements ObjectFactory {
    @Override
    public Object getObject() throws BeansException {
        return new Water();
    }
}

//這樣子無法獲取water,它只單純是個工廠

   ObjectFactory更像是一個在BeanFactory通過Bean名稱關聯的對象,只不過它在運行時確定getObject()方法返回的對象內容,再者它不像BeanFactory一樣能夠制定Bean的類型

二、AOP的核心探究

2.1、核心接口初探

  為什么剛開始要說FactoryBean,因為它的文檔注釋已經提醒我們去參考ProxyFactoryBean了,ProxyFactoryBean是生成目標對象代理的核心,那么我們在此先看一下類圖:

  

  我們可以得知ProxyFactoryBean實現了FactoryBean。

  關於AOP的幾個重要的核心接口和類如下:

  ProxyConfig:

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import java.io.Serializable;

import org.springframework.util.Assert;

/**
 * Convenience superclass for configuration used in creating proxies,
 * to ensure that all proxy creators have consistent properties.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see AdvisedSupport
 */
public class ProxyConfig implements Serializable {

    /** use serialVersionUID from Spring 1.2 for interoperability */
    private static final long serialVersionUID = -8409359707199703185L;


    private boolean proxyTargetClass = false;

    private boolean optimize = false;

    boolean opaque = false;

    boolean exposeProxy = false;

    private boolean frozen = false;


    /**
     * Set whether to proxy the target class directly, instead of just proxying
     * specific interfaces. Default is "false".
     * <p>Set this to "true" to force proxying for the TargetSource's exposed
     * target class. If that target class is an interface, a JDK proxy will be
     * created for the given interface. If that target class is any other class,
     * a CGLIB proxy will be created for the given class.
     * <p>Note: Depending on the configuration of the concrete proxy factory,
     * the proxy-target-class behavior will also be applied if no interfaces
     * have been specified (and no interface autodetection is activated).
     * @see org.springframework.aop.TargetSource#getTargetClass()
     */
    public void setProxyTargetClass(boolean proxyTargetClass) {
        this.proxyTargetClass = proxyTargetClass;
    }

    /**
     * Return whether to proxy the target class directly as well as any interfaces.
     */
    public boolean isProxyTargetClass() {
        return this.proxyTargetClass;
    }

    /**
     * Set whether proxies should perform aggressive optimizations.
     * The exact meaning of "aggressive optimizations" will differ
     * between proxies, but there is usually some tradeoff.
     * Default is "false".
     * <p>For example, optimization will usually mean that advice changes won't
     * take effect after a proxy has been created. For this reason, optimization
     * is disabled by default. An optimize value of "true" may be ignored
     * if other settings preclude optimization: for example, if "exposeProxy"
     * is set to "true" and that's not compatible with the optimization.
     */
    public void setOptimize(boolean optimize) {
        this.optimize = optimize;
    }

    /**
     * Return whether proxies should perform aggressive optimizations.
     */
    public boolean isOptimize() {
        return this.optimize;
    }

    /**
     * Set whether proxies created by this configuration should be prevented
     * from being cast to {@link Advised} to query proxy status.
     * <p>Default is "false", meaning that any AOP proxy can be cast to
     * {@link Advised}.
     */
    public void setOpaque(boolean opaque) {
        this.opaque = opaque;
    }

    /**
     * Return whether proxies created by this configuration should be
     * prevented from being cast to {@link Advised}.
     */
    public boolean isOpaque() {
        return this.opaque;
    }

    /**
     * Set whether the proxy should be exposed by the AOP framework as a
     * ThreadLocal for retrieval via the AopContext class. This is useful
     * if an advised object needs to call another advised method on itself.
     * (If it uses {@code this}, the invocation will not be advised).
     * <p>Default is "false", in order to avoid unnecessary extra interception.
     * This means that no guarantees are provided that AopContext access will
     * work consistently within any method of the advised object.
     */
    public void setExposeProxy(boolean exposeProxy) {
        this.exposeProxy = exposeProxy;
    }

    /**
     * Return whether the AOP proxy will expose the AOP proxy for
     * each invocation.
     */
    public boolean isExposeProxy() {
        return this.exposeProxy;
    }

    /**
     * Set whether this config should be frozen.
     * <p>When a config is frozen, no advice changes can be made. This is
     * useful for optimization, and useful when we don't want callers to
     * be able to manipulate configuration after casting to Advised.
     */
    public void setFrozen(boolean frozen) {
        this.frozen = frozen;
    }

    /**
     * Return whether the config is frozen, and no advice changes can be made.
     */
    public boolean isFrozen() {
        return this.frozen;
    }


    /**
     * Copy configuration from the other config object.
     * @param other object to copy configuration from
     */
    public void copyFrom(ProxyConfig other) {
        Assert.notNull(other, "Other ProxyConfig object must not be null");
        this.proxyTargetClass = other.proxyTargetClass;
        this.optimize = other.optimize;
        this.exposeProxy = other.exposeProxy;
        this.frozen = other.frozen;
        this.opaque = other.opaque;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("proxyTargetClass=").append(this.proxyTargetClass).append("; ");
        sb.append("optimize=").append(this.optimize).append("; ");
        sb.append("opaque=").append(this.opaque).append("; ");
        sb.append("exposeProxy=").append(this.exposeProxy).append("; ");
        sb.append("frozen=").append(this.frozen);
        return sb.toString();
    }

}
View Code

  該類定義代理類最基本的代理配置

  Advised:

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import org.aopalliance.aop.Advice;

import org.springframework.aop.Advisor;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;

/**
 * Interface to be implemented by classes that hold the configuration
 * of a factory of AOP proxies. This configuration includes the
 * Interceptors and other advice, Advisors, and the proxied interfaces.
 *
 * <p>Any AOP proxy obtained from Spring can be cast to this interface to
 * allow manipulation of its AOP advice.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 13.03.2003
 * @see org.springframework.aop.framework.AdvisedSupport
 */
public interface Advised extends TargetClassAware {

    /**
     * Return whether the Advised configuration is frozen,
     * in which case no advice changes can be made.
     */
    boolean isFrozen();

    /**
     * Are we proxying the full target class instead of specified interfaces?
     */
    boolean isProxyTargetClass();

    /**
     * Return the interfaces proxied by the AOP proxy.
     * <p>Will not include the target class, which may also be proxied.
     */
    Class<?>[] getProxiedInterfaces();

    /**
     * Determine whether the given interface is proxied.
     * @param intf the interface to check
     */
    boolean isInterfaceProxied(Class<?> intf);

    /**
     * Change the {@code TargetSource} used by this {@code Advised} object.
     * <p>Only works if the configuration isn't {@linkplain #isFrozen frozen}.
     * @param targetSource new TargetSource to use
     */
    void setTargetSource(TargetSource targetSource);

    /**
     * Return the {@code TargetSource} used by this {@code Advised} object.
     */
    TargetSource getTargetSource();

    /**
     * Set whether the proxy should be exposed by the AOP framework as a
     * {@link ThreadLocal} for retrieval via the {@link AopContext} class.
     * <p>It can be necessary to expose the proxy if an advised object needs
     * to invoke a method on itself with advice applied. Otherwise, if an
     * advised object invokes a method on {@code this}, no advice will be applied.
     * <p>Default is {@code false}, for optimal performance.
     */
    void setExposeProxy(boolean exposeProxy);

    /**
     * Return whether the factory should expose the proxy as a {@link ThreadLocal}.
     * <p>It can be necessary to expose the proxy if an advised object needs
     * to invoke a method on itself with advice applied. Otherwise, if an
     * advised object invokes a method on {@code this}, no advice will be applied.
     * <p>Getting the proxy is analogous to an EJB calling {@code getEJBObject()}.
     * @see AopContext
     */
    boolean isExposeProxy();

    /**
     * Set whether this proxy configuration is pre-filtered so that it only
     * contains applicable advisors (matching this proxy's target class).
     * <p>Default is "false". Set this to "true" if the advisors have been
     * pre-filtered already, meaning that the ClassFilter check can be skipped
     * when building the actual advisor chain for proxy invocations.
     * @see org.springframework.aop.ClassFilter
     */
    void setPreFiltered(boolean preFiltered);

    /**
     * Return whether this proxy configuration is pre-filtered so that it only
     * contains applicable advisors (matching this proxy's target class).
     */
    boolean isPreFiltered();

    /**
     * Return the advisors applying to this proxy.
     * @return a list of Advisors applying to this proxy (never {@code null})
     */
    Advisor[] getAdvisors();

    /**
     * Add an advisor at the end of the advisor chain.
     * <p>The Advisor may be an {@link org.springframework.aop.IntroductionAdvisor},
     * in which new interfaces will be available when a proxy is next obtained
     * from the relevant factory.
     * @param advisor the advisor to add to the end of the chain
     * @throws AopConfigException in case of invalid advice
     */
    void addAdvisor(Advisor advisor) throws AopConfigException;

    /**
     * Add an Advisor at the specified position in the chain.
     * @param advisor the advisor to add at the specified position in the chain
     * @param pos position in chain (0 is head). Must be valid.
     * @throws AopConfigException in case of invalid advice
     */
    void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

    /**
     * Remove the given advisor.
     * @param advisor the advisor to remove
     * @return {@code true} if the advisor was removed; {@code false}
     * if the advisor was not found and hence could not be removed
     */
    boolean removeAdvisor(Advisor advisor);

    /**
     * Remove the advisor at the given index.
     * @param index index of advisor to remove
     * @throws AopConfigException if the index is invalid
     */
    void removeAdvisor(int index) throws AopConfigException;

    /**
     * Return the index (from 0) of the given advisor,
     * or -1 if no such advisor applies to this proxy.
     * <p>The return value of this method can be used to index into the advisors array.
     * @param advisor the advisor to search for
     * @return index from 0 of this advisor, or -1 if there's no such advisor
     */
    int indexOf(Advisor advisor);

    /**
     * Replace the given advisor.
     * <p><b>Note:</b> If the advisor is an {@link org.springframework.aop.IntroductionAdvisor}
     * and the replacement is not or implements different interfaces, the proxy will need
     * to be re-obtained or the old interfaces won't be supported and the new interface
     * won't be implemented.
     * @param a the advisor to replace
     * @param b the advisor to replace it with
     * @return whether it was replaced. If the advisor wasn't found in the
     * list of advisors, this method returns {@code false} and does nothing.
     * @throws AopConfigException in case of invalid advice
     */
    boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

    /**
     * Add the given AOP Alliance advice to the tail of the advice (interceptor) chain.
     * <p>This will be wrapped in a DefaultPointcutAdvisor with a pointcut that always
     * applies, and returned from the {@code getAdvisors()} method in this wrapped form.
     * <p>Note that the given advice will apply to all invocations on the proxy,
     * even to the {@code toString()} method! Use appropriate advice implementations
     * or specify appropriate pointcuts to apply to a narrower set of methods.
     * @param advice advice to add to the tail of the chain
     * @throws AopConfigException in case of invalid advice
     * @see #addAdvice(int, Advice)
     * @see org.springframework.aop.support.DefaultPointcutAdvisor
     */
    void addAdvice(Advice advice) throws AopConfigException;

    /**
     * Add the given AOP Alliance Advice at the specified position in the advice chain.
     * <p>This will be wrapped in a {@link org.springframework.aop.support.DefaultPointcutAdvisor}
     * with a pointcut that always applies, and returned from the {@link #getAdvisors()}
     * method in this wrapped form.
     * <p>Note: The given advice will apply to all invocations on the proxy,
     * even to the {@code toString()} method! Use appropriate advice implementations
     * or specify appropriate pointcuts to apply to a narrower set of methods.
     * @param pos index from 0 (head)
     * @param advice advice to add at the specified position in the advice chain
     * @throws AopConfigException in case of invalid advice
     */
    void addAdvice(int pos, Advice advice) throws AopConfigException;

    /**
     * Remove the Advisor containing the given advice.
     * @param advice the advice to remove
     * @return {@code true} of the advice was found and removed;
     * {@code false} if there was no such advice
     */
    boolean removeAdvice(Advice advice);

    /**
     * Return the index (from 0) of the given AOP Alliance Advice,
     * or -1 if no such advice is an advice for this proxy.
     * <p>The return value of this method can be used to index into
     * the advisors array.
     * @param advice AOP Alliance advice to search for
     * @return index from 0 of this advice, or -1 if there's no such advice
     */
    int indexOf(Advice advice);

    /**
     * As {@code toString()} will normally be delegated to the target,
     * this returns the equivalent for the AOP proxy.
     * @return a string description of the proxy configuration
     */
    String toProxyConfigString();

}
View Code

  該接口主要定義了代理類的工廠基本的行為,比如說添加Advisor,添加Advise,刪除與替換Adivsor等

  Adivise:

  通知接口,該接口沒有方法定義,其常見的子接口有BeforeAdvise,AfterAdvise,MethodInterceptor等

  PointCut:

  切點接口,該接口定義如下:

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop;

/**
 * Core Spring pointcut abstraction.
 *
 * <p>A pointcut is composed of a {@link ClassFilter} and a {@link MethodMatcher}.
 * Both these basic terms and a Pointcut itself can be combined to build up combinations
 * (e.g. through {@link org.springframework.aop.support.ComposablePointcut}).
 *
 * @author Rod Johnson
 * @see ClassFilter
 * @see MethodMatcher
 * @see org.springframework.aop.support.Pointcuts
 * @see org.springframework.aop.support.ClassFilters
 * @see org.springframework.aop.support.MethodMatchers
 */
public interface Pointcut {

    /**
     * Return the ClassFilter for this pointcut.
     * @return the ClassFilter (never {@code null})
     */
    ClassFilter getClassFilter();

    /**
     * Return the MethodMatcher for this pointcut.
     * @return the MethodMatcher (never {@code null})
     */
    MethodMatcher getMethodMatcher();


    /**
     * Canonical Pointcut instance that always matches.
     */
    Pointcut TRUE = TruePointcut.INSTANCE;

}
View Code

  Pointcut由ClassFilter和MethodMatcher構成。它通過ClassFilter定位到某些特定類上,通過MethodMatcher定位到某些特定方法上,這樣Pointcut就擁有了描述某些類的某些特定方法的能力。

  Advisor:

  代表一般切面,它僅包含一個Advice,我們說過,因為Advice包含了橫切代碼和連接點的信息,所以Advior本身就是一個簡單的切面,只不過它代表的橫切的連接點是所有目標類的所有方法,因為這個橫切面太寬泛,所以一般不會直接使用。

2.2、源碼分析

  我們先來看看ProxyFactoryBean的相關方法

  getObject方法:

/**
     * 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()} for a proxy.
     * @return a fresh AOP proxy reflecting the current state of this factory
     */
    @Override
    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();
        }
    }

  這里面我們關注一下getSingletonInstance方法:

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

  在這里我們關在關注一下getProxy(AopProxy aopProxy)方法,AopProxy是一個接口:

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

/**
 * Delegate interface for a configured AOP proxy, allowing for the creation
 * of actual proxy objects.
 *
 * <p>Out-of-the-box implementations are available for JDK dynamic proxies
 * and for CGLIB proxies, as applied by {@link DefaultAopProxyFactory}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see DefaultAopProxyFactory
 */
public interface AopProxy {

    /**
     * Create a new proxy object.
     * <p>Uses the AopProxy's default class loader (if necessary for proxy creation):
     * usually, the thread context class loader.
     * @return the new proxy object (never {@code null})
     * @see Thread#getContextClassLoader()
     */
    Object getProxy();

    /**
     * Create a new proxy object.
     * <p>Uses the given class loader (if necessary for proxy creation).
     * {@code null} will simply be passed down and thus lead to the low-level
     * proxy facility's default, which is usually different from the default chosen
     * by the AopProxy implementation's {@link #getProxy()} method.
     * @param classLoader the class loader to create the proxy with
     * (or {@code null} for the low-level proxy facility's default)
     * @return the new proxy object (never {@code null})
     */
    Object getProxy(ClassLoader classLoader);

}
View Code

  該接口有如下實現類:JdkDynamicAopProxy , CglibAopProxy , ObjenesisCglibAopProxy。

  那么在這里我們看一下JdkDynamicAopProxy的源碼,我只貼出其中一個關鍵部分:

@Override
    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, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

  那么我們可以看出:JDK的動態代理是AOP的實現方式之一

    

三、基於AOP的核心類與接口實現代理

1、先定義基本的JavaBean:

package org.hzgj.spring.study;

@Aop
public class Water {

    private int capacity;

    public int getCapacity() {
        return capacity;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    public void test() {
        System.out.println("test");
    }

    @Aop
    public void test1() {
        System.out.println("test1");
    }
}
View Code

2、自定義注解

package org.hzgj.spring.study;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Aop {
}
View Code

3、定義JavaBean的代理

package org.hzgj.spring.study;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.stereotype.Component;


/**
 * 
 */
@Component
public class WaterProxyFactoryBean extends AbstractSingletonProxyFactoryBean {


    public WaterProxyFactoryBean() {
        super.setTarget(new Water());
    }

    @Override
    protected Object createMainInterceptor() {
        AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(Aop.class, Aop.class);
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, (MethodInterceptor) invocation -> {
            System.out.println(1);
            return invocation.proceed();
        });

        return advisor;
    }
}
View Code

  該類繼承AbstractSingletonProxyFactoryBean,然后需要重寫createMainInterceptor,我在這里定義了一個DefaultPointcutAdvisor與掃描注解的PointCut,至此切點,通知,代理都有了,那么AOP最基本的條件也就具備了

4、主程序

package org.hzgj.spring.study;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
    

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        Water water = applicationContext.getBean(Water.class);
        water.test1();
    }
}
View Code

  運行成功時會得到如下結果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM