1.创建一个starter项目(非必须,主要更好分离代码)
2.创建注解文件@YamlSource
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface YamlSource {
String name() default "";
String value() default "";
String prefix() default "";
boolean ignoreResourceNotFound() default false;
String encoding() default "";
}
其中value属性指定配置文件的全称(默认在classpath根目录,其他情况可以自测),prefix属性指定yaml文件中属性的前缀,比如前缀为"org",yaml文件内容为以下的情况:
org:
# 下面的内容才会被当作主要内容,org是前缀
driverClassName: com.mysql.cj.Driver
username: root
url: jdbc:mysql://localhost:3306/mytest?characterEncoding=utf-8&timeZone=UTC
password: erii
3.创建AutoConfiguration文件
@Configuration
public class MyConfigAutoConfiguration {
// 定义扫描的包,不填默认是classpath内所有class
private String BASE_PACKAGE = "";
// 扫描所有class
private String RESOURCE_PATTERN = "/**/*.class";
// 构造方法优先级高
public MyConfigAutoConfiguration(ApplicationContext applicationContext) throws IOException, IllegalAccessException {
// key:Class类型,value:{"filename": "配置文件路径", "prefix": "是否带有前缀"}
Map<Class<?>, Map<String, String>> map = new HashMap<>();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
// 拼接路径
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + RESOURCE_PATTERN;
// 获取classpath所有class读取为Resource
Resource[] resources = resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource: resources) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
// 判断注解元信息中是否包含YamlSource类型名字的注解
if (reader.getAnnotationMetadata().hasAnnotation(YamlSource.class.getName())) {
// 存在的话获取这个Class类型
Class<?> clazz = Class.forName(reader.getAnnotationMetadata().getClassName());
// 获取类的元注解信息
YamlSource ys = clazz.getAnnotation(YamlSource.class);
// 存入map
map.put(clazz, Map.of("filename", ys.value(), "prefix", ys.prefix()));
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// 遍历所有带了@YamlSource的类
for (Entry<Class<?>, Map<String, String>> e1: map.entrySet()) {
Class<?> ccc = e1.getKey();
// 获取配置文件路径
String configFile = e1.getValue().get("filename");
// 如果未指定路径,默认是application.yml,这样直接写在主配置文件中也ok的,黑科技!!
if ("".equals(configFile)) {
configFile = "application.yml";
}
// 获取前缀
String prefix = e1.getValue().get("prefix");
// 通过ApplicationContext获得Class类型的Bean
Object o = applicationContext.getBean(ccc);
// 使用Yaml读取配置文件为Map类型
Yaml yaml = new Yaml();
Map<?, ?> n = yaml.loadAs(new ClassPathResource(configFile).getInputStream(), Map.class);
// 判断map是否有指定的前缀,没有报错
if (!"".equals(prefix)) {
if (!n.containsKey(prefix)) {
throw new InvalidPropertiesFormatException("Prefix on @YamlSource in " + ccc.getName() + " is not exist in " + configFile);
}
n = (Map<?, ?>) n.get(prefix);
}
// 遍历所有声明的字段,并注入值
for(Field f: ccc.getDeclaredFields()) {
f.setAccessible(true);
f.set(o, n.get(f.getName()));
}
}
}
}
4.如果是starter模块,不在同一个项目,需要创建spring.factories文件,填入我们的自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.cxj.MyConfigAutoConfiguration
5.创建一个配置类,并加上注解(备注,如果字段不为public,需要加上getter,setter方法,否则Yaml解析注入属性会出错)
package cxj.model;
import com.cxj.annotation.YamlSource;
@YamlSource(value = "jdbc.yml", prefix = "")
public class JDBCConfig {
public String driverClassName;
public String username;
public String password;
public String url;
}
6.创建配置文件,需要与配置类指定的value同名
driverClassName: com.mysql.cj.Driver
username: root
url: jdbc:mysql://localhost:3306/mytest?characterEncoding=utf-8&timeZone=UTC
password: *******
7.在需要使用的地方自动注入
@RestController
public class HelloController {
@Autowired
JDBCConfig jdbcConfig;
@GetMapping("/")
public JDBCConfig index( ) {
return jdbcConfig;
}
}
8.访问
9.结语
没有过多测试,使用过程中可能有些bug吧