文档章节

Spring如何实现AOP,请不要再说cglib了!

温安适
 温安适
发布于 10/20 16:16
字数 2210
阅读 2404
收藏 39

1. 从注解入手找到对应核心类

最近工作中我都是基于注解实现AOP功能,常用的开启AOP的注解是@EnableAspectJAutoProxy,我们就从它入手。

上面的动图的流程的步骤就是:
@EnableAspectJAutoProxy
--> AspectJAutoProxyRegistrar
-->AopConfigUtils .registerAspectJAnnotationAutoProxyCreatorIfNecessary
-->AnnotationAwareAspectJAutoProxyCreator.class

AnnotationAwareAspectJAutoProxyCreator查看其中文注释(如下),确定它就是AOP的核心类!--温安适 20191020

/**
1.AspectJAwareAdvisorAutoProxyCreator的子类
,用于处理当前应用上下文中的注解切面
2.任何被AspectJ注解的类将自动被识别。
3.若SpringAOP代理模式可以识别,优先使用Spring代理模式。
4.它覆盖了方法执行连接点
5.如果使用<aop:include>元素,
  则只有名称与include模式匹配的@aspectj bean才被视为切面
  ,并由spring自动代理。
6. Spring Advisors的处理请查阅,
org.springframework.aop
.framework.autoproxy.AbstractAdvisorAutoProxyCreator
 */
@SuppressWarnings("serial")
public class AnnotationAwareAspectJAutoProxyCreator 
extends AspectJAwareAdvisorAutoProxyCreator {
    //...省略实现
    }注解切面

虽然找到了核心类,但是并没有找到核心方法!下面我们尝试画类图确定核心方法。

2.画核心类类图,猜测核心方法

AnnotationAwareAspectJAutoProxyCreator的部分类图。
AnnotationAwareAspectJAutoProxyCreator
从类图看到了AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor,而AOP功能应该在创建完Bean之后执行,猜测AnnotationAwareAspectJAutoProxyCreator实现BeanPostProcessor的postProcessAfterInitialization(实例化bean后处理)是核心方法。 查看AnnotationAwareAspectJAutoProxyCreator实现的postProcessAfterInitialization方法,实际该方法在其父类AbstractAutoProxyCreator中。

//AbstractAutoProxyCreator中的postProcessAfterInitialization实现
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
 throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

发现发现疑似方法wrapIfNecessary,查看其源码如下,发现createProxy方法。确定找对了地方。

