• spring---aop(6)---Spring AOP中ProxyFactoryBean介绍


    写在前面

      这篇文章里面就要说说Spring自己的AOP,搞清楚哪种方式是Spring自己实现的AOP,哪种方式是Spring引入aspectj的AOP。 

    简单例子

      Spring自己的AOP实现在于ProxyFactoryBean。先看下使用案例(仍和之前的案例是一样的):接口AService、实现类AServiceImpl、通知MyBeforeAdvice 。

    public interface AService {
        public void barA(); 
        public void barB(); 
    }
    public class AServiceImpl implements AService{
        @Override
        public void barA() {
             System.out.println("AServiceImpl.barA()");  
             this.barB();
        }
        @Override
        public void barB() {
            System.out.println("AServiceImpl.barB()");  
        }
    }
    public class MyBeforeAdvice implements MethodBeforeAdvice{
        @Override
        public void before(Method method, Object[] args, Object target)
                throws Throwable {
            System.out.println("run my before advice");
        }
    }

      然后就是xml的配置: 

        <bean id="aServiceImpl" class="com.xxx.plus.aop.demo.AServiceImpl"/>
        <bean id="myBeforAdvice" class="com.xxx.plus.aop.demo.MyBeforeAdvice"/>
        <bean id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="com.xxx.plus.aop.demo.AService"/>
            <property name="target">
                <ref bean="aServiceImpl"/>
            </property>
             <property name="interceptorNames">  
                <list>  
                    <value>myBeforAdvice</value>  
                </list>  
            </property>  
        </bean>

      然后就可以使用了: 

        @Resource
        private AService aService;
        
        @Test
        public void testAOP(){
            aService.barA();
        }

      运行这个单元测试,然后你就会看到报如下错误: 

    No qualifying bean of type [com.xxx.aop.service.AService] is defined: expected single matching bean but found 2: aServiceImpl,org.springframework.aop.framework.ProxyFactoryBean#0

      原因就是对于接口AService,有两个实现类aServiceImpl和ProxyFactoryBean所生产的代理类。所以我们不能使用@Resource(它是按类型注入),所以要使用按名称注入,我们怎么获取ProxyFactoryBean所产生的代理类的名称呢?其实就是ProxyFactoryBean配置的名称。因为ProxyFactoryBean实现了FactoryBean接口,对于这种接口从容器中获取该bean,不是获取的本身而是获取他的getObject方法所返回的值,看FactoryBean的文档: 

    /**
     * Interface to be implemented by objects used within a {@link BeanFactory}
     * which are themselves factories. 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 application components as well; however,
     * this is not common outside of infrastructure code.
     *
     * <p><b>NOTE:</b> 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).
    
     */
    public interface FactoryBean<T> {}

      所以通过beanName找到了ProxyFactoryBean,然而不是返回该对象,而是返回他的getObject方法的返回值,所以我们通过ProxyFactoryBean的id就可以获取到它所产生的代理对象,所以更改如下: 

    <bean  id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    @Resource(name="aServiceImplProxy")
    private AService aService;

      然后就可以正常运行了如下: 

    run my before advice
    AServiceImpl.barA()

    源代码解读

      然后我们就要源码分析下这一过程,先看下是如何产生代理对象的,在ProxyFactoryBean的getObject方法中:

    public class ProxyFactoryBean extends ProxyCreatorSupport
            implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
        @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();
            }
        }
    }

      重点1:就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。

      this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。然后就是将interceptorNames转化成Advisor。根据interceptorNames所包含的字符串到容器中进行查找,如果含有*则,则表示进行一定的匹配,符合的都会纳入。

      如下: 

        private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
            if (this.advisorChainInitialized) {
                return;
            }
    
            if (!ObjectUtils.isEmpty(this.interceptorNames)) {
                if (this.beanFactory == null) {
                    throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
                            "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
                }
    
                // Globals can't be last unless we specified a targetSource using the property...
                if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                        this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                    throw new AopConfigException("Target required after globals");
                }
    
                // Materialize interceptor chain from bean names.
                for (String name : this.interceptorNames) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Configuring advisor or advice '" + name + "'");
                    }
    
                    if (name.endsWith(GLOBAL_SUFFIX)) {
                        if (!(this.beanFactory instanceof ListableBeanFactory)) {
                            throw new AopConfigException(
                                    "Can only use global advisors or interceptors with a ListableBeanFactory");
                        }
                        addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                                name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                    }
    
                    else {
                        // If we get here, we need to add a named interceptor.
                        // We must check if it's a singleton or prototype.
                        Object advice;
                        if (this.singleton || this.beanFactory.isSingleton(name)) {
                            // Add the real Advisor/Advice to the chain.
                            advice = this.beanFactory.getBean(name);
                        }
                        else {
                            // It's a prototype Advice or Advisor: replace with a prototype.
                            // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                            advice = new PrototypePlaceholderAdvisor(name);
                        }
                        addAdvisorOnChainCreation(advice, name);
                    }
                }
            }
    
            this.advisorChainInitialized = true;
        }

      这中间页经过了Advice到Advisor的转换,如下: 

        private void addAdvisorOnChainCreation(Object next, String name) {
            // We need to convert to an Advisor if necessary so that our source reference
            // matches what we find from superclass interceptors.
            Advisor advisor = namedBeanToAdvisor(next);
            if (logger.isTraceEnabled()) {
                logger.trace("Adding advisor with name '" + name + "'");
            }
            addAdvisor(advisor);
        }
        private Advisor namedBeanToAdvisor(Object next) {
            try {
                return this.advisorAdapterRegistry.wrap(next);
            }
            catch (UnknownAdviceTypeException ex) {
                // We expected this to be an Advisor or Advice,
                // but it wasn't. This is a configuration error.
                throw new AopConfigException("Unknown advisor type " + next.getClass() +
                        "; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
                        "which may also be target or TargetSource", ex);
            }
        }
        public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
            if (adviceObject instanceof Advisor) {
                return (Advisor) adviceObject;
            }
            if (!(adviceObject instanceof Advice)) {
                throw new UnknownAdviceTypeException(adviceObject);
            }
            Advice advice = (Advice) adviceObject;
            if (advice instanceof MethodInterceptor) {
                // So well-known it doesn't even need an adapter.
                return new DefaultPointcutAdvisor(advice);
            }
            for (AdvisorAdapter adapter : this.adapters) {
                // Check that it is supported.
                if (adapter.supportsAdvice(advice)) {
                    return new DefaultPointcutAdvisor(advice);
                }
            }
            throw new UnknownAdviceTypeException(advice);
        }
    public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
        public DefaultPointcutAdvisor(Advice advice) {
            this(Pointcut.TRUE, advice);
        }
    }

      这个包裹过程已经见过很多遍了,采用了适配器的模式。

      之后又是和其他的AOP方式接轨了,设置一些列要实现的接口和参数,使用DefaultAopProxyFactory先创建出AopProxy,要么是JdkDynamicAopProxy,要么是CglibAopProxy,然后就可以调用AopProxy的getProxy方法来获取代理对象了

      这种方式实现的AOP还是比较麻烦的,同时配置一个ProxyFactoryBean仅能实现对一个目标对象的拦截,要想拦截多个目标对象,需要配置多个ProxyFactoryBean。所以大部分还是使用Spring引进的aspectj的AOP方式来进行AOP编程。 

  • 相关阅读:
    Unix环境高级编程—进程关系
    如何学习CCIE
    Unix环境高级编程—进程控制(三)
    Unix环境高级编程—进程控制(二)
    _THROW 何解?
    Unix高级环境编程—进程控制(一)
    ifndef/define/endif 和 #ifdef 、#if 作用和用法
    内存MCE错误导致暴力扩充messages日志 以及chattr记录
    Intellij IDEA的安装和激活
    Xshell和Xftp间隔一段时间不操作就自动断开连接?
  • 原文地址:https://www.cnblogs.com/chihirotan/p/7352895.html
Copyright © 2020-2023  润新知