当前位置: 首页 > news >正文

建立网站和小程序需要多少钱网站制作怎样盈利

建立网站和小程序需要多少钱,网站制作怎样盈利,新闻发布会邀请哪些媒体,最近国语视频在线观看目录 一、概念1、三级缓存的作用2、循环依赖的含义 二、代码1、代码下载2、文件功能介绍3、源码分析3.1、找到获取A对象的位置#xff0c;打断点进行debug操作3.2、一步步找到在A对象中注入B对象的位置3.3、一步步找到B对象注入A对象的位置3.4、往下找到通过三级缓存解决循环依… 目录 一、概念1、三级缓存的作用2、循环依赖的含义 二、代码1、代码下载2、文件功能介绍3、源码分析3.1、找到获取A对象的位置打断点进行debug操作3.2、一步步找到在A对象中注入B对象的位置3.3、一步步找到B对象注入A对象的位置3.4、往下找到通过三级缓存解决循环依赖的地方3.5、完成对象B注入对象A3.6、完成对象B的剩余流程进而回到对象A注入对象B的地方3.7、查看对象A不会再次执行代理代码的地方3.8、找到往一级缓存中添加对象A的代理对象的位置3.9、往对象C中注入对象A 三、流程分析四、参考文章 一、概念 1、三级缓存的作用 解决循环依赖即三级缓存 singletonFactories的功能实现动态代理 即二级缓存 earlySingletonObjects的功能存储最终对象 即一级缓存 singletonObjects的功能 2、循环依赖的含义 N个类循环(嵌套)引用。 通俗的讲就是N个Bean互相引用对方最终形成闭环。用一副经典的图示可以表示成这样A、B、C都代表对象虚线代表引用关系 注意其实可以N1也就是极限情况的循环依赖自己依赖自己 另需注意这里指的循环引用不是方法之间的循环调用而是对象的相互依赖关系。方法之间循环调用若有出口也是能够正常work的 可以设想一下这个场景如果在日常开发中我们用new对象的方式若构造函数之间发生这种循环依赖的话程序会在运行时一直循环调用最终导致内存溢出示例代码如下 public class Main {public static void main(String[] args) throws Exception {System.out.println(new A());}}class A {public A() {new B();} }class B {public B() {new A();} }这是一个典型的循环依赖问题。Spring通过三级缓存机制巧妙的解决循环依赖问题~ 二、代码 1、代码下载 百度网盘链接https://pan.baidu.com/s/1Cq1kTJJesOfl0eHIU3cMIA?pwdgyg1 提取码gyg1 2、文件功能介绍 CodeStudyApplication类主启动类A类通过IOC方式注入类B和类C的单例对象用来测试循环依赖并且在成员方法test()上添加Transactional注解用来测试动态代理B类通过IOC方式注入类A的单例对象用来测试循环依赖C类通过IOC方式注入类A的单例对象用来测试循环依赖类C和类B的主要区别是“当B对象触发A对象的循环依赖后可以获取A对象的代理对象然后看C对象如何获取A对象的代理对象”application.yml添加一些mysql信息大家不用修改不会报错 3、源码分析 注意 博主使用的开发工具是IDEA所以提到的快捷键都是IDEA中的 3.1、找到获取A对象的位置打断点进行debug操作 当程序启动时最先运行的是com.atguigu.test.CodeStudyApplication类中的main方法。 根据代码执行顺序我们按着Ctrl按键后点击SpringApplication类的静态方法run() 现在进入了SpringApplication类的静态方法run()然后我们按着Ctrl按键后点击重载的静态方法run() 继续按着Ctrl按键后点击成员方法run() 现在进入了一个方法内容较多的run()方法继续按着Ctrl按键后点击成员方法refreshContext(context);由于方法体内很多内容并非本节重点所以不在本节讲解其中IOC的主要内容都在refreshContext(context)方法体现。 继续按着Ctrl按键后点击成员方法refresh(context); 继续按着Ctrl Alt按键后点击applicationContext对象的refresh()方法 下图中refresh方法参数中的applicationContext对象大家应该很熟悉来看一道常见面试题“BeanFactory与ApplicationContext的区别”哈哈哈想起来了吧~ 上述操作之后页面中会出现三个实现类供我们选择我们选择实现类ServletWebServerApplicationContext该实现类是我们常用的至于选它的原因不是本节重点所以不再解释。 继续按着Ctrl按键后点击父类方法super.refresh(); 继续按着Ctrl按键后点击成员方法finishBeanFactoryInitialization(beanFactory); 其中对象构建、注入其他对象、代理、初始化等流程都在该方法中其他代码不是本节重点所以不在讲解。 继续按着Ctrl Alt按键后点击beanFactory.preInstantiateSingletons();方法 我们把断点打在成员方法getBean(beanName);上 其中遍历的beanNames方法来自于成员集合变量beanDefinitionNames该集合的值来自于ComponentScan扫描以及其他注解操作的结果 3.2、一步步找到在A对象中注入B对象的位置 通过按F9快速运行程序遍历beanNames找到beanName等于a的情况停下 点击F7进入方法getBean(beanName); 点击F7进入方法doGetBean()方法 点击F8进入下一步到达getSingleton(beanName);这一行 点击F7进入以下方法 再次点击F7进入以下方法 此时singletonObjects中不存在名字叫做a的对象所以singletonObject等于null由于A对象还没有开始创建所以isSingletonCurrentlyInCreation(beanName)的结果是false因此第1个if方法不会执行所以最终得知singletonObject的结果是null 点击两次F8就可以回到Object sharedInstance getSingleton(beanName); 依然在上述doGetBean方法中我们往下找到if (mbd.isSingleton())这一行然后在这一行上单击鼠标右键点击Run toCursor 然后点击F8往下执行一行代码此时就到了getSingleton方法处该方法有2个参数分别是bean对象名称和一个匿名内部类此时点击F7进入getSingleton方法 我们首先看下beforeSingletonCreation(beanName);进入该方法的方法体看下其中singletonsCurrentlyInCreation集合记录了正在创建的对象对象A不在里面所以会被加入到singletonsCurrentlyInCreation集合中 然后往下可以看到singletonObject singletonFactory.getObject();这行代码真正执行的是形参对应的匿名内部类中的实现方法回顾上一张截图执行getObject方法其实就是执行createBean方法 回顾一下上面那张截图我们找到createBean方法 点击Ctrl Alt在createBean方法上点击鼠标左键就可以进入该方法 继续按着Ctrl按键后点击成员方法doCreateBean(beanName, mbdToUse, args); 在该方法中往下滚动屏幕可以看到addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean));先来分析一下addSingletonFactory方法的方法体现在回头看下addSingletonFactory方法的第2个参数这是一个匿名内部类我们后面会用到它大家用笔记一下后续我们还会回来的~ 然后往下滑可以看到populateBean(beanName, mbd, instanceWrapper);方法 继续按着Ctrl Alt按键后点击成员方法postProcessProperties()方法 在A类中注入对象B使用Resource注解所以我们选择CommonAnnotationBeanPostProcessor类 继续按着Ctrl按键后点击成员方法metadata.inject(bean, beanName, pvs); 继续按着Ctrl按键后点击成员方法element.inject(target, beanName, pvs); 继续按着Ctrl Alt按键后点击成员方法getResourceToInject()方法选择CommonAnnotationBeanPostProcessor类 上述方法的作用就是在A对象中注入B对象 3.3、一步步找到B对象注入A对象的位置 由于在A类中引入的对象B上没有加Lazy注解所以lazyLookup是false因此走getResource方法继续按着Ctrl按键后点击成员方法getResource(this, requestingBeanName) 继续按着Ctrl按键后点击成员方法autowireResource() 继续按着Ctrl Alt按键后点击方法resolveBeanByName() 可以看到代码中调用了getBean方法非常熟悉吧又回到最初的起点继续根据name信息获取Bean工厂中的对象现在是从对象A中准备注入对象B 后面过程和通过对象A的名称来调用getBean(name)方法一样我们把过程走一下吧~ 点击Ctrlorg.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.ClassT)点击Ctrlorg.springframework.beans.factory.support.AbstractBeanFactory#doGetBean点击Ctrlorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory?)点击Ctrlorg.springframework.beans.factory.support.AbstractBeanFactory#createBean点击Ctrlorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean点击Ctrlorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean点击Ctrl Altorg.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties选择CommonAnnotationBeanPostProcessor类点击Ctrlorg.springframework.beans.factory.annotation.InjectionMetadata#inject点击Ctrlorg.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject我们在对象B中注入了对象A此时执行的field.set(target, getResourceToInject(target, requestingBeanName));就是获取对象A的点击Ctrl Altorg.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#getResourceToInject选择CommonAnnotationBeanPostProcessor类由于B类中注入的对象A也没加Lazy注解所以走getResource(this, requestingBeanName)方法点击Ctrlorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource点击Ctrlorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource点击Ctrl Altorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName现在我们来到getBean方法终极反转了现在是从对象B中准备注入对象A 3.4、往下找到通过三级缓存解决循环依赖的地方 现在是B对象准备注入对象A继续按着Ctrl按键后点击方法getBean(name, descriptor.getDependencyType()) 继续按着Ctrl按键后点击方法doGetBean(name, requiredType, null, false) 继续按着Ctrl按键后点击方法getSingleton(beanName) 继续按着Ctrl按键后点击方法getSingleton(beanName, true) 然后进入方法体中由于对象A还没有创建完成所以一级缓存singletonObjects肯定没有因此singletonObject对象是null 我们看下isSingletonCurrentlyInCreation(beanName)方法的方法体其中singletonsCurrentlyInCreation集合作用在上面已经解释过了就是记录当前正在被创建的bean对象名称在处理A对象时调用getSingleton》beforeSingletonCreation方法中已经将a存储到了singletonsCurrentlyInCreation集合中所以此处isSingletonCurrentlyInCreation(beanName)方法的结果肯定是true 此时代码继续往下走由于此时对象A的信息存储在三级缓存singletonFactories中因此也无法从earlySingletonObjects集合中去除对象A的相关信息所以singletonObject this.earlySingletonObjects.get(beanName);的结果也是null 由于上面调用getSingleton()方法时传入的参数allowEarlyReference是true因此方法会继续向下走 为解决并发创建对象问题所以需要将一级对象singletonObjects锁起来在同步代码块中尝试从一级缓存singletonObjects和二级缓存earlySingletonObjects中得到对象A但是此时对象A存储在三级缓存singletonFactories中因此依然获取不到 此时准备从三级缓存singletonFactories中获取对象A由于之前在往对象A进行依赖注入之前往三级缓存中填充过值所以此时singletonFactory是有值的来给大家回忆一下填充值的位置 此时singletonFactory的值就是上图框中的匿名内部类所属的接口是一个函数式接口此时代码继续往下执行 当执行singletonFactory.getObject()的时候其实执行的是匿名内部类中的方法我们把断点打到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法中的if判断上 继续按着Ctrl Alt按键后点击方法bp.getEarlyBeanReference(exposedObject, beanName) 此时将没有被包装过的对象放到earlyProxyReferences集合中该集合作用是记录已经执行过代理方法的对象信息即执行过wrapIfNecessary(bean, beanName, cacheKey)方法的对象信息由于代理这个动作只能执行一次下次不能在执行了所以将已经执行过代理的对象信息记录在earlyProxyReferences集合中后续在需要执行代理的时候我们判断下该集合中是否已经包含进而决定是否还要执行wrapIfNecessary方法这个我们后续会聊到的这里不在展开~ 大家注意现在情况是往对象B中注入对象A目前处于一个获取对象A的过程中由于A类的test()方法上添加了Transactional注解所以在执行wrapIfNecessary方法之后将会生成A类的代理对象既然到这里了我们跟进去看下 继续按着Ctrl按键后点击方法wrapIfNecessary(bean, beanName, cacheKey) 当getAdvicesAndAdvisorsForBean方法执行之后就可以知道代理信息如下 继续按着Ctrl按键后点击方法createProxy() 进入该方法往下就可以找到真正执行代理的地方即proxyFactory.getProxy(classLoader)继续按着Ctrl按键后点击方法proxyFactory.getProxy(classLoader) 继续按着Ctrl按键后点击方法createAopProxy() 继续按着Ctrl Alt按键后点击方法createAopProxy(this) 可以非常清晰的看到使用CGLib还是JDK动态代理方式 最终一层层返回代理结果那么getEarlyBeanReference()方法的返回值就是A类的代理对象了可以非常明显的看到代理对象是CGLib类型的 我们此时依然在B对象中注入A对象的过程中目前准备获取对象A或者A类的代理对象上面方法是在执行singletonFactory.getObject()方法也就是那个匿名内部类中的方法得到的结果singletonObject是一个A类的CGLib代理对象此时将代理对象装在二级缓存earlySingletonObjects中 此时就可以完成B类中的对象A注入了 3.5、完成对象B注入对象A 上面已经获取到对象A的代理对象了我们一部分返回到注入对象A的地方 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)org.springframework.beans.factory.support.AbstractBeanFactory#doGetBeanorg.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class)org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByNameorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResourceorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResourceorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInjectorg.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject 继续往上回到对象B完成注入对象A之后执行初始化代码的地方 org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#injectorg.springframework.beans.factory.annotation.InjectionMetadata#injectorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertiesorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 3.6、完成对象B的剩余流程进而回到对象A注入对象B的地方 初始化流程就是执行一些初始化方法的过程由于对象B的初始化流程不是本节重点所以不在讲解直接略过 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBeanorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])org.springframework.beans.factory.support.AbstractBeanFactory#doGetBeanorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory?)org.springframework.beans.factory.support.AbstractBeanFactory#doGetBeanorg.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class)org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByNameorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResourceorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResourceorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInjectorg.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject 现在回到l对象A注入对象B的地方 然后一步步回到对象A的初始化方法代码处 org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#injectorg.springframework.beans.factory.annotation.InjectionMetadata#injectorg.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertiesorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 3.7、查看对象A不会再次执行代理代码的地方 从上图可见我们进入initializeBean方法主要看方法体中基本快结尾的地方这个里面隐藏着代理类的生成逻辑 按着Ctrl点击进去 我们一次又一次的debug进去终于发现了生成代理对象的地方大家熟悉earlyProxyReferences吗 在B对象中注入对象A时已经将对象A的信息塞在earlyProxyReferences集合中了具体作用是说明对象A的代理对象已经生成并放在二级缓存earlySingletonObjects中了所以if判断结果是false因此wrapIfNecessary(bean, beanName, cacheKey)不会在执行了这样可以避免该方法执行2次并且可以避免无法再bean工厂中生成一致的bean对象 如果没有发生循环依赖那么org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference方法不会执行因此earlyProxyReferences集合中就不存在键为a的情况了如果B类中没有注入对象A就可以达到。但是A类和B类产生了循环依赖所以earlyProxyReferences中存储键为a的情况了 3.8、找到往一级缓存中添加对象A的代理对象的位置 对象A的实例化已经完成代码继续往下执行可以看到 从二级缓存中获得对象A的代理对象 然后将结果一路返回 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBeanorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]) 当匿名内部类中的方法执行完毕后将回到singletonObject singletonFactory.getObject();执行完成的状态 往下继续看方法addSingleton()方法目前singleonObject是一个 往一级缓存中放入对象A的代理对象信息 3.9、往对象C中注入对象A 由于对象A已经在一级缓存中了所以对象C通过getSingleton方法可以直接获取对象A的代理对象 三、流程分析 在上述示例代码中对象A中注入对象B、对象C而对象B和对象C中也会注入对象A这就造成了循环依赖 Spring根据SpringBootApplication注解中ComponentScan扫描到了A类被Component注解修饰 Spring先去Bean容器中获取对象A但是发现对象A不在Spring容器中那就来创建对象A首先根据A类的构造函数创建对象A但是对象A不会被放在三级缓存中 将匿名内部类放到第3级缓存singletonFactories中在后面解决循环依赖时可以用到 在对象A中注入对象B Spring就去Bean容器中获取对象B但是发现对象B不在Spring容器中那就来创建对象B和创建对象A一样也是根据A类的构造函数创建对象A然后准备往对象B中注入对象A Spring就去Bean容器中获取对象A我们在上面将包含对象A信息的匿名内部类放到了第3级缓存singletonFactories中现在我们可以把它取出来之后调用它的getObject()方法其实就是执行匿名内部类中的方法 我们来看下匿名内部类中的方法图片下半部分是方法开始的地方然后在往上看就可以大家看下标号①我们将生成代理对象后的原始对象存在earlyProxyReferences集合中后续当对象A完成初始化方法后通过earlyProxyReferences集合进行判断然后决定是否执行生成代理对象的方法避免其他对象中注入的代理对象和Bean工厂中存储的代理对象不一致大家再看下标号②在A类中有一个test()方法上添加了Transactional注解当执行wrapIfNecessary()方法之后就会通过CGlib生成一个代理对象A而原始对象A将被存储在earlyProxyReferences集合中 当代理对象A生成完成相当于singletonFactory.getObject()执行完毕此时singletonObject的值就是代理对象A然后将代理对象A放到第2级缓存earlySingletonObjects中并删除第3级缓存singletonFactories中存储的信息 上面通过getSingleton()方法获取到了代理对象A然后交给对象B做注入最终完成对象B的创建工作然后将创建完成的对象B返回给对象A做注入当对象B被注入到对象A之后那就完成了populateBean()方法 然后初始化也是对象创建道路上的关键一环现在来执行initializeBean()方法生成代理对象的位置在该方法末尾的地方即执行applyBeanPostProcessorsAfterInitialization()方法的地方其中方法会执行到这个地方在解决循环依赖对象A和B相互依赖的时候我们把原始对象A已经放到了earlyProxyReferences集合中所以此时可以从循环依赖中取到原始对象A所以if判断的结果是false因此就不会执行生成代理对象A的过程了大家是否还记得我们之前已经把代理对象A放到了第2级缓存earlySingletonObjects中了我们在后续步骤中会从二级缓存中去除代理对象A然后放到一级缓存中往下慢慢看 此时exposedObject依然是原始对象A然后往下执行getSingleton()方法来获取代理对象A 在解决循环依赖的时候我们将代理对象放到了2级缓存earlySingletonObjects集合中现在我们取出代理对象赋值给earlySingletonReference 然后doCreateBean()方法将代理对象A返回然后createBean()方法在将代理对象A返回这也标志着红框中匿名内部类中方法的执行结束而匿名内部类方法的调用位置在getSingleton()方法中也就是getObject()方法此时singletonObject的值就是代理对象A 最后调用addSingleton()方法将代理对象A放入1级缓存中 Spring根据SpringBootApplication注解中ComponentScan扫描到了C类被Component注解修饰 C类中注入了对象A所以需要获取对象A此时对象A已经放到了1级缓存中所以可以直接取到代理对象A从而在对象C中注入代理对象A 四、参考文章 一文告诉你Spring是如何利用三级缓存巧妙解决Bean的循环依赖问题的【享学Spring】
http://www.w-s-a.com/news/481463/

