SpringBoot-属性注入改变默认配置-源码探究

上一节,通过分析源码知道了SpringBoot的自动装配原理,SpringBoot给我们做好了很多默认配置,但是我们难免要去更改一些配置,如数据库的连接信息、端口号信息等。那么如何去更改,SpringBoot底层是如何对我们的更改进行识别的呢?

这一篇博文以“更改数据库连接信息”为例演示更改方式,再以“静态资源路径”分析一下SpringBoot底层是如何识别配置的

更改默认配置

回顾Spring配置方式

先回顾一下在Spring中,我们是怎样去注入数据的。

xml方式

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
   <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_study_db"></property>
   <property name="username" value="root"></property>
   <property name="password" value="admin"></property>
</bean>

Java类配置方式

jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_study_db
jdbc.username=root
jdbc.password=admin

配置类

@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dateSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(userName);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
}

Debug启动,结果如下:

@RestController
public class StartController {
    @Autowired
    private DataSource dataSource;
    @RequestMapping("/start")
    public String start(){
        return "helloword springboot!!";
    }
}

在这里插入图片描述
相比之下,可以发现配置类的配置方式可读性强,容易理解。只需要我们明白相应的注解含义并且会使用即可。
但是这样还是显得不是那么的优雅,SpringBoot既然是Spring的再封装,那么他提供了两种配置方式,一种比一种优雅。

SpringBoot注入方式一

使用application.properties配置文件,信息和上面的jdbc.properties一样,只是换了名字而已。因为SpringBoot回去读取以application开头的properties文件和yaml文件。

认识yml

application.yml,是SpringBoot特有的一种配置方式,配置简单灵活

jdbc: 
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://127.0.0.1:3306/spring_study_db
  username: root
  password: admin

在这里插入图片描述
编写JdbcProperties 类,使用@ConfigurationProperties(prefix = "jdbc")注解并加上前缀作为区分。

@ConfigurationProperties(prefix = "jdbc")
@Data
public class JdbcProperties {
    private String driverClassName;
    private String url;
    private String userName;
    private String password;
}

JdbcConfig 类,使用@EnableConfigurationProperties(JdbcProperties.class)注解,引入JdbcProperties 类的信息,这样就可以JdbcConfig类中的任何位置直接注入(构造函数注入,或者@Autowrited,或者@Bean)了

@Configuration
@EnableConfigurationProperties(JdbcProperties.class)
public class JdbcConfig {
	// @Autowired
    // private JdbcProperties jdbcProperties;
    
    // public JdbcConfig(JdbcProperties jdbcProperties){
    //    this.jdbcProperties=jdbcProperties;
    //}
    
    @Bean
    public DataSource dateSource(JdbcProperties jdbcProperties){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(jdbcProperties.getDriverClassName());
        druidDataSource.setUrl(jdbcProperties.getUrl());
        druidDataSource.setUsername(jdbcProperties.getUserName());
        druidDataSource.setPassword(jdbcProperties.getPassword());
        return druidDataSource;
    }
}

Debug启动,测试结果如下:
在这里插入图片描述
这种方式,已经挺优雅了,而且可以重用,在任何位置都可以进行注入,但是必定是多写了一个类,还是有点麻烦。

SpringBoot注入方式二

不需要JdbcProperties 类。直接使用JdbcConfig类,@ConfigurationProperties(prefix = "jdbc")作用在方法上即可。

@Configuration
public class JdbcConfig {
    @Bean
    @ConfigurationProperties(prefix = "jdbc")
    public DataSource dateSource(){
        return new DruidDataSource();
    }
}

当SpringBoot扫描到这个bean时,发现了@ConfigurationProperties(prefix = "jdbc")这个注解,就会根据application.properties文件中前缀为jdbc开头的配置,去要返回的类中找有没有对应的set方法的属性,如果有就会自动注入。
结果如下:在这里插入图片描述
总结:
SpringBoot方式一注入,适用于很多地方要使用这个配置,哪里用,哪里就注入。
方式二,则就是这个地方的DataSource使用,不需要把配置记录下来(JdbcProperties类就起到了记录作用)。

