spring容器加载完毕做一件事情

很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们这个类有可能已经初始化完成,但是引用的其他类不一定初始化完成,所以发生了空指针错误,解决方案如下:

Spring动态替换Properties配置变量

有如下需求,在Spring启动的时候,我们需要修改某个指定的配置的值,从而达到动态加载变量的效果:

很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们这个类有可能已经初始化完成,但是引用的其他类不一定初始化完成,所以发生了空指针错误,解决方案如下:

    @Value("${destination}")
    public String destination;

1.实现方法有如下:

  • 在对应的Spring Bean实例化之后,通过反射动态的修改实例的参数值
  • 动态的修改PropertySource

具体的类如下:

public interface HelloService {

    /**
     * say hello
     */
    void sayHello();
}


@Service
public class HelloServiceImpl implements HelloService{

    @Value("${destination}")
    private String destination;

    @Override
    public void sayHello() {
        System.out.println("Hello, let us go to " + destination);
    }
}

第一种方法实现

方式1:
定义一个应用事件,监听应用上下文刷新事件,然后我们可以通过这个事件拿到ApplicationContext上下文,进而去获取对应的SpringBean,然后进行反射赋值操作。

public class LocalApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        Object helloService = event.getApplicationContext().getBean(HelloService.class);
        if (helloService instanceof HelloServiceImpl) {
            try {
                Field field = HelloServiceImpl.class.getDeclaredField("destination");
                field.setAccessible(true);
                ReflectionUtils.setField(field, helloService, "广东深圳");
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }
}

有了这个监听事件,我们需要把这个监听器添加到Spring的监听器集合中,可以通过启动类的启动方法进行添加:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(DemoApplication.class);
        app.addListeners(new LocalApplicationListener());
        app.run(args);
    }
}

方式2:
定义一个类实现BeanPostProcessor接口,在接口中进行反射操作。BeanPostProcessor是Spring提供的一个Bean扩展接口,可以通过该接口实现一些Bean创建之前和创建之后的操作。

@Component
public class LocalBeanProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof HelloServiceImpl) {
            try {
                Field field = HelloServiceImpl.class.getDeclaredField("destination");
                field.setAccessible(true);
                ReflectionUtils.setField(field, bean, "广东广州");
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return bean;
    }
}

第二种实现方法
通过了解Spring的加载流程,我们得知Spring在上下文准备完毕(配置信息解析完毕并创建好了应用上下文)之后,会调用推送一个ApplicationEnvironmentPreparedEvent,从这个事件中,我们可以获取到ConfigurableEnvironment对象,而ConfigurableEnvironment对象可以获取到MutablePropertySources。对于MutablePropertySources,它包含了Spring的所有 的配置信息,包括我们启动应用的application.properties文件的配置信息。具体代码如下:

public class LocalEnvironmentPrepareEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        MutablePropertySources propertySources = event.getEnvironment().getPropertySources();
        for (PropertySource<?> propertySource : propertySources) {
            boolean applicationConfig = propertySource.getName().contains("applicationConfig");
            if (!applicationConfig) {
               continue;
            }
            Object property = propertySource.getProperty("destination");
            Map<String, OriginTrackedValue> source = (Map<String, OriginTrackedValue>) propertySource.getSource();
            OriginTrackedValue originTrackedValue = source.get("destination");
            OriginTrackedValue newOriginTrackedValue = OriginTrackedValue.of("中国香港", originTrackedValue.getOrigin());
            source.put("destination", newOriginTrackedValue);
            System.out.println(property);
        }
    }
}

同样的我们需要把该监听器添加到Spring中:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(DemoApplication.class);
        app.addListeners(new LocalEnvironmentPrepareEventListener());
        app.run(args);
    }
}

知名的分布式配置管理工具Apollo就是通过反射来实现配置参数的动态修改的,Apollo实现了BeanPostProcessor接口,这样它就可以把所有的Bean和@Value的key的关系保存起来,类似于Map<String, List<Bean>>,当配置中心的配置被改动的时候,就发一个通知给对应的服务,然后由服务自己去拉取配置参数,重新赋值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值