Spring Boot 20天入门(day2)

@PropertySource&@ImportResource

@PropertySource :加载指定的配置文件。

@PropertySource("classpath:person.properties")
@Component
@ConfigurationProperties(prefix = "person")
public class Person {

@ImportResource :导入Spring的配置文件,让配置文件中的内容生效

Springboot里面没有Spring的配置文件,我们自己编写的配置文件,无法自动识别;

想让Spring的配置文件生效,需要将@ImportResource注解标注在Springboot的配置类上,并能让Springboot扫描到。

// 导入类路径下的配置文件,并让其生效
@ImportResource(locations = "classpath:beans.xml")
Springboot 添加组件的方式

传统方式:xml

太过笨重,而且需要在配置类上加上@ImportResource 注解,将配置文件引入

<?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="helloService" class="com.github.anhtom2000.service.HelloService">

</bean>
</beans>

Springboot推荐的方式:注解

创建一个配置类,标注上@SpringBootConfiguration(声明这是一个Springboot的配置类),给需要注入的组件标注上@Bean注解,Springboot扫描到会自动添加到容器中,组件名默认是方法签名,可以通过**@Bean(value=“xx”)**修改

@SpringBootConfiguration
public class ApplicationConfig {
    
    @Bean
    public HelloService helloService(){
        return new HelloService();
    }
}

Profile多环境支持

1、多Profile文件

我们在编写配置文件的时候,文件名可以是 application-{profile}.properties/yml

默认使用application.properties的配置;

2、yml支持多文档块方式

server:
  port: 8883
spring:
  profiles:
    active: dev
---
server:
  port: 8776
spring:
  profiles: dev
---
server:
  port: 8999
spring:
  profiles: prod

3、激活指定profile

1、在配置文件中指定 spring.profile.active={profile}

2、在命令行中指定
在这里插入图片描述

3、打包后在命令行执行的时候指定

java -jar xxx --spring.profiles.active=dev

4、虚拟机参数

-Dspring.profiles.active=dev

配置文件的加载位置

Springboot启动会扫描以下位置的application.properties或者application.yml文件作为Springboot的默认配置文件

PS : 优先级由高到低

  • -file:./config/ : 当前项目根目录下的config文件夹
  • file:./ : 当前项目的根目录下
  • classpath /config/ : 当前项目类路径(resources)下的config文件夹
  • classpath/ : 当前项目类路径下

Springboot外部配置

Springboot也可以在以下位置加载配置;优先级从高到底;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置

1、命令行参数

比如 : java -jar xxx.jar --server.port=9999

2、来自java:comp/env的NDI属性

3、java系统属性(System.properties)

4、操作系统环境变量

5、RandomValueProperySource配置的random.*属性值

Springboot自动配置

自动配置原理

Springboot启动的时候加载配置,扫描到@EnableAutoConfiguration注解,开启自动配置功能

@EnableAutoConfiguration

启动Springboot自动配置,并利用AutoConfigurationImportSelector给容器中导入组件

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

点开AutoConfigurationImportSelector 有以下方法:

selectImports:给容器导入组件

/*
	此方法用于给容器导入组件
*/
@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
        // 获取有用的自动配置类
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

getAutoConfigurationEntry:获取自动配置类

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    	// 获取所有候选的配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    	// 检查所有候选配置类是否有效
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

getCandidateConfigurations :获取所有的候选自动配置类

/*
	 通过 getSpringFactoriesLoaderFactoryClass 获取默认的 EnableAutoConfiguration.class 对象,传入 loadFactoryNames 方法
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
// 返回EnableAutoConfiguration的class对象
 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
}

代码注释都很清楚:

1)、首先是selectImports方法,从方法名可以看出,这个方法是用于给容器导入数组件的,然后通过getAutoConfigurationEntry方法,获取到自动配置类。

2)、getAutoConfigurationEntry方法又是从getCandidateConfigurations方法获取到所有候选的自动配置类,然后自己进行筛选,筛选出有效的自动配置类。

3)、进入到getCandidateConfigurations方法,通过SpringFactoriesLoaderloadFactoryNames方法,获取一个自动配置类的集合,这个集合中包含了所有自动配置类的全类名

4)、进入到SpringFactoriesLoader类中的loadFactoryNames中,该方法通过一个classLoader类加载器,加载一个由常量表示的路径(FACTORIES_RESOURCE_LOCATION),点击这个常量,常量地址是项目类路径下的META-INF/spring.factories

5)、然后把通过classLoader类加载器加载到的文件内容包装成一个Properties对象,由Properties获取到 EnableAutoConfiguration.class 类(类名)对应的值,然后将其添加到容器中

/*
	factoryType 就是获取到的EnableAutoConfiguration.class
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
             // 扫描所有 jar 包类路径下  META-INF/spring.factories
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
                // 把扫描到的这些文件的内容包装成 properties 对象
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                // Properties中的值都是以key:value的形式存在的。
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                          // 从 properties 中获取到 EnableAutoConfiguration.class 类(类名)对应的值,然后把他们添加在容器中
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

META-INF/spring.factories的部分截图 ,上面说到的key就是org.springframework.boot.autoconfigure.EnableAutoConfigurationvalue就是绿色部分

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\

每一个这样的 xxxAutoConfiguration 类都是容器中的一个组件,都加入到容器中,用他们来做自动配置。上述的每一个自动配置类都有自动配置功能,也可在配置文件中自定义配置。

XXXAutoConfiguration 与 xxxProperties

在上面的跟进中,我们只看到Springboot成功加载的配置类,却还没有看到自动配置值,随便选择一个AutoConfiguration查看源码:

这里选择了ServletWebServerFactoryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 判断当前项目中是否存在这个类
@ConditionalOnClass(ServletRequest.class)
// 判断当前项目是否是Web应用
@ConditionalOnWebApplication(type = Type.SERVLET)
// 如果上面的条件都满足,配置类生效
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    
 //将这个类与配置类建立关系   
 //只有一个有参构造器的情况下,参数的值就会从容器中拿
    @Bean
	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
		return new ServletWebServerFactoryCustomizer(serverProperties);
	}
}

public class ServletWebServerFactoryCustomizer
		implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

	private final ServerProperties serverProperties;

	public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
		this.serverProperties = serverProperties;
	}
}

所有的在配置文件中能配置的属性都是在封装在 xxxProperties这样的类中的。这些属性一般有自己默认的值,我们需要修改什么就在配置文件中修改。

点进上面的ServerProperties.class

// 从配置文件中获取前缀为server的配置信息注入到当前类中。
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

	/**
	 * Server HTTP port.
	 */
	private Integer port;

	/**
	 * Network address to which the server should bind.
	 */
	private InetAddress address;
}

在配置文件中对应的配置:

server.port=8888
server.address=127.0.0.1

自动配置原理总结

​ 1)、Springboot启动的时候加载主配置类,扫描到了@EnableAutoConfiguration注解,开启自动配置功能;

​ 2)、@EnableAutoConfiguration给容器导入META-INF/spring.factories里面定义好的自动配置类,若没有定义我们想要的配置类,就需要我们自己写配置类,并让Springboot扫描到。

​ 3)、筛选有效的配置类(依赖存在)

​ 4)、给容器中自动配置类添加组件的时候,会从 properties 类中获取某些属性。我们就可以在配置文件中指定这些属性的值

​ 5)、XXXAutoConfiguration就是向容器提供自动配置类

​ 6)、xxxProperties封装配置文件中对应的属性

以上…

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值