草庐IT

spring bean依赖注入

sunnysgw 2023-04-15 原文

spring bean依赖注入

1.依赖注入的步骤

结合调用方法,从大到小讲述中间涉及到代码以及bean创建过程中的注入点。

这里涉及的逻辑是在AbstractAutowireCapableBeanFactory的doCreateBean方法中实现的,是bean创建生命周期中的重要的一环。

 

  1. 实例化bean对象之后,即在applyMergedBeanDefinitionPostProcessors方法中调用MergedBeanDefinitionPostProcessor实现类中的postProcessMergedBeanDefinition方法:

    // Allow post-processors to modify the merged bean definition.
    synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
        }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
        }
         mbd.postProcessed = true;
      }
    }

    这个注入点对spring合并好的beandefinition做一些定制化的处理,其中就包括寻找bean中的注入点,resource、value、autowired这些注解的预处理都是在这个注入点实现的。

    具体的实现类包括CommonAnnotationBeanPostProcessor(处理resource注解)和AutowiredAnnotationBeanPostProcessor(处理autowired、value注解)。

    具体寻找注入点的过程即通过反射遍历bean的field和method,检查是否有目标注解,扫描的结果放到cache中,供之后做注入,其中对字段的过滤中,不会记录静态字段,因为对于原型bean,如果静态字段也可以做注入,就会出现不合理的结果,对方法的过滤中,有涉及桥接方法的处理,对于实现的接口中的方法,编译的字节码中会出现两个同名方法,其中一个会被标记为桥接方法,spring不会扫描这些方法上的注解,同时spring同样不会关心静态方法上的注解。

  1. 标记完注入点之后,要做的就是基于注解找到要注入的内容了

    这是在populateBean(beanName, mbd, instanceWrapper)方法中实现的,这个方法中做的就是各种依赖注入,包括前面讲的在bean注解或者xml标签中标记的autowired属性这种废弃的方式,以及基于注解的更灵活的方式。注入点是InstantiationAwareBeanPostProcessor中的postProcessProperties方法,上一步中讲的两个BeanPostProcessor类均实现了这个接口,所以前后两个注入点是在同一个bean中做的。

    对于autowired以及value注解的处理中,开始同样是调用在上一步中调用过的findResourceMetadata方法,不同的是,这一步直接使用上一步缓存好的值,取到值之后,即调用InjectionMetadata的inject方法,做真正的注入,这里AutowiredAnnotationBeanPostProcessor针对field和method两种情况分别实现了注入点内部类AutowiredFieldElement以及AutowiredMethodElement,它们都继承了InjectionMetadata.InjectedElement。

     

    下面是找注入值的大体步骤

    1. 基于type找到所有的候选bean,之后对每个候选bean做2 3 4的过滤

    2. 检查是否autowireCandidate的属性为false,如果是的话,丢弃之

    3. 基于范型做匹配

    4. 基于Qualifier注解做匹配

    5. 如果没有候选bean,如果required为true即报错;如果只有一个了,直接返回;如果有多个就按照6 7 8做过滤

    6. 如果有候选bean使用Primary注解修饰,直接返回

    7. 基于Priority注解排序

    8. 基于参数name确定

    到这一步,如果找到的依旧是多个候选bean,直接报错,如果没有候选bean,看下注入点的require参数,如果不是false,同样报错。

 

2.中间用到的设计模式

在过滤候选bean的时候使用到了AutowireCandidateResolver接口的isAutowireCandidate方法,这个方法一共有三种实现,对于每个子类的实现方法中,都会有如下代码:

if (!super.isAutowireCandidate(bdHolder, descriptor)) {
  // If explicitly false, do not proceed with any other checks...
  return false;
}

即,首先调用父类的判断逻辑,如果父类通过,再调用自己的判断逻辑。

这里是责任链模式

