Spring自定义@Value属性注入逻辑

Spring内@Value注解默认从Spring环境内(主要是Properties)获取String类型的配置值赋值给Bean内简单数据类型属性,会使用TypeConverter转换String类型以适配属性值。

原理是Spring容器在实例化所有普通类型的Bean之前,添加了一个StringValueResolver接口的实现类:

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        ......
        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return getEnvironment().resolvePlaceholders(strVal);
                }
            });
        }
        ......
        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }

重写StringValueResolver的resolveStringValue方法,在解析@Value类型的属性时,替换目标的值。内部通过类似PropertyPlaceholderConfigurer形式来替换{}内部的字符串,所以要求@Value内的value格式为${}形式。

自定义StringValueResolver的实现类,支持任意格式的字符串替换,以及任意形式的配置获取。


import com.bob.web.mvc.mapper.BankUserMapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.util.StringValueResolver;

/**
 * 通过{@link org.springframework.beans.factory.annotation.Value}实现自定义属性注入
 * 属性值可以从环境变量,磁盘,内存,及网络等获取
 *
 * @author wb-jjb318191
 * @create 2018-02-27 11:32
 */
public class CustomizedStringValueResolver implements StringValueResolver, BeanFactoryAware {

    @Autowired
    private BankUserMapper bankUserMapper;

    @Override
    public String resolveStringValue(String strVal) {
        String value = null;
        if (strVal.startsWith("#{") && strVal.endsWith("}")) {
            String key = strVal.substring(2, strVal.length() - 1);
            value = bankUserMapper.selectByPrimaryKey(Integer.valueOf(key)).getAge().toString();
        }
        return value == null ? strVal : value;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        ((DefaultListableBeanFactory)beanFactory).addEmbeddedValueResolver(this);
    }
}

我在此处时使用Mapper从数据库获取,可以自定义任意形式的数据来源。

第二步,要确定此StringValueResolver的实例化顺序,太早了,会导致CustomizedStringValueResolver
依赖的所有Bean提前实例化,很可能会有意想不到的问题,比如这里的Mapper实例化,Mapper依赖SqlSessionFactory等等,这种间接的依赖都会提前触发实例化,所以实例化CustomizedStringValueResolver 的顺序肯定不能太早。同样,也不能太晚,如果CustomizedStringValueResolver 实例化晚于Bean A,而A里面需要用到CustomizedStringValueResolver 的resolveStringValue()方法,那么A里面的@Value的解析就会忽略CustomizedStringValueResolver 的实现。

import java.util.concurrent.atomic.AtomicBoolean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * 自定义StringValueResolver注册器
 *
 * @author Administrator
 * @create 2018-03-03 9:31
 */
public class StringValueResolverRegistrar extends InstantiationAwareBeanPostProcessorAdapter {

    @Autowired
    private DefaultListableBeanFactory beanFactory;

    private AtomicBoolean registerLock = new AtomicBoolean(false);

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanFactory.isConfigurationFrozen() && registerLock.compareAndSet(false, true)) {
            beanFactory.getBean(CustomizedStringValueResolver.class);
        }
        return super.postProcessBeforeInstantiation(beanClass, beanName);
    }
}

借助BeanPostProcessor的特性,将CustomizedStringValueResolver实例化的时机选择在:
Spring实例化完所有BeanFactoryPostProcessor,BeanPostProcessor后,开始实例化第一个普通Bean,且这个Bean还未有实际对象。

选择这个时机的用意在于CustomizedStringValueResolver能覆盖所有的非PostProcessor类型的Bean,而又不早于BeanProcessor的实例化。

有一点要注意,不要在CustomizedStringValueResolver依赖的Bean内使用其功能,本例中CustomizedStringValueResolver依赖BankUserMapper ,而Mapper又间接依赖SqlSessionFactory,也就是实例化SqlSessionFactory时,不要用到CustomizedStringValueResolver的功能,因为此时BankUserMapper 实例化,会报NullpointException异常。

最后就是使用了。

@Configuration

public class WebContextConfig extends WebMvcConfigurerAdapter {
    ......
    @Bean
    public StringValueResolverRegistrar stringValueResolverRegister() {
        return new StringValueResolverRegistrar();
    }

    @Bean
    public SpringBeanInstanceAccessor customizedBeanFactoryUtils() {
        return new SpringBeanInstanceAccessor();
    }
}

@RestController
@RequestMapping("/async")
public class AsyncController {

    @Value("#{1}")
    private Integer userName;

    ......
}

