Principio de configuración automática SpringBoot-MVC

1 Lectura del sitio web oficial

  Antes de escribir el proyecto, también necesitamos saber una cosa, que es la configuración que SpringBoot ha hecho a nuestro SpringMVC, incluida la forma de extender y personalizar.

  Solo cuando estos se aclaren, seremos más útiles para usar en el futuro. Ruta 1: Análisis del código fuente, Ruta 2: ¡Documentación oficial!

Dirección: https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

Spring MVC Auto-configuration
// Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
// 自动配置在Spring默认设置的基础上添加了以下功能:
The auto-configuration adds the following features on top of Spring’s defaults:
// 包含视图解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
// 支持静态资源文件夹的路径,以及webjars
Support for serving static resources, including support for WebJars 
// 自动注册了Converter:
// 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
// Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
Automatic registration of Converter, GenericConverter, and Formatter beans.
// HttpMessageConverters
// SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
Support for HttpMessageConverters (covered later in this document).
// 定义错误代码生成规则的
Automatic registration of MessageCodesResolver (covered later in this document).
// 首页定制
Static index.html support.
// 图标定制
Custom Favicon support (covered later in this document).
// 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
/*
如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己
的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供
RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义
实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
*/
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
(interceptors, formatters, view controllers, and other features), you can add your own 
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

  Comparémoslo cuidadosamente y veamos cómo se implementa. Nos dice que SpringBoot configuró automáticamente SpringMVC para nosotros, y luego configuró automáticamente qué?

2 ContentNegotiatingViewResolver Content Negotiating View Resolver

  El ViewResolver se configura automáticamente, que es el sistema de resolución de vistas de SpringMVC que aprendimos anteriormente;

  Es decir, el objeto de vista (Vista) se obtiene de acuerdo con el valor de retorno del método, y luego el objeto de vista decide cómo representar (reenviar, redirigir).

  Echemos un vistazo al código fuente aquí: encontramos WebMvcAutoConfiguration, luego buscamos ContentNegotiatingViewResolver. ¡Encuentra el método a continuación!

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    
    
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
    // ContentNegotiatingViewResolver使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}

¡Podemos hacer clic en esta categoría para ver! Encuentre el código para la vista de análisis correspondiente;

@Nullable // 注解说明:@Nullable 即参数可为null
public View resolveViewName(String viewName, Locale locale) throws Exception {
    
    
    RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
    Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
    List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
    if (requestedMediaTypes != null) {
    
    
        // 获取候选的视图对象
        List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
        // 选择一个最适合的视图对象,然后把这个对象返回
        View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
        if (bestView != null) {
    
    
            return bestView;
        }
    }
    // .....
}

  Sigamos haciendo clic y veamos, ¿cómo obtuvo la vista del candidato?

  En getCandidateViews, vi que tomó todos los analizadores de vistas, realizó un ciclo while y los analizó uno por uno.

Iterator var5 = this.viewResolvers.iterator();

  Así que la conclusión es: ContentNegotiatingViewResolver Esta resolución de vista se usa para combinar todas las resoluciones de vista

  ¡Estudiemos su lógica de combinación nuevamente, veamos que hay un atributo viewResolvers y veamos dónde está asignado!

protected void initServletContext(ServletContext servletContext) {
    
    
    // 这里它是从beanFactory工具中获取容器中的所有视图解析器
    // ViewRescolver.class 把所有的视图解析器来组合的
    Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
    ViewResolver viewResolver;
    if (this.viewResolvers == null) {
    
    
        this.viewResolvers = new ArrayList(matchingBeans.size());
    }
    // ...............
}

  Dado que está buscando una resolución de vista en el contenedor, ¿podemos adivinar que podemos implementar una resolución de vista?

  Podemos agregar una resolución de vista al contenedor nosotros mismos; esta clase lo combinará automáticamente para nosotros; implementémoslo

1. Intentemos escribir un analizador de vista en nuestro programa principal;

@Bean //放到bean中
public ViewResolver myViewResolver(){
    
    
    return new MyViewResolver();
}
//我们写一个静态内部类,视图解析器就需要实现ViewResolver接口
private static class MyViewResolver implements ViewResolver{
    
    
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
    
    
        return null;
    }
}

2. ¿Cómo ver si el analizador de vistas que escribimos nosotros mismos funciona?

  Agreguemos un punto de interrupción al método doDispatch en DispatcherServlet para la depuración, porque todas las solicitudes irán a este método.

imagen

Encuentre la resolución de vista, vemos que nuestra propia definición está aquí;

imagen

  Entonces, si queremos usar nuestras propias cosas personalizadas, ¡solo necesitamos agregar este componente al contenedor! ¡SpringBoot hará el resto por nosotros!

3 Conversores y formateadores

Encuentra el convertidor de formato:

@Bean
@Override
public FormattingConversionService mvcConversionService() {
    
    
    // 拿到配置文件中的格式化规则
    WebConversionService conversionService = 
        new WebConversionService(this.mvcProperties.getDateFormat());
    addFormatters(conversionService);
    return conversionService;
}

Haga clic para ir:

public String getDateFormat() {
    
    
    return this.dateFormat;
}
/**
* Date format to use. For instance, `dd/MM/yyyy`. 默认的
 */
private String dateFormat;

Como puede ver en nuestro archivo de propiedades, ¡podemos configurarlo automáticamente!

  Si configura su propio método de formato, se registrará en el bean para que surta efecto.Podemos configurar las reglas de formato de fecha en el archivo de configuración:

imagen