protected Object wrapIfNecessary
        (Object bean, String beanName, Object cacheKey) {
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   if (isInfrastructureClass(bean.getClass()) 
                   || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // 创建代理
   Object[] specificInterceptors = 
       getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      Object proxy = createProxy(
            bean.getClass(), beanName, 
            specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

即AnnotationAwareAspectJAutoProxyCreator实现BeanPostProcessor的postProcessAfterInitialization方法,在该方法中由wrapIfNecessary实现了AOP的功能。 wrapIfNecessary中有2个和核心方法

  • getAdvicesAndAdvisorsForBean获取当前bean匹配的增强器
  • createProxy为当前bean创建代理
    要想明白核心流程还需要分析这2个方法。

3.读重点方法,理核心流程

3.1 getAdvicesAndAdvisorsForBean获取当前bean匹配的增强器

查看源码如下,默认实现在AbstractAdvisorAutoProxyCreator中。

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, 
      @Nullable TargetSource targetSource) {
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}

查阅findEligibleAdvisors方法,就干了3件事

  • 找所有增强器,也就是所有@Aspect注解的Bean
  • 找匹配的增强器,也就是根据@Before,@After等注解上的表达式,与当前bean进行匹配,暴露匹配上的。
  • 对匹配的增强器进行扩展和排序,就是按照@Order或者PriorityOrdered的getOrder的数据值进行排序,越小的越靠前。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   //找所有增强器
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //找所有匹配的增强器
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
       //排序
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

AnnotationAwareAspectJAutoProxyCreator 重写了findCandidateAdvisors,下面我们看看具体实现了什么

3.1.1findCandidateAdvisors找所有增强器,也就是所有@Aspect注解的Bean

@Override
protected List<Advisor> findCandidateAdvisors() {
   // Add all the Spring advisors found according to superclass rules.
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
   if (this.aspectJAdvisorsBuilder != null) {
      //@Aspect注解的类在这里除了
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   return advisors;
}

从该方法我们可以看到处理@Aspect注解的bean的方法是:this.aspectJAdvisorsBuilder.buildAspectJAdvisors()。 这个方法如下:

public List<Advisor> buildAspectJAdvisors() {
   List<String> aspectNames = this.aspectBeanNames;

   if (aspectNames == null) {
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            List<Advisor> advisors = new ArrayList<>();
            aspectNames = new ArrayList<>();
            //找到所有BeanName
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true, false);
            for (String beanName : beanNames) {
               if (!isEligibleBean(beanName)) {
                  continue;
               }
               // 必须注意,bean会提前暴露,并被Spring容器缓存,但是这时还不能织入。
               Class<?> beanType = this.beanFactory.getType(beanName);
               if (beanType == null) {
                  continue;
               }
               if (this.advisorFactory.isAspect(beanType)) {
                  //找到所有被@Aspect注解的类
                  aspectNames.add(beanName);
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                     //解析封装为Advisor返回
                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                     if (this.beanFactory.isSingleton(beanName)) {
                        this.advisorsCache.put(beanName, classAdvisors);
                     }
                     else {
                        this.aspectFactoryCache.put(beanName, factory);
                     }
                     advisors.addAll(classAdvisors);
                  }
                  else {
                     // Per target or per this.
                     if (this.beanFactory.isSingleton(beanName)) {
                        throw new IllegalArgumentException("Bean with name '" + beanName +
                              "' is a singleton, but aspect instantiation model is not singleton");
                     }
                     MetadataAwareAspectInstanceFactory factory =
                           new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                     this.aspectFactoryCache.put(beanName, factory);
                     advisors.addAll(this.advisorFactory.getAdvisors(factory));
                  }
               }
            }
            this.aspectBeanNames = aspectNames;
            return advisors;
         }
      }
   }

   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   List<Advisor> advisors = new ArrayList<>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}

这个方法可以概括为:

  • 找到所有BeanName
  • 根据BeanName筛选出被@Aspect注解的类
  • 针对类中被Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class注解的方法,先按上边的注解顺序排序后按方法名称排序,每一个方法对应一个Advisor。

3.2 createProxy为当前bean创建代理。

3.2.1 创建代理的2种方式

众所周知,创建代理的常用的2种方式是:JDK创建和CGLIB,下面我们就看看这2中创建代理的例子。

3.2.1 .1 jdk创建代理的例子

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyMain {

    public static void main(String[] args) {
        JDKProxyTestInterface target = new JDKProxyTestInterfaceImpl();
        // 根据目标对象创建代理对象
        JDKProxyTestInterface proxy =
         (JDKProxyTestInterface) Proxy
         .newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), 
                new JDKProxyTestInvocationHandler(target));
        // 调用代理对象方法
        proxy.testProxy();
    }

    interface JDKProxyTestInterface {
        void testProxy();
    }
    static class JDKProxyTestInterfaceImpl 
                    implements JDKProxyTestInterface {
        @Override
        public void testProxy() {
            System.out.println("testProxy");
        }
    }
   static class JDKProxyTestInvocationHandler 
                           implements InvocationHandler {
        private  Object target;
        public JDKProxyTestInvocationHandler(Object target){
            this.target=target;
        }
        @Override
        public Object invoke(Object proxy, Method method,
                             Object[] args) throws Throwable {
            System.out.println("执行前");
            Object result=  method.invoke(this.target,args);
            System.out.println("执行后");
            return result;
        }
    }