相关文章:

  • 烟台有没有做网站大连建设工程信息网专家库
  • 网站建设明确细节商贸有限公司的经营范围
  • 南宁微网站开发做的好的有哪些网站
  • 好的素材下载网站读书网网站建设策划书
  • 东莞南城网站建设wordpress用户投稿插件
  • 开个网站做代理赚钱吗沽源网站建设
  • 做卖车网站需要什么手续wordpress 主题 demo
  • 上海外贸网站开发公司建设内容
  • 网站制作品牌公司网站的字体颜色
  • 外贸wordpress模板常德seo快速排名
  • 网站后台认证码专门做网页的网站
  • 宁波企业品牌网站建设物流公司招聘
  • 北京机建网站做网站用angular
  • 攀枝花市网站建设outlook企业邮箱注册申请
  • 企业网站建设报价单免费劳务网站建设
  • 天津平台网站建设方案国际新闻最新消息今天乌克兰与俄罗斯
  • 食用油 网站 模板网页游戏网站在线玩
  • 做网站用的书新能源东莞网站建设技术支持
  • 漯河网站超市建设软件开发的五个阶段
  • 制作深圳网站建设阿里OSS做网站图库费用
  • 网页设计与网站建设 入门必练宜都网站seo
  • 网站设计沟通阆中网站网站建设
  • 缩短网址做钓鱼网站如何确保网站安全
  • 网店网站开发怎样用ps做企业网站
  • 南京门户网站建设做网站一般注册哪几类商标
  • 企业咨询管理服务wordpress seo tdk
  • 做网站前期创建文件夹flash 开发的网站
  • 天津网站制作培训搭建网站的工具
  • 江西网站建设价格低网上卖产品怎么推广
  • 做aelogo动效有什么好的网站建立网站站点的步骤