【每天学点Spring】Import相关,如何引入配置文件properties、yml、yaml以及外部类

Spring用的比较多的配置文件格式有很多种,文章简单介绍下相关文件的引入:

  • 使用@ImportResource引入xml文件
  • 使用@PropertySource引入properties配置、yml或yaml配置。与@Value结合使用。
    • 使用@ConfigurationProperties替换@Value
    • 使用@EnableConfigurationProperties替换@Component
  • 使用@Import引入超出@ComponentScan范围的外部类

文章内容:

文章内容


1. 使用@ImportResource引入xml文件

这个在新的版本用的很少,只有做一些老项目的迁移啊之类的时候才会用到。新的项目一般都会用注解去做了,也就是无xml配置了。

注解@ImportResource很早就有了,位于org.springframework.context.annotation包中,即Spring核心包spring-context中,引用的版本为3.0

示例:

1.1首先是定义一个Bean:
public class XmlBean {
    public String name() {
        return "name = xmlBean";
    }
}
1.2 再在resources下定义一个helloBean.xml文件:

文件定义了xmlBean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="xmlBean" class="com.faj.configuration.xmlImport.XmlBean" />
</beans>
1.3 如何引用:

【重点】使用@Component声明需要被Spring管理,再使用@ImportResource进行xml文件的引入:

@Configuration
@ImportResource(locations = {"classpath:helloBean.xml"})
public class XmlConfiguration {
}
1.4 测试:
@SpringBootTest
public class XmlConfigurationTest {
    @Autowired
    private XmlBean xmlBean;

    @Test
    public void test() {
        Assertions.assertNotNull(xmlBean);
        Assertions.assertEquals("name = xmlBean", xmlBean.name());
    }
}

2. 使用@PropertySource引入properties配置,yml或yaml配置

@ImportResource一样,@PropertySource也位于spring-context核心包中,package也相同:org.springframework.context.annotation,这个注解从3.1版本开始就有了。

第一部分,引入properties配置:

示例:

2.1.1 首先创建一个properties文件:hello.properties
hello.server=hServer
hello.ip=127.0.0.1
hello.profile=test
2.1.2 创建配置类,并使用@PropertySource注解引入hello.properties

在Spring Boot下:src/main/java或者src/main/resources下面都是classpath

@Data
@Component
@PropertySource("classpath:hello.properties")
public class PropertiesConfiguration {
    @Value(value = "${hello.server}")
    private String server;

    @Value(value = "${hello.ip}")
    public String ip;

    @Value(value = "${hello.profile}")
    public String profile;
}
2.1.3 测试
@SpringBootTest
public class PropertiesConfigurationTest {
    @Autowired
    private PropertiesConfiguration propertiesConfiguration;

    @Test
    public void te() {
        Assertions.assertEquals("hServer", propertiesConfiguration.getServer());
        Assertions.assertEquals("127.0.0.1", propertiesConfiguration.getIp());
        Assertions.assertEquals("test", propertiesConfiguration.getProfile());
    }
}
第二部分,导入yml或yaml:

参考:@PropertySource 解析 yml 配置文件,自定义解析 yaml 工厂类

@PropertySource可以传属性factory用来解析文件,默认的DefaultPropertySourceFactory只提供了properties后缀文件的解析,对于yml或是yaml的解析,需要自定义factory。

示例:

2.2.1 首先定义yaml解析工厂:
public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource (@Nullable String name, EncodedResource resource) throws IOException {

        // 返回 yaml 属性资源
        return new YamlPropertySourceLoader()
                .load (resource.getResource ().getFilename (), resource.getResource ())
                .get (0);
    }
}
2.2.2再定义yaml文件:hello.yaml
hello:
  version: v1.0
2.2.3 定义配置类,需要显性传入factory=YamlPropertySourceFactory.class
@Data
@Component
@PropertySource(value = "classpath:hello.yaml", factory = YamlPropertySourceFactory.class)
public class YamlConfiguration {

    @Value(value = "${hello.version}")
    private String version;
}
2.2.4 测试:
@SpringBootTest
public class YamlConfigurationTest {
    @Autowired
    private YamlConfiguration yamlConfiguration;

    @Test
    public void test() {
        System.out.printf(yamlConfiguration.getVersion());
    }
}

3. 升级版引入properties配置(使用@ConfigurationProperties)

