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一样的效果。