美文网首页Spring
Spring AOP之AopProxy代理对象

Spring AOP之AopProxy代理对象

作者: 九点半的马拉 | 来源:发表于2019-09-26 20:38 被阅读0次

在Spring的AOP模块,一个主要的部分是代理对象的生成,可以通过ProxyFactoryBean来完成,它封装了主要代理对象的生成过程。在这个生成过程中,可以使用JDK的Proxy和CGLIB两种生成情况。

JDK动态代理与CGLIB区别

  1. jdk动态代理是利用反射机制生成的一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  2. CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
  3. 如果目标对象实现了接口,默认情况下采用JDK的动态代理实现AOP,但可以强制性使用CGLIB实现AOP
  4. 如果目标对象没有实现接口,必须采用cglib库,

Tip: 什么是匿名类
即没有名称的类,其名称由Java编译器给出,一般为:外部类名称+$+匿名类顺序,名称也就是其他地方不能引用,不能实例化,只用一次,当然也就不能有构造器。

  • 匿名类可以继承父类的方法,也可以重写父类的方法。
  • 匿名类可以访问外部类的成员变量和方法,匿名类的类体不可以声明为static成员变量和static方法。
  • 匿名类由于是一个new的结果,所以其实可以赋值给一个父类对象。

配置ProxyFactoryBean

  1. 定义通知器Advisor,这个通知器应该作为一个Bean来定义,定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
  2. 定义ProxyFactoryBean,作为另一个Bean来定义,它是封装AOP功能的主要类。需要设定相关属性。
  • interceptorNames: 设置为需要定义的通知器,要通过使用代理对象的拦截器机制起作用的。
  • target: 是需要用AOP通知器中的切面应用来增强的对象。

生成AopProxy代理对象

在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备。从FactoryBean中获取对象,是以getObject()方法作为入口完成的。在该方法中,首先对通知器链进行初始化,封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。在生成代理对象时,因为Spring中有singleton类型和prototype类型这两种不同的Bean,所以要对代理对象的生成做一个区分。

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

首先为Proxy代理对象配置Advisor链,在initializeAdvisorChain()方法中执行。
在该方法中它会首先通过this.advisorChainInitialized来判断通知器链是否已经初始化了,如果已经初始化了,就直接返回。其他情况下,通过this.interceptorNames来要添加的通知器名,然后通过该名从IOC容器中取得的通知器加入到拦截器链中。

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

生成singleton的代理对象在getSingletonInstance()中完成
如果它还没有被创建,则lazily creating

在Spring代理目标target时,其实并不是直接创建一个目标target的对象实例的,而是通过一个TargetSource类型的对象对目标target进行封装,Spring Aop获取目标对象始终是通过TargetSource.getTarget()方法进行的。

proxy(代理对象)代理的不是target,而是TargetSource

那么问题来了:为什么SpringAOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢?

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

Spring内置了多种TargetSource

  • SingletonTargetSource
    从这个目标源取得的目标对象是单例的,成员变量target缓存了目标对象,每次getTarget()都是返回这个对象。
  • PrototypeTargetSource
    每次getTarget()将生成prototype类型的bean,即其生成的bean并不是单例的,因而使用这个类型的TargetSource时需要注意,封装的目标bean必须是prototype类型的。PrototypeTargetSource继承了AbstractBeanFactoryBasedTargetSource拥有了创建bean的能力。
  • CommonsPool2TargetSource
    里CommonsPool2TargetSource也就是池化的TargetSource,其基本具有平常所使用的“池”的概念的所有属性,比如:最小空闲数,最大空闲数,最大等待时间等等.
  • ThreadLocalTargetSource
    ThreadLocalTargetSource也就是和线程绑定的TargetSource,可以理解,其底层实现必然使用的是ThreadLocal

在上面简单介绍了有关TargetSource的有关知识,接下来将对getSingletonInstance()方法的有关步骤进行解释。

  • this.targetSource = freshTargetSource() 返回要在创建代理时使用的TargetSource.
    如果在interceptorNames列表的末尾没有指定目标,TargetSource将是该类的TargetSource成员。
    否则,我们将获得目标bean,并在必要时将其封装在TargetSource中。
private TargetSource freshTargetSource() {
   if (this.targetName == null) {
      if (logger.isTraceEnabled()) {
         logger.trace("Not refreshing target: Bean name not specified in 'interceptorNames'.");
  }
      return this.targetSource;
  }
   else {
      if (this.beanFactory == null) {
         throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
               "- cannot resolve target with name '" + this.targetName + "'");
  }
      if (logger.isDebugEnabled()) {
         logger.debug("Refreshing target with name '" + this.targetName + "'");
  }
      Object target = this.beanFactory.getBean(this.targetName);
 return (target instanceof TargetSource ? (TargetSource) target : new SingletonTargetSource(target));
  }
}
  • Class<?> targetClass = getTargetClass(); 根据AOP框架来判断需要代理的接口