在示例#2中使用@Value(value = "${hello.server}")来引入properties里的值,Spring也提供了另外的方式来使用properties里的值。
也就是使用@ConfigurationProperties(prefix = "hello"),加上前缀后,在正式的配置类里,就能直接引用去掉前缀后的key了。总体上来说,@ConfigurationProperties的作用是可以很方便的将配置文件转换成类对象。

@ConfigurationProperties是Spring boot v1.0引入的。

示例:

@Data
@Component
@PropertySource("classpath:hello.properties")
@ConfigurationProperties(prefix = "hello")
public class PropertiesConfig {

    public String server;
    public String ip;
    public String profile;
}
测试:

propertiesConfig.getProfile()拿到的值和#2.1中的一样。

关于spring-boot-configuration-processor

参考:Stackoverflow - What is the spring-boot-configuration-processor ? Why do people exclude libraries from it? Why is it invisible in dependency tree?

在引入@ConfigurationProperties后,IntelliJ会提示一个错误
IntelliJ提示

这是因为需要导入spring-boot-configuration-processor依赖,这个依赖主要是让IDE有提示,optional=true表示只是IDE需要,并不是Code本身需要。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

4. @EnableConfigurationProperties,与@ConfigurationProperties搭配使用

参考:
baeldung - Guide to @EnableConfigurationProperties
Stackoverflow - What difference does @EnableConfigurationProperties make if a bean is already annotated with @ConfigurationProperties?

通常情况下@EnableConfigurationProperties都会与 @ConfigurationProperties搭配使用,两个注解都是spring-boot v1.0引入的。

@ConfigurationProperties在#3中已经有演示,它的作用是方便的将properties或是yaml配置-->转成Java类。

在Spring的世界中,想要include它有两种方式:

  • 第1种就是示例中的,在Java配置类上加@Conponent或是@Configuration都可。
  • 第2种方式就是本章要介绍的,使用@EnableConfigurationProperties来告诉Spring需要引入具体的配置类。

示例

依旧是#3中的示例,只是这时候没有@Component修饰了。
@Data
@PropertySource("classpath:hello.properties")
@ConfigurationProperties(prefix = "hello")
public class PropertiesConfig {

    public String server;
    public String ip;
    public String profile;
}
需要定义另外一个类来声明上述配置类:

这里的loading类,需要加上@Component或是@Configuration来让Spring扫描到。

@Component
@EnableConfigurationProperties(PropertiesConfig.class)
public class LoadHelloPropertiesConfig {
}
测试

同样我们可以通过propertiesConfig.getServer()拿到配置。

【重点】这样的搭配在Spring Boot的AutoConfiguration模式中应用很广泛。
RabbitProperties.javaRabbitAutoConfiguration.java中就使用了这两个注解:

@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {
    ...
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
   ...
}

5. 使用@Import引入超出@ComponentScan范围的外部类

参考:baeldung - Spring @Import Annotation

我们都知道Spring Boot中有个核心配置@SpringBootApplication,这个配置有个注解就是@ComponentScan,这就是为什么我们在项目中只定义了一个@SpringBootApplication,在启动Spring Boot后,项目中的@Controller@Service等注解会被自动扫描的原因:
SpringBootApplication源码:

...
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   ...
}

而关于@ComponentScan的扫描范围,我们可以传入valuebasePackages来定义需要扫描的包名,那么如果没有定义包名,默认的扫描范围是什么呢?
@Component文档:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html

If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
如果没有定义包名,那么会自动扫描配置注解所在的类的包名。

【重点】所以如果我们的Spring boot的启动类Application在com.test下,那么String只会自动扫描com.test下的所有类(包括sub package)。如果这时候有个类在com.another下,就不会被include进来了。可以使用@Import来手动引入该类。

示例

5.1 在com.another类中创建一个Service:
package com.another;
import org.springframework.stereotype.Service;

public class AnotherService {
    public String name() {
        return "anotherService";
    }
}

如果我们给AnotherService加上@Service注解,然后在别的Service中引用,那么会报错NoSuchBeanDefinitionExceptionNo qualifying bean of type 'com.another.AnotherService' available: expected at least 1 bean which qualifies as autowire candidate.
原因就是com.another不在@ComponentScan扫描范围内。

5.2 使用@Import

AnotherServiceTest位于com.test子包中。import代码:略

package com.test.service;

@SpringBootTest
@Import(AnotherService.class)
public class AnotherServiceTest {
    @Autowired
    private AnotherService anotherService;

    @Test
    public void test() {
        Assertions.assertEquals("anotherService", anotherService.name());
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容