范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

随手记AOP如何避开BeanNotOfRequiredTypeException及CGLIB

  一 . 前言
  今天对 Spring 进行深度使用的时候 , 想仿照 AOP 去实现对应的代理 , 但是却触发了 BeanNotOfRequiredTypeException 异常 , 原因是因为 Spring 会进行类的校验
  于是突然产生了好奇 , 决定研究一下 , AOP 是通过什么方式避开这个校验过程二 . 前置知识AOP 通过 AopProxy 进行代理SpringBoot 1.5 默认使用 JDK Proxy , SpringBoot 2.0 基于自动装配(AopAutoConfiguration)的配置 , 默认使用 CGlib
  JDK Proxy 和 CGLib 的区别
  老生常谈的问题 , 问了完整性(凑字数) , 还是简单列一下 :JDK Proxy : 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。CGLIB动态代理:利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
  PS : 通过 proxy-target-class 可以进行配置三 . 原理探索
  常规方式是 CGLIB , 所以主流程还是通过这种方式分析 , 有了前置知识的补充 , 实现猜测是由于 CGLIB 的特性 , 实际上被校验出来.源头 :autowired 导入得时候会校验注入的类是否正确3.1 拦截的入口Step 1 : AbstractAutowireCapableBeanFactory # populateBeanStep 2 : AutowiredAnnotationBeanPostProcessor # postProcessPropertiesStep 3 : InjectionMetadata # injectStep 4 : AutowiredAnnotationBeanPostProcessor # injectStep 5 : DefaultListableBeanFactory # doResolveDependencypublic Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,       @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {        //............ 以下是主要逻辑         if (autowiredBeanNames != null) {          autowiredBeanNames.add(autowiredBeanName);       }              // 获取 Autowired 的实际对象或者代理对象       if (instanceCandidate instanceof Class) {          instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);       }              // 判断该对象是否为null       Object result = instanceCandidate;       if (result instanceof NullBean) {          if (isRequired(descriptor)) {             raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);          }          result = null;       }              // 核心拦截逻辑       if (!ClassUtils.isAssignableValue(type, result)) {          throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());       }       return result;    } } 3.2 拦截的判断public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {          // 类型判断     if (lhsType.isAssignableFrom(rhsType)) {         return true;     } else {         Class resolvedWrapper;                  // 基本类型特殊处理         if (lhsType.isPrimitive()) {             resolvedWrapper = (Class)primitiveWrapperTypeMap.get(rhsType);             return lhsType == resolvedWrapper;         } else {             resolvedWrapper = (Class)primitiveTypeToWrapperMap.get(rhsType);             return resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper);         }     } } 3.3 AOP 的使用
  看到了拦截的入口 , 那就得看看 AOP 中是如何通过 PostProcessor 进行处理的了 , 首先看一下 PostProcessor 链表
  Step 1 : 当对象 A 中字段是 @Autowired 注入的 AOP 代理类时
  此时我们可以发现 , 在 DefaultListableBeanFactory # doResolveDependency 环节会去获取该代理类的对象 , 拿到的对象如下图所示 :// doResolveDependency 中获取对象环节 if (instanceCandidate instanceof Class) {     // 此时拿到的对象就是一个 cglib 代理类    instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  Step 2 : 判断类的关系入口// doResolveDependency 中判断类的关系 -> true if (!ClassUtils.isAssignableValue(type, result)) {    throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass()); }  // result.getClass() - name=com.gang.aop.demo.service.StartService$EnhancerBySpringCGLIB$d673b902
  Step 3 : 判断类的关系逻辑C- ClassUtils public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {        // 核心语句 , native 方法 -> public native boolean isAssignableFrom(Class<?> cls);    if (lhsType.isAssignableFrom(rhsType)) {       return true;    }    //......... }  // 这里简单做了一个继承类 , ChildService extends ChildService : ------> ChildService By ParentService :false <------- : ------> ParentService By ChildService:true <-------
  由此可见 cglib 创建的对象满足该条件 : 相同 , 或者是超类或者超接口
  这里回过头看之前的问题 , 就很简单了 :// 问题原因 :  我通过实现 postProcessor 去做了一个代理  public class AopProxyImpl extends Sourceable {     private Sourceable source; }    // 修改后 :  public class AopProxyImpl extends Source {     private Sourceable source; }  // 通过继承即可解决 BeanNotOfRequiredTypeException ,弄懂了就没什么难度了 //       四 . 深入原理
  那么继续回顾下 CGLIB 的创建过程 , 实际上在编译的结果上是可以很直观的看到代理的对象的 :
  关于 CGLIB 的基础 , 可以看看菜鸟的文档 CGLIB(Code Generation Library) 介绍与原理 , 写的很详细4.1 CGLIB 的创建过程
  FastClass 的作用
  FastClass 就是给每个方法编号,通过编号找到方法,这样可以避免频繁使用反射导致效率比较低
  CGLIB 会生成2个 fastClass :xxxx$FastClassByCGLIB$xxxx :为生成的代理类中的每个方法建立了索引xxxx$EnhancerByCGLIB$xxxx$FastClassByCGLIB$xxxx : 为我们被代理类的所有方法包含其父类的方法建立了索引
  原因 : cglib代理基于继承实现,父类中非public、final的方法无法被继承,所以需要一个父类的fastclass来调用代理不到的方法
  FastClass 中有2个主要的方法 :// 代理方法 public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {     final CglibService cglibService = (CglibService)o;     switch (n) {         case 0: {             // 代理对应的业务方法             cglibService.run();             return null;         }         case 1: {             // 代理 equeals 方法             return new Boolean(cglibService.equals(array[0]));         }         case 2: {             // 代理 toString 方法             return cglibService.toString();         }         case 3: {             // 代理 hashCode 方法              return new Integer(cglibService.hashCode());         }     }     throw new IllegalArgumentException("Cannot find matching method/constructor"); }           // 实例化对象  public Object newInstance(final int n, final Object[] array) throws InvocationTargetException {     switch (n) {         case 0: {             // 此处总结通过 new 进行了实例化             return new CglibService();         }     }     throw new IllegalArgumentException("Cannot find matching method/constructor"); }
  enchance 对象
  之前了解到 , cglib 通过重写字节码生成主类达到代理的目的 , 这里来看一下 , 原方法被改写成什么样了final void CGLIB$run$0() {     super.run(); }      public final void run() {     MethodInterceptor cglib$CALLBACK_2;     MethodInterceptor cglib$CALLBACK_0;     if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {         CGLIB$BIND_CALLBACKS(this);         cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);     }     if (cglib$CALLBACK_0 != null) {         // 调用拦截器对象         cglib$CALLBACK_2.intercept((Object)this, CglibService$EnhancerByCGLIB$7aba7860.CGLIB$run$0$Method, CglibService$EnhancerByCGLIB$7aba7860.CGLIB$emptyArgs, CglibService$EnhancerByCGLIB$7aba7860.CGLIB$run$0$Proxy);         return;     }     // 没有拦截器对象 , 则直接调用     super.run(); }   // 实际被调用的拦截器 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {          // 这里会调用关联类     // 最终通过 super.run 调用     Object result = proxy.invokeSuper(obj, args);           return result; }
  此处也可以看到映射关系
  总结

为什么小孩子不玩微信,更爱QQ大家好,我是老朋友大叔。大叔有个问题想问大家,你还在用QQ吗?说起QQ,那可是我们八零,九零后的青春,但自从大学毕业开始找工作后,大叔就很少再使用QQ了。不知道有多少人和大叔一样?儿童内衣广告涉及擦边球软色情?谁在对孩子下手?大家好,我是大叔。林子大了,什么鸟都有!这不最近又有人利用儿童来打软色情擦边球来获利。事情来源于一则微博网帖据网友爆料某电商平台上的儿童内衣店,商家为了博眼球在内衣裤广告上动了心思又一手机巨头倒下啦!全球第三的手机巨头大家好,我是大叔。这两年大家都能感受到手机行业的竞争越来越激烈,很多企业都没能承受住时代的大风大浪,最终黯然离场。比如近几年我们熟悉的锤子,360,HTC,黑莓等。如今,又一手机巨打麻将也犯法了?官方给出正式回应,只因涉及到这3点打麻将这项娱乐活动在中国由来已久,是中国人民喜闻乐见的活动之一。无论是南方还是北方,无论是东部,还是西部,打麻将活动处处可见。特别是在过年时期,成了广大民众阖家欢乐的不二之选。这项8000款全新PR无缝转场预设合集,让你的作品狂拽酷炫吊炸天什么是转场?有什么作用?一个视频场景转换到另一个视频场景,场景与场景之间的过渡或转换,就叫做转场在两个镜头场景之间快速切换时会产生模糊观众无法察觉到模糊有何不同这有利于制作一种很酷INS超火青橙旅拍神仙调色,我只服这位油管大神对旅拍感兴趣的小伙伴有福了本期我们给大家带来一套全能的旅拍滤镜预设INS人文街拍Tknorth油管大神旅拍预设让你美美的照片瞬间刷爆朋友圈!由知名旅拍摄影师,曾与Adobe日本国家甲方做梦也想要的五彩斑斓黑我竟2分钟做出来了作为一名设计师你都遇到过甲方爸爸的哪些奇葩要求?是把图片放大的同时缩小一点还是你要用Photoshop,不要用PS或我想要五彩斑斓的黑色五彩斑斓的黑真的是不可能的吗?本期我们给小伙全网热门孟菲斯素材送给你,你想要的全在这里了近两年最火的设计风格少不了孟菲斯风格该风格是非常新潮的激烈的色彩碰撞几何结构的点线面的随机拼贴有非常浓郁的视觉冲击简单粗暴非常适合商业视觉效果好吸引眼球,也适合传达信息本期我们给大迪士尼动画手稿2400张,临摹完可以进大厂了迪斯尼带给我们许许多多的感动与回忆从1928年米老鼠诞生,到后来的唐老鸭狮子王这些经典的动画形象一直伴我们成长。迪斯尼在过去的90年里创作的动画形成了一种文化,展示了永恒的主题家庭搬迁工作开始了!2021年起建造新农村,3类房子在搬迁范围内为了实现乡村与城市的平衡,在近几年,中央政府对于农村的投资越来越多。在前者的带领之下,不少农村都在搞新建设,这种建设要因地制宜,如果当地的先天条件比较好,原地重建即可,如果先天条件通胀危机越来越近?未来若有这3件东西,老百姓们就不用担心了随着人们收入和生活水平的提高,社会中各个产品的物价也在相应增长,前不久美国政府为了缓解疫情造成的经济压力,开始毫无节制地大量印钞,结果给美国本就岌岌可危的经济雪上加霜,现在美国国内
炒币者极度深寒不止凉了,还冻上了炒币的巨大风险,正从币圈向外蔓延,加速侵蚀炒币客的金融信用,甚至危及正常生活。今年以来,国内虚拟货币监管愈发严厉,币价频繁暴跌,炒币客深陷亏损无力自拔。除了账面财富灰飞烟灭外,有人iQOONeo5活力版评测LCD永不为奴iQOONeo5活力版在5月23日低调地上架了。对比前代iQOONeo3,新机主要升级是换了骁龙865和LPDDR5内存。发布价虽然是8128版2299元,8256版2499元,1有哪些牌子的手机用的是OLED屏幕?OLED屏幕具有色域广,反应速率快,轻薄省电,自发光的特点,是一种价格较高的高端显示屏。在中小尺寸的OLED中,三星的AMOLED面板占有90以上的市场份额。也就是说,目前市面上绝买iPhone是买128g的好还是买256g的好?如果资金预算充足的情况下,建议你购买256G或者512G版本,听说iPhone12将会在今晚9月9日凌晨发布,如果是5G手机,而且手机拍摄视频逐步的1080P以及4K成为了主流,甚苹果供应链名单曝光,新增12家中国大陆厂商(原标题刚刚!苹果供应链名单曝光,这些A股公司新入列!这家被踢出果链,市值蒸发超300亿)中国的工厂,在苹果供应链中的分量无疑越来越重。近日,科技巨头苹果公司在官网公布了2021年麒麟980还能在撑几年呢?麒麟980在2018年发布,华为余大嘴声成业界第一个7nm制成的soc,集成69亿晶体管如今现在怎么样了呢?海思麒麟980由4个a76核心(2。6hz)加4个a55的核心(1。8)轻薄外观超级快充,荣耀Play5用户好评率达99,将成618爆款?自5月25日首销开启以来,荣耀Play5的第一批用户评价已经出炉。大家是如何评价这款新机的?小编盘点了一下荣耀商城好评率高达99苏宁好评率99京东好评率98天猫方面有4。9分的好评华为5G被排除半年后,最想哭的是爱立信我们将失去中国5G市场点击关注,每天精彩不断!导读华为5G被排除半年后,最想哭的是爱立信我们将失去中国5G市场!众所周知,自从进入21世纪以后,移动互联网就开始快速的发展起来,如今网络已经渗透到了我们生vivoS1正式开售升降式摄像头零界全面屏2298元手机中国新闻在预热了多日之后,vivoS1今日正式开售。vivoS1采用了和vivoX27系列相同的升降式摄像头零界全面屏,拥有光感自拍加持,在外观设计上可谓是走在了众多厂商的前列霸权?Google阻止我写Web浏览器Metastream创始人SamuelMaddock在其博客中称过去的两年中,我一直致力于一个网页浏览器的开发,却被谷歌阻止。开源浏览器Chrome的创建者竟不允许将数字版权授予开13个新职业公布!最亮的却不是电子竞技员近年来,伴随人工智能电子竞技等新兴产业的发展,新职业也层出不穷。今天,13个新职业正式公布了!今年初初步确定15个拟发布新职业后,4月1日,人社部发布通知,正式确认了13个新职业信