【SpringBoot】自定义注解实现yml格式配置文件注入

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吧

posted on 2022-08-23 17:10  仓鼠不爱吃辣条  阅读(796)  评论(0编辑  收藏  举报

页尾

页尾

页尾

页尾

页尾

页尾

页尾