3.2.1 .2 cglib创建代理的例子

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyTest {

   static class CglibProxyService {
        public  CglibProxyService(){
        }
        void sayHello(){
            System.out.println(" hello !");
        }
    }

    static class CglibProxyInterceptor implements MethodInterceptor{
        @Override
        public Object intercept(Object sub, Method method,
             Object[] objects, MethodProxy methodProxy)
                                          throws Throwable {
            System.out.println("before hello");
            Object object = methodProxy.invokeSuper(sub, objects);
            System.out.println("after hello");
            return object;
        }
    }

    public static void main(String[] args) {
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(CglibProxyService.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new CglibProxyInterceptor());
        // 创建代理对象
        CglibProxyService proxy= (CglibProxyService)enhancer.create();
        System.out.println(CglibProxyService.class);
        System.out.println(proxy.getClass());
        // 通过代理对象调用目标方法
        proxy.sayHello();
    }
}

3.2.1 .3 jdk创建代理与cglib创建代理的区别

类型 jdk创建动态代理 cglib创建动态代理
原理 java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理 cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
核心类 Proxy 创建代理利用反射机制生成一个实现代理接口的匿名类InvocationHandler 方法拦截器接口,需要实现invoke方法 net.sf.cglib.proxy.Enhancer:主要增强类,通过字节码技术动态创建委托类的子类实例net.sf.cglib.proxy.MethodInterceptor:方法拦截器接口,需要实现intercept方法
局限性 只能代理实现了接口的类 不能对final修饰的类进行代理,也不能处理final修饰的方法

3.2.2 Spring如何选择的使用哪种方式

Spring的选择选择如何代理时在DefaultAopProxyFactory 中。

public class DefaultAopProxyFactory implements AopProxyFactory, 
                                                        Serializable {
   @Override
   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);
      }
   }
   //...
   }
  • config.isOptimize() 查看源码注释时发现,这个是配置使用cglib代理时,是否使用积极策略。这个值一般不建议使用!
  • config.isProxyTargetClass() 就是@EnableAspectJAutoProxy中的proxyTargetClass属性。

//exposeProxy=true AopContext 可以访问,proxyTargetClass=true CGLIB生成代理 @EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)

  • hasNoUserSuppliedProxyInterfaces 是否存在可代理的接口

总结下Spring如何选择创建代理的方式:

  1. 如果设置了proxyTargetClass=true,一定是CGLIB代理
  2. 如果proxyTargetClass=false,目标对象实现了接口,走JDK代理
  3. 如果没有实现接口,走CGLIB代理

4.总结

Spring如何实现AOP?,您可以这样说:

  1. AnnotationAwareAspectJAutoProxyCreator是AOP核心处理类
  2. AnnotationAwareAspectJAutoProxyCreator实现了BeanProcessor,其中postProcessAfterInitialization是核心方法。
  3. 核心实现分为2步
    getAdvicesAndAdvisorsForBean获取当前bean匹配的增强器 createProxy为当前bean创建代理
  4. getAdvicesAndAdvisorsForBean核心逻辑如下
    a. 找所有增强器,也就是所有@Aspect注解的Bean
    b. 找匹配的增强器,也就是根据@Before,@After等注解上的表达式,与当前bean进行匹配,暴露匹配上的。
    c. 对匹配的增强器进行扩展和排序,就是按照@Order或者PriorityOrdered的getOrder的数据值进行排序,越小的越靠前。
  5. createProxy有2种创建方法,JDK代理或CGLIB
    a. 如果设置了proxyTargetClass=true,一定是CGLIB代理
    b. 如果proxyTargetClass=false,目标对象实现了接口,走JDK代理
    c. 如果没有实现接口,走CGLIB代理

© 著作权归作者所有

温安适

温安适

粉丝 118
博文 32
码字总数 53884
作品 0
朝阳
后端工程师
私信 提问
加载中

评论(11)