El resto no daré ejemplos uno por uno, ¡puedes bajar y estudiar más!

4 Modificar la configuración por defecto de SpringBoot

  El principio de tantas configuraciones automáticas es el mismo, a través del análisis del principio de configuración automática de WebMVC, tenemos que aprender una forma de aprender, a través de la exploración del código fuente y sacar conclusiones, esta conclusión debe ser nuestra, y todo es Perfecto.

  La capa inferior de SpringBoot usa muchos de estos detalles de diseño, ¡así que necesita leer más el código fuente! obtener conclusión;

  Cuando SpringBoot configura automáticamente muchos componentes, primero verifica si hay alguna configuración configurada por el usuario en el contenedor (si el usuario configura el @bean por sí mismo ), si la hay, use la configuración configurada por el usuario, y si no, use la configuración automática;

  Si algunos componentes pueden existir más de uno, como nuestro sistema de resolución de vistas, combine el configurado por el usuario y el suyo propio.

Extendiendo usando SpringMVC

La documentación oficial es la siguiente:

Si desea conservar las funciones de Spring Boot MVC y desea agregar una configuración de MVC adicional (interceptores, formateadores, controladores de vista y otras funciones), puede agregar su propia clase @Configuration de tipo WebMvcConfigurer pero sin @EnableWebMvc . Si desea proporcionar instancias personalizadas de RequestMappingHandlerMapping, RequestMappingHandlerAdapter o ExceptionHandlerExceptionResolver, puede declarar una instancia de WebMvcRegistrationsAdapter para proporcionar dichos componentes.

  Todo lo que tenemos que hacer es escribir una clase de anotación @Configuration , y el tipo debe ser WebMvcConfigurer, y la anotación @EnableWebMvc no se puede marcar; escribiremos una nosotros mismos; crearemos un nuevo paquete llamado config, y escribiremos una clase MyMvcConfig ;

//应为类型要求为WebMvcConfigurer,所以我们实现其接口
//可以使用自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    
    
        // 浏览器发送/test , 就会跳转到test页面;
        registry.addViewController("/test").setViewName("test");
    }
}

Vamos al navegador y visitamos:

imagen

  ¡Realmente saltó por encima! Por lo tanto, queremos extender SpringMVC, y el oficial nos recomienda usarlo de esta manera, no solo para asegurarnos de que SpringBoot retiene toda la configuración automática, ¡sino también para usar nuestra configuración extendida!

Podemos analizar el principio:

1. WebMvcAutoConfiguration es la clase de configuración automática de SpringMVC, que tiene una clase WebMvcAutoConfigurationAdapter

2. Hay una anotación en esta clase, que se importará al realizar otra configuración automática: @Import ( EnableWebMvcConfiguration.class )

3. Echemos un vistazo a la clase EnableWebMvcConfiguration, que hereda una clase principal: DelegatingWebMvcConfiguration

Hay tal pieza de código en esta clase padre:

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
  // 从容器中获取所有的webmvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
    
    
        if (!CollectionUtils.isEmpty(configurers)) {
    
    
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}

4. Podemos buscar un viewController que acabamos de configurar en esta clase como referencia y encontrar que llama a un

protected void addViewControllers(ViewControllerRegistry registry) {
    
    
    this.configurers.addViewControllers(registry);
}

5. Entremos y echemos un vistazo

public void addViewControllers(ViewControllerRegistry registry) {
    
    
    Iterator var2 = this.delegates.iterator();
    while(var2.hasNext()) {
    
    
        // 将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的
        WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
        delegate.addViewControllers(registry);
    }
}

  Por lo tanto, se concluye que se utilizarán todas las WebMvcConfigurations, no solo las propias clases de configuración de Spring, sino que, por supuesto, se llamarán nuestras propias clases de configuración;

5 Tomar el control de SpringMVC en su totalidad

Documentación oficial:

If you want to take complete control of Spring MVC
you can add your own @Configuration annotated with @EnableWebMvc.

  Adquisición completa significa: la configuración automática de SpringMVC de SpringBoot ya no es necesaria, ¡todo lo configuramos nosotros mismos!

Simplemente agregue un @EnableWebMvc a nuestra clase de configuración .

  Veamos que si nos hacemos cargo de SpringMVC, el mapeo de recursos estáticos que SpringBoot configuró para nosotros antes no será válido, podemos probarlo;

Antes de comentar, visita la página de inicio:

imagen

Anote la clase de configuración: @EnableWebMvc

imagen

¡Descubrimos que toda la configuración automática de SpringMVC está rota! volvió a su estado original;

Por supuesto, en nuestro desarrollo, no se recomienda utilizar una adquisición completa de SpringMVC.

¿Pensando en un problema? ¿Por qué agregar una anotación? ¡La configuración automática no es válida! Veamos el código fuente:

1. Aquí se encuentra que importó una clase, podemos seguir buscando en

@Import({
    
    DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
    
    
}

2. Hereda una clase padre WebMvcConfigurationSupport

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    
  // ......
}

3. Repasemos la clase de configuración automática de Webmvc

@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 {
    
    
}

Para resumir una oración: @EnableWebMvc importó el componente WebMvcConfigurationSupport;

¡El WebMvcConfigurationSupport importado es solo la función más básica de SpringMVC!

  Habrá muchas configuraciones de extensiones en SpringBoot. Mientras veamos esto, deberíamos prestar más atención~

Supongo que te gusta

Origin blog.csdn.net/qq_41355222/article/details/123965808
Recomendado
Clasificación