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.java
和RabbitAutoConfiguration.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
的扫描范围,我们可以传入value
或basePackages
来定义需要扫描的包名,那么如果没有定义包名,默认的扫描范围是什么呢?
@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中引用,那么会报错NoSuchBeanDefinitionException
:No 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());
}
}
网友评论