J
JiuHuang
标题党啊
温安适
温安适 博主
也有干活
kakai
kakai
标题有点浮夸,明明还是jdk proxy+cglib,为什么“请不要说cglib”呢?更确切的说spring还用了AspectJ的注解类库,因为AspectJ完整是借助静态编译的。总结的话AspectJ注解+jdk proxy+cglib构建了spring动态切面功能。
温安适
温安适 博主
这个是我面试别人时,他们的回答,也却又吸引人的作用😀
JPer
JPer
jdk+cglib,楼主gif图好评
温安适
温安适 博主
谢谢
s
stys35
jdk,cglib
f
freezingsky
如果有人问我, spring的AOP实现, 我绝对不会回答具体到某些类名以及详细的创建过程 . 但我会明确告诉他, AOP的基本实现原理 以及 哪些可选的AOP实现技术.
温安适
温安适 博主
您说的也有道理!每个人理解不同!这就是我认为的AOP实现。
f
freezingsky
一码归一码啦, 文章写得还是好. 相当于spring的源码赏析.
温安适
温安适 博主
谢谢
Sring如何选择JDK动态代理与CGLIB字节码增强

Spring将事务代理工厂TransactionProxyFactoryBean或自动代理拦截器BeanNameAutoProxyCreator的proxyTargetClass属性,设置为true,则使用CGLIB代理,此属性默认为false,使用JDK动态代理。 Spri...

无语年华
2018/09/11
102
0
Java设计模式综合运用(动态代理+Spring AOP)

AOP设计模式通常运用在日志,校验等业务场景,本文将简单介绍基于Spring的AOP代理模式的运用。 1. 代理模式 1.1 概念 代理(Proxy)是一种提供了对目标对象另外的访问方式,即通过代理对象访问...

landy8530
2018/10/05
0
0
spring基础知识---AOP动态代理原理

Spring Boot实践——Spring AOP实现之动态代理 Spring AOP 介绍   AOP的介绍可以查看 Spring Boot实践——AOP实现   与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理...

spinachgit
02/21
231
0
Spring中JDK动态代理和Cglib代理的区别

Spring的AOP基于代理模式,并且它即使用到了JDK动态代理也使用了Cglib代理。 如下代码是springAOP中默认的代理实现类DefaultAopProxyFactory,其中创建代理的方法createAopProxy,在这个方法...

王子城
2018/08/01
1K
0
Spring AOP 创建代理的源码解析

相关文章 Spring AOP 注解方式源码解析 Spring AOP 功能使用详解 Spring 的 getBean 方法源码解析 Spring bean 创建过程源码解 Spring 中 bean 注册的源码解析 前言 在上篇文章 Spring AOP 注...

TSMYK
01/01
190
0

没有更多内容

加载失败,请刷新页面

加载更多

前端的一些雕虫小技,从100%和滚动条说起

1、100%和滚动条 当我们在css中把html和body同时设为100%时,会出现滚动条 html, body { width: 100%; height: 100%; } 原因是html和b...

wphmoon
32分钟前
6
0
电力区块链应用案例【2019】

随着区块链技术的日益普及,出现了大量创业企业尝试使用区块链技术来解决能源与电力行业中存在的问题。在本文中,我们将介绍其中的三个能源区块链项目。 能源行业以价格不透明著称:消费者很...

汇智网教程
54分钟前
7
0
聊聊rocketmq的adjustThreadPoolNumsThreshold

序 本文主要研究一下rocketmq的adjustThreadPoolNumsThreshold DefaultMQPushConsumer rocketmq-client-4.5.2-sources.jar!/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.ja......

go4it
今天
9
0
关于早起

早起是非常好的事情,但是像如果前一天睡得晚,或者第二天早上是非常冷的时候,那就不是很美好了。 但是本身早起是一件非常棒的事情,我记得我每次早起 如果不觉得困的话,世界是那么安静,脑...

T型人才追梦者
今天
7
0
Java输入输出

JDK中的InputStream/OutputStream构成了IO输入输出继承层次的基础。它们都是面向字节序列的,每次可以从序列中读入或者写出一个字节或者指定大小的字节数组。但是面向字节流的输入输出不便于...

ytuan996
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部