精华内容
下载资源
问答
  • 主要介绍了Intellij IDEA如何去掉@Autowired 注入警告的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 主要介绍了@Autowired注入为null问题原因分析吗,小编觉得挺不错的,对日后比较有帮助,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 主要介绍了Spring注解@Resource和@Autowired区别对比详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 通过@Bean 和spring的factoryBean注入的bean. 以及对应@service注解注入的类 通过@Autowired 注入对象的时间是现根据类型在根据beanName获取的案例集合
  • 主要介绍了@Autowired(required=false)注入注意的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 主要介绍了Spring使用@Autowired为抽象父类注入依赖代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要给大家介绍了因Spring AOP导致@Autowired依赖注入失败的解决方法,文中通过示例代码给大家介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。
  • 主要介绍了使用@Autowired注解警告Field injection is not recommended的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • WebappApplication 一定要在包的最外层,否则Spring无法对所有的类进行托管,会造成@Autowired 无法注入。接下来给大家介绍解决Springboot @Autowired 无法注入问题,感兴趣的朋友一起看看吧
  • 通过spring 的BeanPostProcessor 将对象注入到spring之前创建一个代理对象注入到spring容器中,通过@autowired 注入的对象就是指定的代理对象内容
  • NULL 博文链接:https://huihai.iteye.com/blog/1880106
  • 主要介绍了详解SpringBoot 多线程处理任务 无法@Autowired注入bean问题解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 本篇文章主要介绍最重要的三个Spring注解,也就是@Autowired、@Resource和@Service,具有很好的参考价值。下面跟着小编一起来看下吧
  • 今天小编就为大家分享一篇关于SpringBoot集成shiro,MyRealm中无法@Autowired注入Service的问题,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • Autowired

    千次阅读 2018-09-28 10:23:59
    (@Autowired) @Autowired private SysLogDao sysLogDao; /*public void setSysLogDao(SysLogDao sysLogDao) { this.sysLogDao = sysLogDao; }*/ public SysLogDao getSysLogDao() { return sysLogDao; ...

    (@Autowired)

    @Autowired
    private LogDao logDao;

    /*public void setLogDao(LogDao logDao) {
    	this.logDao = logDao;
      }*/
    
    展开全文
  • 主要介绍了彻底搞明白Spring中的自动装配和Autowired注解的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Spring Boot 自定义 Shiro 过滤器无法使用 @Autowired问题及解决方法 ,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
  • 主要介绍了详解Spring依赖注入:@Autowired,@Resource和@Inject区别与实现原理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • NULL 博文链接:https://forestqqqq.iteye.com/blog/2235292
  • 主要介绍了SpringBoot项目使用多线程处理任务时无法通过@Autowired注入bean问题的解决方法,需要的朋友可以参考下
  • 主要介绍了详解Spring Controller autowired Request变量的相关资料,通过此文希望能帮助到大家,需要的朋友可以参考下
  • 主要介绍了详解Spring @Autowired 注入小技巧,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 自定义标签中@Autowired的属性为null 解决办法:两步 1.新建一个类SpringContext,实现接口ApplicationContextAware; 2.spring.xml中添加 3.使用SpingContext.getBean("bean名");获取
  • @Autowired使用 构造函数注入 public Class Outer { private Inner inner; @Autowired public Outer(Inner inner) { this.inner = inner; } } 属性注入 public Class Outer { @Autowired private Inner ...
  • 下面小编就为大家带来一篇基于Spring@Autowired注解与自动装配详谈。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了你所不知道的Spring的@Autowired实现细节分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 主要介绍了Spring框架中 @Autowired 和 @Resource 注解的区别的相关资料,需要的朋友可以参考下
  • @Autowired用法详解

    千次阅读 2019-05-05 10:05:24
    首先要知道另一个东西,default-autowire,它是在xml文件中进行配置的,可以设置为byName、byType、constructor和autodetect;... @Autowired是用在JavaBean中的注解,通过byType形式,用来给指定的字段或...
    • 首先要知道另一个东西,default-autowire,它是在xml文件中进行配置的,可以设置为byName、byType、constructor和autodetect;比如byName,不用显式的在bean中写出依赖的对象,它会自动的匹配其它bean中id名与本bean的set**相同的,并自动装载。
    • @Autowired是用在JavaBean中的注解,通过byType形式,用来给指定的字段或方法注入所需的外部资源。
    • 两者的功能是一样的,就是能减少或者消除属性或构造器参数的设置,只是配置地方不一样而已。
    • autowire四种模式的区别

     

    • 先看一下bean实例化和@Autowired装配过程 
    1. 一切都是从bean工厂的getBean方法开始的,一旦该方法调用总会返回一个bean实例,无论当前是否存在,不存在就实例化一个并装配,否则直接返回。(Spring MVC是在什么时候开始执行bean的实例化过程的呢?其实就在组件扫描完成之后)
    2. 实例化和装配过程中会多次递归调用getBean方法来解决类之间的依赖。
    3. Spring几乎考虑了所有可能性,所以方法特别复杂但完整有条理。
    4. @Autowired最终是根据类型来查找和装配元素的,但是我们设置了<beans default-autowire="byName"/>后会影响最终的类型匹配查找。因为在前面有根据BeanDefinition的autowire类型设置PropertyValue值得一步,其中会有新实例的创建和注册。就是那个autowireByName方法。
       
    • 下面通过@Autowired来说明一下用法

     

    • Setter 方法中的 @Autowired 

           你可以在 JavaBean中的 setter 方法中使用 @Autowired 注解。当 Spring遇到一个在 setter 方法中使用的 @Autowired 注解,它会在方法中执行 byType 自动装配。 
    这里是 TextEditor.java 文件的内容:
     

    package com.tutorialspoint;
    import org.springframework.beans.factory.annotation.Autowired;
    public class TextEditor {
       private SpellChecker spellChecker;
       @Autowired
       public void setSpellChecker( SpellChecker spellChecker ){
          this.spellChecker = spellChecker;
       }
       public SpellChecker getSpellChecker( ) {
          return spellChecker;
       }
       public void spellCheck() {
          spellChecker.checkSpelling();
       }
    }
    

    下面是另一个依赖的类文件 SpellChecker.java 的内容:

    package com.tutorialspoint;
    public class SpellChecker {
       public SpellChecker(){
          System.out.println("Inside SpellChecker constructor." );
       }
       public void checkSpelling(){
          System.out.println("Inside checkSpelling." );
       }  
    }
    

    下面是 MainApp.java 文件的内容:

    package com.tutorialspoint;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    public class MainApp {
       public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
          TextEditor te = (TextEditor) context.getBean("textEditor");
          te.spellCheck();
       }
    }

    下面是配置文件 Beans.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
       <context:annotation-config/>
    
       <!-- Definition for textEditor bean without constructor-arg  -->
       <bean id="textEditor" class="com.tutorialspoint.TextEditor">
       </bean>
    
       <!-- Definition for spellChecker bean -->
       <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
       </bean>
    
    </beans>

    一旦你已经完成的创建了源文件和 bean 配置文件,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

    Inside SpellChecker constructor. 
    Inside checkSpelling.

    • 属性中的 @Autowired 

    你可以在属性中使用 @Autowired 注解来除去 setter 方法。当时使用 为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。所以利用在属性中 @Autowired 的用法,你的 TextEditor.java 文件将变成如下所示:
     

    package com.tutorialspoint;
    import org.springframework.beans.factory.annotation.Autowired;
    public class TextEditor {
       @Autowired
       private SpellChecker spellChecker;
       public TextEditor() {
          System.out.println("Inside TextEditor constructor." );
       }  
       public SpellChecker getSpellChecker( ){
          return spellChecker;
       }  
       public void spellCheck(){
          spellChecker.checkSpelling();
       }
    }

     

    下面是配置文件 Beans.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
       <context:annotation-config/>
    
       <!-- Definition for textEditor bean -->
       <bean id="textEditor" class="com.tutorialspoint.TextEditor">
       </bean>
    
       <!-- Definition for spellChecker bean -->
       <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
       </bean>
    
    </beans>

    一旦你在源文件和 bean 配置文件中完成了上面两处改变,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

    Inside TextEditor constructor. 
    Inside SpellChecker constructor. 
    Inside checkSpelling.

     

    • 构造函数中的 @Autowired 

    你也可以在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用 元素配置 bean ,构造函数也会被自动连接。让我们检查一下下面的示例。
    这里是 TextEditor.java 文件的内容:
     

    package com.tutorialspoint;
    import org.springframework.beans.factory.annotation.Autowired;
    public class TextEditor {
       private SpellChecker spellChecker;
       @Autowired
       public TextEditor(SpellChecker spellChecker){
          System.out.println("Inside TextEditor constructor." );
          this.spellChecker = spellChecker;
       }
       public void spellCheck(){
          spellChecker.checkSpelling();
       }
    }

    下面是配置文件 Beans.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
       <context:annotation-config/>
    
       <!-- Definition for textEditor bean without constructor-arg  -->
       <bean id="textEditor" class="com.tutorialspoint.TextEditor">
       </bean>
    
       <!-- Definition for spellChecker bean -->
       <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
       </bean>
    
    </beans>

    一旦你在源文件和 bean 配置文件中完成了上面两处改变,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:

    Inside TextEditor constructor. 
    Inside SpellChecker constructor. 
    Inside checkSpelling.

    • @Autowired 的(required=false)选项 

    默认情况下,@Autowired 注解意味着依赖是必须的,它类似于 @Required 注解,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。
    即使你不为 age 属性传递任何参数,下面的示例也会成功运行,但是对于 name 属性则需要一个参数。你可以自己尝试一下这个示例,因为除了只有 Student.java 文件被修改以外,它和 @Required 注解示例是相似的。
     

    package com.tutorialspoint;
    import org.springframework.beans.factory.annotation.Autowired;
    public class Student {
       private Integer age;
       private String name;
       @Autowired(required=false)
       public void setAge(Integer age) {
          this.age = age;
       }  
       public Integer getAge() {
          return age;
       }
       @Autowired
       public void setName(String name) {
          this.name = name;
       }   
       public String getName() {
          return name;
       }
    }

    参考:

    https://blog.csdn.net/u013257679/article/details/52295106
    http://my.oschina.net/HeliosFly/blog/203902 
    http://wiki.jikexueyuan.com/project/spring/annotation-based-configuration/spring-autowired-annotation.html


     

    展开全文
  • @Autowired正是用来支持依赖注入的核心利器,案例1:过多赠予,无所适从 在使用@Autowired时 required a single bean, but 2 were found 仅需要一个Bean,但实际却提供了2个,模拟下,根据学生的学号(ID)来移除...

    1 做不到雨露均沾

    经常会遇到,required a single bean, but 2 were found。

    • 根据ID移除学生
      DataService是个接口,其实现依赖Oracle:

    现在期望把部分非核心业务从Oracle迁移到Cassandra,自然会先添加上一个新的DataService实现:

    @Repository
    @Slf4j
    public class CassandraDataService implements DataService{
        @Override
        public void deleteStudent(int id) {
            log.info("delete student info maintained by cassandra");
        }
    }
    

    当完成支持多个数据库的准备工作时,程序就已经无法启动了,报错如下:

    解析

    当一个Bean被构建时的核心步骤:

    • 执行AbstractAutowireCapableBeanFactory#createBeanInstance:通过构造器反射出该Bean,如构建StudentController实例
    • 执行AbstractAutowireCapableBeanFactory#populate:填充设置该Bean,如设置StudentController实例中被 @Autowired 标记的dataService属性成员。

    “填充”过程的关键就是执行各种BeanPostProcessor处理器,关键代码如下:

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
          //省略非关键代码
          for (BeanPostProcessor bp : getBeanPostProcessors()) {
             if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
              //省略非关键代码
             }
          }
       }   
    }
    

    因为StudentController含标记为Autowired的成员属性dataService,所以会使用到AutowiredAnnotationBeanPostProcessor完成“装配”:找出合适的DataService bean,设置给StudentController#dataService。
    装配过程:

    1. 寻找所有需依赖注入的字段和方法:AutowiredAnnotationBeanPostProcessor#postProcessProperties
    2. 根据依赖信息寻找依赖并完成注入。比如字段注入,参考AutowiredFieldElement#inject方法:
    @Override
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
       Field field = (Field) this.member;
       Object value;
       // ...
          try {
              DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
             // 寻找“依赖”,desc为"dataService"的DependencyDescriptor
             value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
          }
          
       }
       // ...
       if (value != null) {
          ReflectionUtils.makeAccessible(field);
          // 装配“依赖”
          field.set(bean, value);
       }
    }
    

    案例中的错误就发生在上述“寻找依赖”的过程中,DefaultListableBeanFactory#doResolveDependency

    当根据DataService类型找依赖时,会找出2个依赖:

    • CassandraDataService
    • OracleDataService

    在这样的情况下,如果同时满足以下两个条件则会抛出本案例的错误:

    • 调用determineAutowireCandidate方法来选出优先级最高的依赖,但是发现并没有优先级可依据。具体选择过程可参考
    DefaultListableBeanFactory#determineAutowireCandidate:
    protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
       Class<?> requiredType = descriptor.getDependencyType();
       String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
       if (primaryCandidate != null) {
          return primaryCandidate;
       }
       String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
       if (priorityCandidate != null) {
          return priorityCandidate;
       }
       // Fallback
       for (Map.Entry<String, Object> entry : candidates.entrySet()) {
          String candidateName = entry.getKey();
          Object beanInstance = entry.getValue();
          if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
                matchesBeanName(candidateName, descriptor.getDependencyName())) {
             return candidateName;
          }
       }
       return null;
    }
    

    优先级的决策是先根据@Primary,其次是@Priority,最后根据Bean名严格匹配。
    如果这些帮助决策优先级的注解都没有被使用,名字也不精确匹配,则返回null,告知无法决策出哪种最合适。

    @Autowired要求是必须注入的(required默认值true),或注解的属性类型并不是可以接受多个Bean的类型,例如数组、Map、集合。
    这点可以参考DefaultListableBeanFactory#indicatesMultipleBeans:

    private boolean indicatesMultipleBeans(Class<?> type) {
       return (type.isArray() || (type.isInterface() &&
             (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));
    }
    

    案例程序能满足这些条件,所以报错并不奇怪。而如果我们把这些条件想得简单点,或许更容易帮助我们去理解这个设计。就像我们遭遇多个无法比较优劣的选择,却必须选择其一时,与其偷偷地随便选择一种,还不如直接报错,起码可以避免更严重的问题发生。

    修正

    打破上述两个条件中的任何一个即可,即让候选项具有优先级或根本不选择。
    但并非每种条件的打破都满足实际需求:
    如可以通过使用**@Primary**让被标记的候选者有更高优先级,但并不一定符合业务需求,好比我们本身需要两种DB都能使用,而非不可兼得。

    @Repository
    @Primary
    @Slf4j
    public class OracleDataService implements DataService{
        //省略非关键代码
    }
    

    要同时支持多种DataService,不同情景精确匹配不同的DataService,可这样修改:

    @Autowired
    DataService oracleDataService;
    

    将属性名和Bean名精确匹配,就能实现完美的注入选择:

    • 需要Oracle时指定属性名为oracleDataService
    • 需要Cassandra时则指定属性名为cassandraDataService

    显式引用Bean时首字母忽略大小写

    还有另外一种解决办法,即采用@Qualifier显式指定引用服务,例如采用下面的方式:

    @Autowired()
    @Qualifier("cassandraDataService")
    DataService dataService;
    

    这样能让寻找出的Bean只有一个(即精确匹配),无需后续的决策过程:

    DefaultListableBeanFactory#doResolveDependency

    @Nullable
    public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
          @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
          //省略其他非关键代码
          //寻找bean过程
          Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
          if (matchingBeans.isEmpty()) {
             if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
             }
             return null;
          }
          //省略其他非关键代码
          if (matchingBeans.size() > 1) {
             //省略多个bean的决策过程,即案例1重点介绍内容
          } 
         //省略其他非关键代码
    }
    

    使用 @Qualifier 指定名称匹配,最终只找到唯一一个。但使用时,可能会忽略Bean名称首字母大小写。
    如:

    @Autowired
    @Qualifier("CassandraDataService")
    DataService dataService;
    

    运行报错:

    Exception encountered during context initialization - cancelling refresh
    attempt: org.springframework.beans.factory.UnsatisfiedDependencyException:
     Error creating bean with name 'studentController': Unsatisfied dependency
      expressed through field 'dataService'; nested exception is
       org.springframework.beans.factory.NoSuchBeanDefinitionException: No
        qualifying bean of type 'com.spring.puzzle.class2.example2.DataService'
         available: expected at least 1 bean which qualifies as autowire
          candidate. Dependency annotations:
           {@org.springframework.beans.factory.annotation.Autowired(required=true),
            @org.springframework.beans.factory.annotation.Qualifier(value=CassandraDataService)}
    

    若未显式指定 bean 名称,默认就是类名,不过首字母小写!

    假设要支持SQLServer,定义了一个名为SQLServerDataService的实现:

    @Autowired
    @Qualifier("sQLServerDataService")
    DataService dataService;
    

    依然出现之前错误,而若改成SQLServerDataService,则运行通过。
    这真是疯了呀!

    显式引用Bean时,首字母到底是大写还是小写?

    答疑

    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(),
    	descriptor);
    

    当因名称问题(例如引用Bean首字母搞错了)找不到Bean,会抛NoSuchBeanDefinitionException。

    不显式设置名字的Bean,其默认名称首字母到底是大写还是小写呢?
    Spring Boot应用会自动扫包,找出直接或间接标记了 @Component 的BeanDefinition。例如CassandraDataService、SQLServerDataService都被标记了@Repository,而Repository本身被@Component标记,所以都间接标记了@Component。

    一旦找出这些Bean信息,就可生成Bean名,然后组合成一个个BeanDefinitionHolder返回给上层:

    ClassPathBeanDefinitionScanner#doScan

    BeanNameGenerator#generateBeanName产生Bean名,有两种实现方式:

    因为DataService实现都是使用注解,所以Bean名称的生成逻辑最终调用的其实是

    AnnotationBeanNameGenerator#generateBeanName


    看Bean有无显式指明名称,若:


    • 用显式名称
    • 没有
      生成默认名称

    案例没有给Bean指名,所以生成默认名称,通过方法:

    buildDefaultBeanName


    首先,获取一个简短的ClassName,然后调用Introspector#decapitalize方法,设置首字母大写或小写,具体参考下面的代码实现:

    • 一个类名是以两个大写字母开头,则首字母不变
    • 其它情况下默认首字母变成小写

    SQLServerDataService的Bean,其名称应该就是类名本身,而CassandraDataService的Bean名称则变成了首字母小写(cassandraDataService)。

    修正

    引用处修正

    @Autowired
    @Qualifier("cassandraDataService")
    DataService dataService;
    

    定义处显式指定Bean名字,我们可以保持引用代码不变,而通过显式指明CassandraDataService 的Bean名称为CassandraDataService来纠正这个问题。

    @Repository("CassandraDataService")
    @Slf4j
    public class CassandraDataService implements DataService {
      //省略实现
    }
    

    如果你不太了解源码,不想纠结于首字母到底是大写还是小写,建议第二种方法

    引用内部类的Bean遗忘类名

    这就能搞定所有Bean显式引用不出 bug 吗?
    沿用上面案例,稍微再添加点别的需求,例如我们需要定义一个内部类来实现一种新的DataService,代码如下:

    public class StudentController {
        @Repository
        public static class InnerClassDataService implements DataService{
            @Override
            public void deleteStudent(int id) {
              //空实现
            }
        }
        // ...
     }
    

    这时一般都用下面的方式直接去显式引用这个Bean:

    @Autowired
    @Qualifier("innerClassDataService")
    DataService innerClassDataService;
    

    那直接采用首字母小写,这样就万无一失了吗?
    仍报错“找不到Bean”,why?

    答疑

    现在问题是“如何引用内部类的Bean”。
    在AnnotationBeanNameGenerator#buildDefaultBeanName,只关注了首字母是否小写,而在最后变换首字母前,有这么一行处理 class 名称的:
    我们可以看下它的实现:

    ClassUtils#getShortName


    假设是个内部类,例如下面的类名:

    com.javaedge.StudentController.InnerClassDataService
    

    经过该方法处理后,得到名称:

    StudentController.InnerClassDataService
    

    最后经Introspector.decapitalize首字母变换,得到Bean名称:

    studentController.InnerClassDataService
    

    所以直接使用 innerClassDataService 找不到想要的Bean。

    修正

    @Autowired
    @Qualifier("studentController.InnerClassDataService")
    DataService innerClassDataService;
    

    总结

    像第一个案例,同种类型的实现,可能不是同时出现在自己的项目代码中,而是有部分实现出现在依赖的类库。看来研究源码的确能让我们少写几个 bug!

    展开全文
  • @Autowired注解我们实际工作和学习中,用的是比较频繁的。这篇文章主要是结合源码了解它的实际工作过程即是怎么把我们需要的类注入到我们类中的。@Autowired注入过程,实际上分为三个方面:1、元信息解析;2、依赖...

            @Autowired注解我们实际工作和学习中,用的是比较频繁的。先了解下@Autowired注解,

    1、可以对Bean类成员变量、方法及构造函数进行标注,完成依赖注入的自动装配工作。使用@Autowired可以省略Bean类的待依赖注入对象的set方法,@Autowired默认情况下按照依赖注入对象的类型自动进行匹配。加入@Autowired注解的方式是在Bean类依赖注入对象的前面加上@Autowired语句。

    2、@Autowired还提供required的属性,用来处理当注入的Bean实例不存在的情况。required为true时,如果注入的Bean实例不存在,程序会抛出异常;required为false时,如果注入的Bean实例不存在,程序会忽略。由于默认情况下@Autowired是按类型匹配的(byType),如果需要按名称(byName)匹配,可以使用@Qualifier注解与@Autowired结合。

    下面结合源码了解它的实际工作过程即是怎么把我们需要的类注入到我们类中的。@Autowired注入过程,实际上分为三个方面:1、元信息解析;2、依赖查找;3、依赖注入(字段、方法)。

            元信息解析,在处理过程中,有一个类叫DependencyDescriptor,比如说我们的依赖描述器或者依赖描述符,那个里面就记录了我们注入的对象是关于那个字段,比如说我的字段名称以及它的字段相关的元信息,就是我们说java发射一个field字段,这个部分了解完之后它就会把相关的信息包括类所有的信息全部加入进来,帮助我们去分析,包括它的一个字段类型或者方法里面参数类型等等,

        依赖查找,依赖查找其实就是依赖处理

        依赖注入,@autowired可以注入在字段和方法里面,

    这个就是@Autowired的一个操作思路:解析谁要进行注入==》查找依赖的来源==》注入的一个对象。

    现在还是上源码,注意看注释部分

    一、@Autowired的源码,通过源码可以看到他可以应用在方法、字段、注解等

    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    
    	/**
    	 * Declares whether the annotated dependency is required.
    	 * <p>Defaults to {@code true}.
    	 */
    	boolean required() default true;
    
    }

    二、注解的实现类:AutowiredAnnotationBeanPostProcessor,通过它的构造方法,可以看到它不仅支持@Autowired还支持@Value注解。

    public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
    		implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {  
      //省略部分代码
    
        /**
    	 * Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's
    	 * standard {@link Autowired @Autowired} and {@link Value @Value} annotations.
    	 * <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
    	 * if available.
    	 */
    	@SuppressWarnings("unchecked")
    	public AutowiredAnnotationBeanPostProcessor() {
    		this.autowiredAnnotationTypes.add(Autowired.class);
    		this.autowiredAnnotationTypes.add(Value.class);
    		try {
    			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
    					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
    			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    		}
    		catch (ClassNotFoundException ex) {
    			// JSR-330 API not available - simply skip.
    		}
    	}
    }

    三、还是AutowiredAnnotationBeanPostProcessor,它实现了接口类MergedBeanDefinitionPostProcessor的方法postProcessMergedBeanDefinition,这个方法是合并我们定义类的信息,比如:一个类集成了其它类,这个方法会把父类属性和信息合并到子类中。代码如下:

    
    	@Override
    	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
            //查找注解的元信息,传入了bean的名称、类型
    		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    		metadata.checkConfigMembers(beanDefinition);
    	}
    
       /**
       *
       *查找注解元信息
       */
        private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    		// Fall back to class name as cache key, for backwards compatibility with custom callers.
            //先读缓存
    		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    		// Quick check on the concurrent map first, with minimal locking.
    		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
    			synchronized (this.injectionMetadataCache) {
    				metadata = this.injectionMetadataCache.get(cacheKey);
    				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
    					if (metadata != null) {
    						metadata.clear(pvs);
    					}
                        //把查找出来的元信息进行构建
    					metadata = buildAutowiringMetadata(clazz);
    					this.injectionMetadataCache.put(cacheKey, metadata);
    				}
    			}
    		}
    		return metadata;
    	}
    
    //构建注解元信息   
    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
            //判断是否符合条件注解类型(@AutoWired和@Value)
    		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
    			return InjectionMetadata.EMPTY;
    		}
    
    		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    		Class<?> targetClass = clazz;
    
    		do {
    			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
                //先处理注解字段
    			ReflectionUtils.doWithLocalFields(targetClass, field -> {
                    //是不是要找的字段
    				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
    				if (ann != null) {
                        //静态字段不支持@Autowired
    					if (Modifier.isStatic(field.getModifiers())) {
    						if (logger.isInfoEnabled()) {
    							logger.info("Autowired annotation is not supported on static fields: " + field);
    						}
    						return;
    					}
    					boolean required = determineRequiredStatus(ann);
    					currElements.add(new AutowiredFieldElement(field, required));
    				}
    			});
    
               //再处理注解方法
    			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
    				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
    				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
    					return;
    				}
    				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
    				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                        //@Autowired不支持静态方法
    					if (Modifier.isStatic(method.getModifiers())) {
    						if (logger.isInfoEnabled()) {
    							logger.info("Autowired annotation is not supported on static methods: " + method);
    						}
    						return;
    					}
    					if (method.getParameterCount() == 0) {
    						if (logger.isInfoEnabled()) {
    							logger.info("Autowired annotation should only be used on methods with parameters: " +
    									method);
    						}
    					}
    					boolean required = determineRequiredStatus(ann);
    					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
    					currElements.add(new AutowiredMethodElement(method, required, pd));
    				}
    			});
    
    			elements.addAll(0, currElements);
    			targetClass = targetClass.getSuperclass();
    		}
    		while (targetClass != null && targetClass != Object.class);
    
    		return InjectionMetadata.forElements(elements, clazz);
    	}
    
    /**
    	 * Class representing injection information about an annotated field.
    	 */
    	private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
    
    		private final boolean required;
    
    		private volatile boolean cached = false;
    
    		@Nullable
    		private volatile Object cachedFieldValue;
    
    		public AutowiredFieldElement(Field field, boolean required) {
    			super(field, null);
    			this.required = required;
    		}
    
    		@Override
    		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    			Field field = (Field) this.member;
    			Object value;
    			if (this.cached) {
    				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    			}
    			else {
    				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    				desc.setContainingClass(bean.getClass());
    				Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
    				Assert.state(beanFactory != null, "No BeanFactory available");
    				TypeConverter typeConverter = beanFactory.getTypeConverter();
    				try {
    					value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    				}
    				catch (BeansException ex) {
    					throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
    				}
    				synchronized (this) {
    					if (!this.cached) {
    						if (value != null || this.required) {
    							this.cachedFieldValue = desc;
    							registerDependentBeans(beanName, autowiredBeanNames);
    							if (autowiredBeanNames.size() == 1) {
    								String autowiredBeanName = autowiredBeanNames.iterator().next();
    								if (beanFactory.containsBean(autowiredBeanName) &&
    										beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
    									this.cachedFieldValue = new ShortcutDependencyDescriptor(
    											desc, autowiredBeanName, field.getType());
    								}
    							}
    						}
    						else {
    							this.cachedFieldValue = null;
    						}
    						this.cached = true;
    					}
    				}
    			}
    			if (value != null) {
    				ReflectionUtils.makeAccessible(field);
    				field.set(bean, value);
    			}
    		}
    	}
    
    
    	/**
    	 * Class representing injection information about an annotated method.
    	 */
    	private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
    
    		private final boolean required;
    
    		private volatile boolean cached = false;
    
    		@Nullable
    		private volatile Object[] cachedMethodArguments;
    
    		public AutowiredMethodElement(Method method, boolean required, @Nullable PropertyDescriptor pd) {
    			super(method, pd);
    			this.required = required;
    		}
    
    		@Override
    		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    			if (checkPropertySkipping(pvs)) {
    				return;
    			}
    			Method method = (Method) this.member;
    			Object[] arguments;
    			if (this.cached) {
    				// Shortcut for avoiding synchronization...
    				arguments = resolveCachedArguments(beanName);
    			}
    			else {
    				int argumentCount = method.getParameterCount();
    				arguments = new Object[argumentCount];
    				DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
    				Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
    				Assert.state(beanFactory != null, "No BeanFactory available");
    				TypeConverter typeConverter = beanFactory.getTypeConverter();
    				for (int i = 0; i < arguments.length; i++) {
    					MethodParameter methodParam = new MethodParameter(method, i);
    					DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
    					currDesc.setContainingClass(bean.getClass());
    					descriptors[i] = currDesc;
    					try {
    						Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
    						if (arg == null && !this.required) {
    							arguments = null;
    							break;
    						}
    						arguments[i] = arg;
    					}
    					catch (BeansException ex) {
    						throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
    					}
    				}
    				synchronized (this) {
    					if (!this.cached) {
    						if (arguments != null) {
    							DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
    							registerDependentBeans(beanName, autowiredBeans);
    							if (autowiredBeans.size() == argumentCount) {
    								Iterator<String> it = autowiredBeans.iterator();
    								Class<?>[] paramTypes = method.getParameterTypes();
    								for (int i = 0; i < paramTypes.length; i++) {
    									String autowiredBeanName = it.next();
    									if (beanFactory.containsBean(autowiredBeanName) &&
    											beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
    										cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
    												descriptors[i], autowiredBeanName, paramTypes[i]);
    									}
    								}
    							}
    							this.cachedMethodArguments = cachedMethodArguments;
    						}
    						else {
    							this.cachedMethodArguments = null;
    						}
    						this.cached = true;
    					}
    				}
    			}
    			if (arguments != null) {
    				try {
    					ReflectionUtils.makeAccessible(method);
    					method.invoke(bean, arguments);
    				}
    				catch (InvocationTargetException ex) {
    					throw ex.getTargetException();
    				}
    			}
    		}
    
    		@Nullable
    		private Object[] resolveCachedArguments(@Nullable String beanName) {
    			Object[] cachedMethodArguments = this.cachedMethodArguments;
    			if (cachedMethodArguments == null) {
    				return null;
    			}
    			Object[] arguments = new Object[cachedMethodArguments.length];
    			for (int i = 0; i < arguments.length; i++) {
    				arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
    			}
    			return arguments;
    		}
    	}

    这个方法找到了所有需要注解注入的元信息并进行解析,这个过程也是一个依赖查找过程。至于注入的过程是怎么调用的呐,接下来看这个方法:

        //属性处理过程,元信息查找、解析、注入
    	@Override
    	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
            //元信息查找、解析,在上一步已经分析过了
    		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    		try {
                //进行注入
    			metadata.inject(bean, beanName, pvs);
    		}
    		catch (BeanCreationException ex) {
    			throw ex;
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    		}
    		return pvs;
    	}
    
    
    public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    		Collection<InjectedElement> checkedElements = this.checkedElements;
    		Collection<InjectedElement> elementsToIterate =
    				(checkedElements != null ? checkedElements : this.injectedElements);
    		if (!elementsToIterate.isEmpty()) {
    			for (InjectedElement element : elementsToIterate) {
    				if (logger.isTraceEnabled()) {
    					logger.trace("Processing injected element of bean '" + beanName + "': " + element);
    				}
                    //注入
    				element.inject(target, beanName, pvs);
    			}
    		}
    	}
    
           /**
    		 * Either this or {@link #getResourceToInject} needs to be overridden.
    		 */
    		protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
    				throws Throwable {
                //字段注入
    			if (this.isField) {
    				Field field = (Field) this.member;
                    //设置字段权限为可以访问权限
    				ReflectionUtils.makeAccessible(field);
    				field.set(target, getResourceToInject(target, requestingBeanName));
    			}
    			else {//方法注入
                    
    				if (checkPropertySkipping(pvs)) {
    					return;
    				}
    				try {
    					Method method = (Method) this.member;
                        //设置方法权限为可以访问权限
    					ReflectionUtils.makeAccessible(method);
    					method.invoke(target, getResourceToInject(target, requestingBeanName));
    				}
    				catch (InvocationTargetException ex) {
    					throw ex.getTargetException();
    				}
    			}
    		}
    

    经过上述分析,@Autowired注解,先通过postProcessMergedBeanDefinition方法->findAutowiringMetadata方法分析查找元信息,并且经过反射写入类信息,再次通过inject方法进行注入。

     

    本文如果不对之处,请私信我,谢谢阅读

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 409,593
精华内容 163,837
关键字:

autowired