美文网首页
【每天学点Spring】Import相关,如何引入配置文件pro

【每天学点Spring】Import相关,如何引入配置文件pro

作者: 伊丽莎白2015 | 来源:发表于2022-05-14 16:51 被阅读0次

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());
    }
}

相关文章

网友评论

      本文标题:【每天学点Spring】Import相关,如何引入配置文件pro

      本文链接:https://www.haomeiwen.com/subject/sycpurtx.html