springboot的定制化原理
个人理解,springboot设计目标就是希望利用提供各种默认约定配置,代替配置,从而达到简略配置的目的。
实现原理就是利用类似SPI机制,通过AutoConfigurationImportSelector,从而读取所有的classpath里的MATA_INF/META-INF/spring.factories里的自动配置类,配合对应Conditional注解从而有条件的注册对应的自动配置类,而@EnableConfigurationProperties绑定配置到对应的配置类,从而提供默认配置和个性化配置。达到修改配置后,对应的服务功能也修改。
所以我们个性化使用springboot,通常可以直接引入对应的starter,修改配置,自然就能达到开箱即用的效果。
定制化springboot
现在比较常见的个性化自己配置的有4个方式。
修改配置
从springboot的整体流程可以看出,springboot的默认配置基本都是通过绑定对应的配置类,从而注入对应的bean到容器中。
自然修改配置就能达到个性化自己的配置。
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
容器中注入自己的bean
从自动注入原理也能知道,自动配置是配合Conditional注解注解有条件的注入,因此如果我们破坏这个条件,自然也能打破自动注入,注入自己的bean。
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
比如这个,springboot的自动注入InternalResourceViewResolver类的 前提是容器中没有对应的类,
如果我们申明一个对应类的@Bean,自然容器中存在的就我们声明的bean。
通过实现XXXCustomizer达到修改配置的效果
官方通过类似定义XXXCustomizer格式的类,预留了一个修改点给用户,从而达到个性化配置的效果。
我们追踪一下ServletWebServerFactoryCustomizer。
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
平平无奇的注入bean。那么哪里使用了呢。
下面有个实现了ImportBeanDefinitionRegistrar的BeanPostProcessorsRegistrar
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
...
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory != null) {
this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
}
}
里面的registerBeanDefinitions,我们可以看到,注册了两个类,WebServerFactoryCustomizerBeanPostProcessor,ErrorPageRegistrarBeanPostProcessor。
从命名就可以看出,WebServerFactoryCustomizerBeanPostProcessor就是WebServerFactoryCustomizer的后置处理器,
点进去看看。
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
...
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
customizer.customize(webServerFactory);
});
}
可以看出,里面循环调用了customizer的customize,那么我们就可以在里面修改webServerFactory,
因为是BeanPostProcessor从而可以在注册bean到容器前修改配置。
通过XXXConfigurer修改配置
追踪WebMvcConfigurer
我们拿WebMvcConfigurer举例,说明一下为什么我们实现WebMvcConfigurer接口就能达到自定义配置的效果。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/** 开启跨域 */
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路由
registry
.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
.allowedHeaders("*")
// 是否允许证书(cookies)
.allowCredentials(true)
// 设置允许的方法
.allowedMethods("*")
// 跨域允许时间
.maxAge(30 * 24 * 3600L);
}
}
我们常通过这样类型的配置类配置.
怎么就能修改配置呢。
我们知道springboot是通过读取所有的classpath里的MATA_INF/META-INF/spring.factories里的自动配置,从而注入XXXAutoConfiguration的类,而
对应类里会有对应的注入bean。
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration
我们可以看出,WebMvcAutoConfiguration的注入条件是容器中没有WebMvcConfigurationSupport类的bean。
我们看下下面的实现
@Configuration
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware
我们可以看出,他导入了WebMvcAutoConfiguration.EnableWebMvcConfiguration,绑定了WebMvcProperties.class, ResourceProperties.class.
@Configurationpublic static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration
@Configurationpublic class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); public DelegatingWebMvcConfiguration() { } @Autowired( required = false ) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); } }
这样我们就很清楚了,我们WebMvcAutoConfigurationAdapter级联注册EnableWebMvcConfiguration,而EnableWebMvcConfiguration继承WebMvcConfigurationSupport,而里面setConfigurers注入了所有的实现了WebMvcConfigurer的实现类。
我们来追踪下怎么注入的HttpMessageConverter。
@Configurationpublic class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware
我们看到DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport,而WebMvcConfigurationSupport里面有几个方法却有@bean注解,说明一定有那个注解类注册了这个类,我们看下WebMvcConfigurationSupport实现类。
@Configuration@ComponentScan({"org.activiti.rest.exception", "org.activiti.rest.service.api"})@ConditionalOnClass(WebMvcConfigurationSupport.class)@EnableAsyncpublic class DispatcherServletConfiguration extends WebMvcConfigurationSupport
可以看到DispatcherServletConfiguration继承了这个WebMvcConfigurationSupport
protected final List<HttpMessageConverter<?>> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList(); this.configureMessageConverters(this.messageConverters); if (this.messageConverters.isEmpty()) { this.addDefaultHttpMessageConverters(this.messageConverters); } this.extendMessageConverters(this.messageConverters); } return this.messageConverters; }
可以看到,实现方法是getMessageConverters,里面调用了this.configureMessageConverters(this.messageConverters);调用子类添加HttpMessageConverter。this.extendMessageConverters(this.messageConverters);调用子类的extendMessageConverters的方法。
自然就对应我们WebMvcConfigurer的configureMessageConverters重写HttpMessageConverter,而extendMessageConverters继承。
看下是谁调用的
@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = this.createRequestMappingHandlerAdapter(); adapter.setContentNegotiationManager(this.mvcContentNegotiationManager()); adapter.setMessageConverters(this.getMessageConverters()); adapter.setWebBindingInitializer(this.getConfigurableWebBindingInitializer()); adapter.setCustomArgumentResolvers(this.getArgumentResolvers()); adapter.setCustomReturnValueHandlers(this.getReturnValueHandlers()); if (jackson2Present) { adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice())); adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice())); } AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); this.configureAsyncSupport(configurer); if (configurer.getTaskExecutor() != null) { adapter.setTaskExecutor(configurer.getTaskExecutor()); } if (configurer.getTimeout() != null) { adapter.setAsyncRequestTimeout(configurer.getTimeout()); } adapter.setCallableInterceptors(configurer.getCallableInterceptors()); adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); return adapter;}
这里注册了RequestMappingHandlerAdapter,而里面注入了HttpMessageConverter。 adapter.setMessageConverters(this.getMessageConverters());
那么自此,逻辑就清晰了,为什么我们继承WebMvcConfigurer就能实现添加HttpMessageConverter。
为什么我们
@Configurationpublic class WebMvcConfig extends WebMvcConfigurationSupport
继承WebMvcConfigurationSupport,会覆盖 @EnableAutoConfiguration 关于 WebMvcAutoConfiguration。
因为自动注入WebMvcAutoConfiguration的注入条件是容器中没有WebMvcConfigurationSupport类的bean。而我们如果继承WebMvcConfigurationSupport就得自己注入对应的所有类。
而@EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Import({DelegatingWebMvcConfiguration.class})public @interface EnableWebMvc {}
也级联配置了DelegatingWebMvcConfiguration,这个也继承WebMvcConfigurationSupport,所以@EnableWebMvc就跟我们容器内实现继承WebMvcConfigurationSupport一样的效果。