美文网首页
@Value 和 @Bean 的执行顺序问题

@Value 和 @Bean 的执行顺序问题

作者: zhengaoly | 来源:发表于2021-12-09 15:13 被阅读0次

本篇内容主要讲解 “如何理解 @Value 和 @Bean 的执行顺序问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解 @Value 和 @Bean 的执行顺序问题” 吧!

问题描述

使用 @Autowired 处理多个同种类型的 bean,出现 @Value 和 @Bean 的执行顺序问题。

先说结论

@Bean和@Value在同一个文件时,先执行@Value进行注入,在执行@Bean

@Bean和@Value不在同一个文件时,先执行@Bean,在执行@Bean进行注入

根据配置文件初始化Bean

Springboot 中使用 @Configruation 和 @Bean 一起将 Bean 注册到 ioc 容器中,而 @Value 常用于将 yml 配置文件中的配置信息注入到类的成员变量中。当 @Configruation、@Bean 和 @Value 出现在同一个类中时,@Bean 会比 @Value 先执行,这会导致当 @Bean 注解的方法中用到 @Value 注解的成员变量时,无法注入(null)的情况。例如在为 Feign 创建配置类,以实现 Feign 的权限验证时,需要将 yml 文件中的用户名和密码注入到配置类的成员变量中,@Bean 注解方法则依赖于用户名和密码产生 Bean 的实例。

@Configuration
public class FeignConfig implements BeanFactoryPostProcessor {
  @Value("${spring.cloud.config.username}")
  private static String username;
 
  @Value("${spring.cloud.config.username}")
  private static String password;
 
  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    //这时username和password无法被及时注入
    return new BasicAuthRequestInterceptor(username, password);
  }
}

解决方法

第一种:

直接在 @Bean 方法的参数上使用 @Value 注入 username 和 password

@Configuration
public class FeignConfig implements BeanFactoryPostProcessor {
 
  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor(@Value("${spring.cloud.config.username}") String username,
      @Value("${spring.cloud.config.password}") String password) {
    //这时username和password可以注入
    return new BasicAuthRequestInterceptor(username, password);
  }
}

第二种:

重新写一个用户名和密码的类 AuthConfig,然后用 @AutoWired 通过注入 AuthConfig 的对象来实现用户名和密码的注入,同样要在方法的参数上注入。

@Component
public class AuthConfig {
  @Value("${spring.cloud.config.username}")
  private String username;
 
  @Value("${spring.cloud.config.password}")
  private String password;
 
   //...省略getter和setter方法
}
 
@Configuration
public class FeignConfig implements BeanFactoryPostProcessor {
 
 
  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor(@Autowired AuthConfig authConfig) {
    return new BasicAuthRequestInterceptor(authConfig.getUsername(), authConfig.getPassword());
  }
}

这两种方法的思路应该都是利用 Springboot 对依赖关系的解析,当一个类对另一个类有明显依赖关系时,会改变 bean 加载的优先级?待深入研究
此处可以参考,springbean控制加载顺序的文章

https://www.jianshu.com/p/68f31019b75b

以下为@Value和@Bean执行顺序的验证

首先使用扫描包 + 注解的方式注册 User 类型的不同 bean, 分别是 user、user1, 注册方式如下

package com.fanyinhang.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@NoArgsConstructor
@Data
@AllArgsConstructor
@Component
public class User {
    private Integer id;
    private String name;
}

该方式得到 User 类型的名为 user 的 bean

package com.fanyinhang.config;
import com.fanyinhang.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value={"com.fanyinhang.dao"})
public class AnnotationConfig {
    @Bean()
    public User user1(){
        return new User(2,"李四");
    }
}

UserDao 配置如下:

package com.fanyinhang.dao;
import com.fanyinhang.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;


@Repository
public class UserDao {
    @Autowired()
    private User user1;
    
    @Override
    public String toString() {
        return "UserDao{" +
                "user1=" + user1 +
                '}';
    }
}
import com.fanyinhang.config.AnnotationConfig;
import com.fanyinhang.dao.UserDao;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test3 {
    @Test
    public void testAutowired(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationConfig.class);
        UserDao userDao = context.getBean(UserDao.class);
        System.out.println(userDao);
    }
}

输出结果如下:

UserDao{user1=User(id=2, name = 李四)}

没有加入 @Value 注解时是没有问题的,但是加入了 @Value 之后

package com.fanyinhang.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@NoArgsConstructor
@Data
@AllArgsConstructor
@Component
public class User {
    @Value("1")
    private Integer id;
    @Value("张三")
    private String name;
}

再次运行 testWired 方法后

结果输出如下:

UserDao{user1=User(id=1, name = 张三)}

为什么会出现这种情况?
一开始,怎么也想不通,查看网上的资料大多数是说 @Bean 和 @Value 有执行顺序这一说法。

为了验证这一说法,做个对比试验

去掉了一个 @Value(“张三”)

package com.fanyinhang.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@NoArgsConstructor
@Data
@AllArgsConstructor
@Component
public class User {
    @Value("1")
    private Integer id;
    
    private String name;
}
结果输出如下:

UserDao{user1=User(id=1, name = 李四)}

问题原因
@Value 和 @Bean 在不同文件下时,@Bean 比 @Value 先执行。这样就回导致 @Bean 注入的值失效。

解决办法
网上说 @Value 和 @Bean 在不同文件下时,@Bean比@Value 先执行, 因此,我做了如下设置

把 User.java 下的 @Value 注解去掉,而是将 @Value 注解放在 @bean 同一文件下

package com.fanyinhang.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

@NoArgsConstructor
@Data
@AllArgsConstructor
@Component
public class User {
    private Integer id;
    private String name;
}
package com.fanyinhang.config;
import com.fanyinhang.bean.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value={"com.fanyinhang.dao"})
public class AnnotationConfig {
    @Bean()
    public User user1(@Value("1") Integer id,@Value("张三") String name){
        return new User(2,"李四");
    }
}

此时再运行测试方式,输出结果如下:

UserDao{user1=User(id=2, name = 李四)}

相关文章

网友评论

      本文标题:@Value 和 @Bean 的执行顺序问题

      本文链接:https://www.haomeiwen.com/subject/csbcfrtx.html