Spring在实例化AsyncController 时,会用CustomizedStringValueResolver来解析userName属性值。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Spring Boot ,您可以使用 `@PostConstruct` 和 `@EventListener` 注解来实现属性注入成功后的回调事件。 示例代码如下: ```java @Component public class MyService { @Value("${my.property}") private String myProperty; @PostConstruct public void init() { // 在属性注入成功后执行 System.out.println("My property value is: " + myProperty); } @EventListener(ApplicationReadyEvent.class) public void onApplicationReady() { // 在应用启动完成后执行 System.out.println("Application is ready!"); } } ``` 在上面的示例,`@PostConstruct` 注解用于在属性注入成功后执行 `init()` 方法,而 `@EventListener` 注解用于在应用启动完成后执行 `onApplicationReady()` 方法。您可以根据您的需求在这些方法添加自己的业务逻辑。 ### 回答2: 在Spring Boot,当属性注入成功后,可以使用事件机制进行回调。具体步骤如下: 1. 创建一个自定义事件类,该类继承自ApplicationEvent,并在该类定义需要回调的方法。 ```java public class CustomEvent extends ApplicationEvent { public CustomEvent(Object source) { super(source); } // 定义需要回调的方法 public void callbackMethod() { // 处理自定义事件的逻辑 System.out.println("属性注入成功,执行回调方法"); } } ``` 2. 在需要进行回调的地方,使用ApplicationEventPublisher对象发布自定义事件。 ```java @RestController public class UserController { @Autowired private ApplicationEventPublisher applicationEventPublisher; @PostMapping("/user") public void createUser() { // 创建自定义事件对象 CustomEvent event = new CustomEvent(this); // 发布自定义事件 applicationEventPublisher.publishEvent(event); } } ``` 3. 创建一个事件监听器,该监听器实现ApplicationListener接口,并在onApplicationEvent方法进行事件的监听和处理。 ```java @Component public class CustomEventListener implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent event) { // 获取事件源对象 Object source = event.getSource(); // 执行回调方法 if (source instanceof UserController) { UserController userController = (UserController) source; userController.callbackMethod(); } } } ``` 这样,在属性注入成功后,会触发自定义事件的发布,事件监听器会接收到该事件并执行回调方法,从而实现属性注入成功后的回调事件。 ### 回答3: Spring Boot 提供了一种方便的方式来注册属性注入成功后的回调事件。在应用程序的配置类上使用 `@ConfigurationPropertiesBinding` 注解,将属性绑定的转换器注册为一个 `Converter` bean,然后使用 `@PostConstruct` 注解将回调事件的处理方法标记为初始化方法。 首先,我们创建一个类来处理属性注入成功后的回调事件。在这个类,我们使用 `@ConfigurationPropertiesBinding` 注解将属性绑定的转换器注册为一个 `Converter` bean,然后在 `@PostConstruct` 注解的方法处理回调事件。 ```java @Configuration public class PropertyCallbackConfig { @Bean public ConversionService conversionService() { DefaultConversionService conversionService = new DefaultConversionService(); conversionService.addConverter(new MyPropertyConverter()); return conversionService; } @Bean public PropertyCallbackHandler propertyCallbackHandler() { return new PropertyCallbackHandler(); } @ConfigurationPropertiesBinding public static class MyPropertyConverter implements Converter<String, MyProperty> { @Override public MyProperty convert(String source) { // 根据需要的逻辑进行属性转换 return new MyProperty(source); } } public static class MyProperty { private String value; public MyProperty(String value) { this.value = value; } // getter 和 setter 方法省略 } public static class PropertyCallbackHandler { @Autowired private MyProperty myProperty; @PostConstruct public void handlePropertyCallback() { // 属性注入成功后的处理逻辑 System.out.println("属性注入成功,值为:" + myProperty.getValue()); } } } ``` 在这个例子,`MyPropertyConverter` 类实现了 `Converter` 接口来进行属性转换,`MyProperty` 类用于存储属性的值。`PropertyCallbackHandler` 类使用 `@Autowired` 注解将属性注入到 `myProperty` 字段,并使用 `@PostConstruct` 注解的方法来处理回调事件。 最后,我们需要在应用程序的启动类上使用 `@EnableConfigurationProperties` 注解来启用属性注入。 ```java @SpringBootApplication @EnableConfigurationProperties public class MyApplicaton { public static void main(String[] args) { SpringApplication.run(MyApplicaton.class, args); } } ``` 通过使用 Spring Boot 提供的这种方式,我们可以很方便地注册属性注入成功后的回调事件。当属性注入成功时,回调事件的处理方法将被自动调用,并可以在其进行需要的处理逻辑

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值