有关spring bean依赖注入的更多相关文章

  1. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby - 这个 ruby​​ 注入(inject)魔术是如何工作的? - 2

    我今天看到了一个ruby​​代码片段。[1,2,3,4,5,6,7].inject(:+)=>28[1,2,3,4,5,6,7].inject(:*)=>5040这里的注入(inject)和之前看到的完全不一样,比如[1,2,3,4,5,6,7].inject{|sum,x|sum+x}请解释一下它是如何工作的? 最佳答案 没有魔法,符号(方法)只是可能的参数之一。这是来自文档:#enum.inject(initial,sym)=>obj#enum.inject(sym)=>obj#enum.inject(initial){|mem

  4. ruby - 有什么方法可以告诉 sidekiq 一项工作依赖于另一项工作 - 2

    有什么方法可以告诉sidekiq一项工作依赖于另一项工作,并且在后者完成之前无法开始? 最佳答案 仅使用Sidekiq;答案是否定的。正如DickieBoy所建议的那样,您应该能够在依赖作业完成时将其启动。像这样。#app/workers/hard_worker.rbclassHardWorkerincludeSidekiq::Workerdefperform()puts'Doinghardwork'LazyWorker.perform_async()endend#app/workers/lazy_worker.rbclassLaz

  5. ruby - 防止SQL注入(inject)/好的Ruby方法 - 2

    Ruby中防止SQL注入(inject)的好方法是什么? 最佳答案 直接使用ruby?使用准备好的语句:require'mysql'db=Mysql.new('localhost','user','password','database')statement=db.prepare"SELECT*FROMtableWHEREfield=?"statement.execute'value'statement.fetchstatement.close 关于ruby-防止SQL注入(inject

  6. 关于Qt程序打包后运行库依赖的常见问题分析及解决方法 - 2

    目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'

  7. Ruby:映射和注入(inject)之间的区别 - 2

    在此处阅读有关SO的各种解释,它们是这样描述的:map:Themapmethodtakesanenumerableobjectandablock,andrunstheblockforeachelement注入(inject):Injecttakesavalueandablock,anditrunsthatblockonceforeachelementofthelist.希望你明白为什么我觉得它们表面上看起来很相似。我什么时候会选择一个而不是另一个,它们之间有什么明显的区别吗? 最佳答案 如果您认为inject也别名为reduce,这

  8. ruby - 为什么 Ruby 注入(inject)方法不能对没有初始值的字符串长度求和? - 2

    为什么下面的代码会报错?['hello','stack','overflow'].inject{|memo,s|memo+s.length}TypeError:can'tconvertFixnumintoStringfrom(irb):2:in`+'from(irb):2:in`blockinirb_binding'from(irb):2:in`each'from(irb):2:in`inject'from(irb):2如果传递了初始值,它就可以正常工作:['hello','stack','overflow'].inject(0){|memo,s|memo+s.length}=>18

  9. ruby - 在多个线程中引用类方法会导致自动加载循环依赖崩溃 - 2

    代码:threads=[]Thread.abort_on_exception=truebegin#throwexceptionsinthreadssowecanseethemthreadseputs"EXCEPTION:#{e.inspect}"puts"MESSAGE:#{e.message}"end崩溃:.rvm/gems/ruby-2.1.3@req/gems/activesupport-4.1.5/lib/active_support/dependencies.rb:478:inload_missing_constant':自动加载常量MyClass时检测到循环依赖稍加研究后,

  10. ruby-on-rails - 本地 gem 的“bundle 安装”没有解决依赖关系,而 'gem install' 可以 - 2

    我在目录“/home/enterprise/pkg”中有一个本地gem(enterprise-0.0.1.gem)。它依赖于active_directorygem(v1.5.5),这是在它的enterprise.gemspec文件中指定的,如下所示:-gem.add_dependency("active_directory")在我的应用程序的Gemfile中,我添加了以下行:-gem'enterprise','0.0.1',path=>'/home/enterprise/pkg'当我做的时候bundleinstall在我的应用程序的源目录中,只安装了企业gem。因此,我遇到了引用act

随机推荐