看看源码

知道了如何去改变默认配置,那么现在去看看源码,以我们熟悉的WebMvcAutoConfiguration类为例,看看他的内部是怎样给我们配置的,同时也就知道如何去改变这些默认的配置了。
WebMvcAutoConfiguration 类中有一个WebMvcAutoConfigurationAdapter 内部类,这个类标注了@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }),根据字面意思他是启动MVC自动配置,这个内部类中有ResourceProperties、WebMvcProperties两个对象,而且在构造方法中也传入了这两个对象,类似与我们上面的自定义的JdbcProperties类。哈哈!是不是似乎有点明白了。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
	/**
	 * Configuration equivalent to {@code @EnableWebMvc}.
	 */
	// Defined as a nested config to ensure WebMvcConfigurer is not read when not
	// on the classpath
	@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

		private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

		private final ResourceProperties resourceProperties;

		private final WebMvcProperties mvcProperties;
		
		public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
				ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
				ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
			this.resourceProperties = resourceProperties;
			this.mvcProperties = mvcProperties;
			this.beanFactory = beanFactory;
			this.messageConvertersProvider = messageConvertersProvider;
			this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
		}
		
		@Override
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
			CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
			if (!registry.hasMappingForPattern("/webjars/**")) {
				customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
						.addResourceLocations("classpath:/META-INF/resources/webjars/")
						.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
			}
			String staticPathPattern = this.mvcProperties.getStaticPathPattern();
			if (!registry.hasMappingForPattern(staticPathPattern)) {
				customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
						.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
						.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
			}

		……
	}
}

WebMvcProperties

这里只针对性的摘取部分源码进行分析。我们发现WebMvcProperties 类确实是定义了很多属性,这些属性的前缀全部被定义为@ConfigurationProperties(prefix = "spring.mvc"),而且都提供了set方法。和我们上面SpringBoot注入方式一是一模一样的。

@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
	private DefaultMessageCodesResolver.Format messageCodesResolverFormat;
	private Locale locale;
	private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;
	private String dateFormat;
	private boolean dispatchTraceRequest = false;
	private boolean dispatchOptionsRequest = true;
	private boolean ignoreDefaultModelOnRedirect = true;
	private boolean publishRequestHandledEvents = true;
	private boolean throwExceptionIfNoHandlerFound = false;
	private boolean logResolvedException = false;
	private String staticPathPattern = "/**";

	private final Async async = new Async();
	private final Servlet servlet = new Servlet();
	private final View view = new View();
	private final Contentnegotiation contentnegotiation = new Contentnegotiation();
	private final Pathmatch pathmatch = new Pathmatch();

	public static class View {
		private String prefix;
		private String suffix;

		public String getPrefix() {
			return this.prefix;
		}

		public void setPrefix(String prefix) {
			this.prefix = prefix;
		}

		public String getSuffix() {
			return this.suffix;
		}

		public void setSuffix(String suffix) {
			this.suffix = suffix;
		}
	}
}

这样在application.yaml中,就会有很多的提示,而这些提示就是我们这里定义的属性,我们要想去改变他,只需要在这里去修改就好了。例如我们修改端口号为8081。
在这里插入图片描述
在这里插入图片描述
再啰嗦一下,其实这三者存在这样的关系:

  • XXXXXAutoConfiguration帮我们自动装配,然后从XXXXProperties中取得默认配置,而XXXXProperties则和配置文件(application.properties,application.yml)可以绑定,我们就能修改配置文件使用自定义配置了。**

ResourceProperties

静态资源的导入,其实也是靠这种方式的。
直接上图分析吧 。好像更清晰啊。
在这里插入图片描述

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {

	private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
			"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
	/**
	 * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
	 * /resources/, /static/, /public/].
	 */
	private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
}

所以这就是我们的静态资源一般只写在"classpath:/resources/", “classpath:/static/”, “classpath:/public/” 路径下的原因。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值