@ConfigurationProperties注解

该文章详细解释了SpringBoot如何通过BeanPostProcessor接口在Bean初始化前后执行逻辑,特别是用于处理@ConfigurationProperties的类。作者创建了一个自定义注解@Config和相应的处理器ConfigPostProcess,实现了从配置文件中读取属性并注入到带有@Config注解的Bean中。示例代码展示了如何绑定配置值到Bean的字段上。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:
https://blog.youkuaiyun.com/yanluandai1985/article/details/99446060.

原理

SpringBoot主要帮助我们做了两件事情。
     1、获取到使用@ConfigurationProperties的类。
     2、解析配置文件,并把对应的值设置到我们的Bean中。
按照源码提供的实现思路,其核心就是对Bean的声明周期的管理。主要涉及一个叫做 BeanPostProcessor 的接口,可以在Bean初始化的时候,之前与之后实现一些逻辑。
public interface BeanPostProcessor {
    //初始化之前执行
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
 
    //初始化之后执行
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
 
}

自己div写一个实现

注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
 
    /**
     * 配置属性 的前缀
     */
    String prefix();
}

实现类

// An highlighted block
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

@Component
public class ConfigPostProcess implements BeanPostProcessor {

	private static final Logger LOGGER = LoggerFactory.getLogger(ConfigPostProcess.class);

	private static final String FILE_NAME = "config.properties";

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		//获取使用了我们自定义注解@Config的类
		Config configAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), Config.class);
		//如果这个对象使用了此注解
		if (configAnnotation != null) {
			LOGGER.info("当前操作的类:{}", beanName);
			//解析配置文件,并将解析结果放入Map中
			Map<String, String> configProperties = getConfigPropertiesFromFile(configAnnotation);
			//将对应的值,使用反射技术,注入到这个bean中
			bindBeanValue(bean, configProperties);
		}

		return bean;
	}

	/**
	 * 将对应的值,使用反射技术,注入到这个bean中
	 */
	private void bindBeanValue(Object bean, Map<String, String> configProperties) {
		if (configProperties.size() > 0) {
			configProperties.forEach((key, value) -> {
				setFieldValueByFieldName(key, bean, value);
			});
		}
	}

	/**
	 * 从配置文件中读取配置好的键值对,并放入到Map中
	 */
	private Map<String, String> getConfigPropertiesFromFile(Config configAnnotation) {
		//get prefix from annotation
		String prefix = configAnnotation.prefix();

		//read value from resource file
		Properties properties = getClassNameFromResource(FILE_NAME);
		Map<String, String> configProperties = new HashMap<>();
		Set<String> keys = properties.stringPropertyNames();
		List<String> keyList = new ArrayList<>(keys);
		for (String key : keyList) {
			if (key.startsWith(prefix)) {
				//default.password ==> password
				String realKey = key.substring(key.indexOf(prefix) + prefix.length() + 1);
				String value = properties.getProperty(key);
				configProperties.put(realKey, value);
			}
		}


		return configProperties;
	}

	/**
	 * 读取配置文件,返回一个流对象
	 */
	private Properties getClassNameFromResource(String fileName) {
		Properties properties = new Properties();
		ClassLoader classLoader = ConfigPostProcess.class.getClassLoader();
		InputStream inputStream = classLoader.getResourceAsStream(fileName);
		try {
			properties.load(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return properties;
	}

	/**
	 * 为指定字段使用反射技术设值(只支持String 与 int 类型)
	 */
	private void setFieldValueByFieldName(String fieldName, Object object, String value) {
		try {
			Class c = object.getClass();
			if (checkFieldExists(fieldName, c)) {
				Field f = c.getDeclaredField(fieldName);
				f.setAccessible(true);
				//如果不是String,那么就是int。其它类型不支持
				if (f.getType().equals(String.class)) {
					f.set(object, value);
				} else {

					int number = Integer.valueOf(value);
					f.set(object, number);
				}

			}
		} catch (Exception e) {
			LOGGER.error("设置" + fieldName + "出错");
		}
	}

	/**
	 * 检查这个Bean是否有配置文件中配置的字段
	 * 没有就不设置了
	 */
	private boolean checkFieldExists(String fieldName, Class c) {
		Field[] fields = c.getDeclaredFields();
		for (Field field : fields) {
			if (field.getName().equals(fieldName)) {
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}
}

测试

import lombok.Data;
import org.springframework.stereotype.Component;
 
@Config(prefix = "default")
@Component
@Data
public class Test {
 
    private String username;
 }

完工

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值