@Override @Nullable public Class<?> getTargetClass() {
   return this.targetSource.getTargetClass(); }
  • setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader)); 这里设置代理对象的接口

  • super.setFrozen(this.freezeProxy); 初始化共享单例实例 ,当一个配置被冻结时,不能对advice进行更改

  • this.singletonInstance = getProxy(createAopProxy()); 通过createAopProxy返回的AopProxy来生成需要的Proxy

protected Object getProxy(AopProxy aopProxy) {
   return aopProxy.getProxy(this.proxyClassLoader); }
protected final synchronized AopProxy createAopProxy() {
   if (!this.active) {
      activate();
  }
   return getAopProxyFactory().createAopProxy(this); }

监听调用AdvisedSupportListener实现类的activated方法

private void activate() {
   this.active = true;
 for (AdvisedSupportListener listener : this.listeners) {
      listener.activated(this);
  }
}

具体的代理对象的生成,是在ProxyFactoryBean的基类AdvisedSupport的实现中借助AopProxyFactory完成的,这个代理对象要么从JDK中生成,要么借助CGLIB获得。

这个AopProxyFactory是在初始化函数中定义的,使用的是DefaultAopProxyFactor。

  • createAopProxy(AdvisedSupport config)
    在该方法中会判断采用不同的策略来生成AopProxy

如果targetClass是接口类,使用JDK来生成Proxy
如果不是接口类要生成Proxy,那么使用CGLIB来生成。

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() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
  }
      return new ObjenesisCglibAopProxy(config);
  }
   else {
      return new JdkDynamicAopProxy(config);
  }
}

接下来分别介绍两种不同的方式来产生AopProxy代理对象

  1. JdkDynamicAopProxy
@Override public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isDebugEnabled()) {
      logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
  }
  # 获取AdvisedSupport类型对象的所有接口
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   # 接口是否定义了 equals和hashcode方法 正常是没有的
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

首先从advised对象中取得代理对象的代理接口配置,然后调用Proxy的newProxyInstance方法,得到最终的Proxy代理对象。

在生成代理对象时,需要指明三个参数,类加载器,代理接口和Proxy回调方法所在的对象。
在回调方法所在对象中,需要实现InvocationHandler接口,它定义了invoke方法,
对于JdkDynamimcAopProxy,它本身实现了InvocationHandler接口和invoke方法,这个invoke方法是Proxy代理对象的回调方法。

  1. CGLIB生成代理对象

在该篇文章中就不讲解了,感兴趣的可以百度搜索。

注:本文大多数是对《Spring技术内幕》的阅读整理。

相关文章

  • Spring AOP之AopProxy代理对象

    在Spring的AOP模块,一个主要的部分是代理对象的生成,可以通过ProxyFactoryBean来完成,它封装...

  • Spring AOP内部调用失效问题

    Spring AOP基本原理 Spring AOP是基于动态代理机制实现的,通过动态代理机制生成目标对象的代理对象...

  • AOP代理:

    AOP代理:AOP框架创建的对象,代理就是对目标对象的增强。Spring中的AOP代理可以是JDK动态代理,也可以...

  • Spring:DynamicAdvisedInterceptor

    对于不同的AopProxy代理对象生成方式,会使用不同的拦截回调入口。1、对于JDK的AopProxy代理对象,使...

  • Spring AOP从原理到源码(三)

    接着上一节Spring AOP从原理到源码(二),本节关注spring aop创建代理对象的过程。 Spring ...

  • spring aop 汇总

    静态代理、动态代理和cglib代理 aop 使用 Spring AOP - 注解方式使用介绍spring aop ...

  • spring-AOP(二) 自动代理

    spring-AOP(二) 自动代理 知识导读 在何时何处创建代理对象,如何能覆盖被代理对象,返回代理后的对象 自...

  • spring AOP

    1,原理:Spring AOP底层原理采用运行时生成动态代理的方式来增强目标对象。AOP 代理其实是由 AOP 框...

  • spring aop获取代理目标对象

    spring aop获取代理目标对象 转载: https://coget.cn/archives/spring%2...

  • Spring的两种代理方式

    AOP是Spring的重要组成部分,而AOP正是通过代理实现的。如果代理对象实现了接口,则默认使用jdk动态代理,...

网友评论

    本文标题:Spring AOP之AopProxy代理对象

    本文链接:https://www.haomeiwen.com/subject/vbcyyctx.html