数据存储和文件下载异步遇到的问题之注解不生效

数据存储和文件下载异步遇到的问题之注解不生效

初始设计时,直接将需要的Bean进行了@AutoWired注解,yml配置文件也进行了@Value("${isAsync}")注解,但进行调试的发现相应的Bean和读取配置的属性全部返回null,在查询相应的资料后得到解决。接下来分析原因。

1.读取配置的方式

1.@Value的方式

@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;

这种生效需要什么条件呢?

需要将类注册在spring的上下文中,即在springboot实现自动装配的注解@SpringBootApplication中可以看到有三个核心的注解:

@Configuration@EnableAutoConfiguration@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  • @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
  • @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean

也就是说你在启动这个自动装配的时候,需要将类注解为@Component@Service,@Controller,它才会被扫描到,但由于项目原先设计的类中,我的构造函数为有参,需要注解成bean的话,那么你就需要逐层的对他进行一个注解,不然ioc容器无法找到相应的依赖进行注入,但我这里想的一点就是说,如果你把这个接口注解成bean了,那么在属性注入初始化之后,如果这个接口没有重写对吧,那么误调用了没有重写的接口,肯定是与预期的结果就不一样的,所以在目前的项目中不采用注解成bean的方法。
在这里插入图片描述

2.@ConfigurationProperties

这种方式与@Value的原理是一样的,也是需要将类注解为组件bean,才会被扫描,不同的是它只需要在@ConfigurationProperties后加一个prefix前缀,那么对当前的bean都生效,只需要获取对应的value值即可,如下

@Component
@ConfigurationProperties(prefix = "yancloud.dataretention")
public class DataRetentionProperties {

    private String storage;
    private ThirdApi thirdApi;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WdnP0wBi-1625140511503)(image-20210701190354619.png)]

3.Environment的方式

@Configuration("MyPropertiesConfig")
public class MyPropertiesConfig {
    @Resource
    private Environment env;

    @PostConstruct
    public void setProperties() {
        PropertiesUtil.setEnvironment(env);

    }
}
@Component("PropertiesUtil")
public class PropertiesUtil {
    private static Environment env = null;

    public static void setEnvironment(Environment env) {
        PropertiesUtil.env = env;
    }

    public static String getProperty(String key) {
        return PropertiesUtil.env.getProperty(key);
    }
}

通过@Autowired的方式对env进行初始化,而且那个Environment默认实现的类是AbstractEnvironment,所有environment实现都会继承AbstractEnvironment,而且AbstractEnvironment中就包含了MutablePropertySources(即存储配置文件的对象),所以这就是为什么从environment中可以获取所有配置的原因了。@PostConstruct的作用是让读取配置在bean示例生成之后执行,这点是显然的,bean实例化初始化才会取调用配置文件进行读取配置。不然的话你先读取配置,再实例化,这时候bean的属性就是默认初始化的值而不是读取配置的值了。从注解上来看这个注解也是属于一种后置增强的功能了,实例化后置增强。
具体参考这篇文章:https://blog.csdn.net/shijianjinghun/article/details/110440733

那么这个时候就可以不需要注解直接调用PropertiesUtil的getProperty()方法取相应的value进行赋值

String isAsync = PropertiesUtil.getProperty("isAsync");

2.@AutoWired注解生效的条件

上面提到由于我们需要改造的类中调用的接口中有的接口没有实现,导致无法进行@Service等注解成一个bean,ioc容器里面(Beanfactory)都不存在这个bean,没有进行管理,所以@Autowired自然不会生效了(为null),因此我们必须从别的方法入手。看如下方法通过实现ApplicationContextAware的接口来实现获取ApplicationContext中的所有bean。

@Component("SpringUtil")
public class SpringUtil implements ApplicationContextAware{

    private static ApplicationContext applicationContext;

    @Override
    public  void setApplicationContext(ApplicationContext applicationContext) throws BeansException {		
        //ioc为空,则将加载的ioc赋值给全局静态ioc
        if(SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        ApplicationContext applicationContext = getApplicationContext();
        return applicationContext.getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgNAPdjS-1625140511511)(image-20210701194606906.png)]

为什么重写AppplicationContextAware就可以实现beanFactory无法实例化的bean呢?

​ ApplicationContext的BeanFactory 的子类, 拥有更强大的功能,ApplicationContext可以在服务器启动的时候自动实例化所有的bean,而 BeanFactory只有在调用getBean()的时候才去实例化那个bean, 这也是我们为什么要得到一个ApplicationContext对象, 事实上Spring相关的web应用默认使用的是ApplicationContext对象去实例化bean, 换一句话说, 在服务器启动的时候,Spring容器就已经实例化好了一个ApplicationContext对象,所以我们要在老的代码里尝试去获取这个对象。 但是如何才能得到一个ApplicationContext对象呢?方法很多,最常用的办法就是用ClassPathXmlApplicationContext, FileSystemClassPathXmlApplicationContext, FileSystemXmlApplicationContext 等对象去加载Spring配置文件,这样做也是可以, 但是在加载Spring配置文件的时候,就会生成一个新的ApplicaitonContext对象而不是Spring容器帮我们生成的哪一个, 这样就产生了冗余, 所以我们在这里不采用这种加载文件的方式,我们使用ApplicationContextAware让Spring容器传递自己生成的ApplicationContext给我们, 然后我们把这个ApplicationContext设置成一个类的静态变量, 这样我们就随时都可以在老的代码里得到Application的对象了

3.一点扩展

Spring中@Value中#和$有什么区别?
@Value的值有两类:
① ${ property : default_value }
② #{ obj.property? : default_value }

@Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量,而@Value("${}")是用来读取yml配置文件对应的value值,通俗的讲一个是到内部找bean的属性值,另一个是取外部配置找key对应的value

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值