Springboot 个性化配置SpringMVC

Springboot很重要的一个功能就是整合了各种spring相关框架以及其他在开发场景中通用的包和需要配置的类。达到的效果就是以前需要写非常多的配置类以及XML文件,现在只需要在porm中配置:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.7.RELEASE</version>
</parent>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>

其中spring-boot-starter-xxx就是spring官方 给出的各种应用场景的打包,当然我们可以自己打包一些场景。但是什么都打包好了,如果我要自己配置,那怎么办呢?

1 SpringMVC自动配置

要想自己配置一些个性化的设置,那么要了解springboot是如何进行自动化配置以及配置了哪些东西的。

加载入口

springboot的启动路口相对于tomocat来说要明显一点,至少这里有了一个主类,最关键的就是这个@SpringBootApplication注解。

@SpringBootApplication
public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class,args);}}

@SpringBootApplication注解里面主要有三个注解:

  • @SpringBootConfiguration 表明这是一个Configuration

  • @ComponentScan 用于标注扫哪些包

  • @EnableAutoConfiguration 开启自动配置

    其中完成的两个动作

    • @AutoConfigurationPackage 里面是 @Import({Registrar.class})
    • @Import({AutoConfigurationImportSelector.class}) 关键的就是把AutoConfigurationImportSelector.class注册到容器当中

在整个SpringBoot当中会见到非常多的xxxAutoConfiguration类,这里面储备的就是各种相关的配置。而对于SpringMVC来说,主要的配置就在WebMvcAutoConfiguration中。

看一下WebMvcAutoConfiguration的注解

@Configuration(proxyBeanMethods = false
) // 不会创建代理对象
@ConditionalOnWebApplication(type = Type.SERVLET
) // web应用才初始化
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class}) //这些类加载了才初始化
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) //不存在这个类才初始化
@AutoConfigureOrder(-2147483638) //加载顺序
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}) //这些类加载后再初始化
public class WebMvcAutoConfiguration {}

而这个WebMvcAutoConfiguration中还有许多内部类,这些内部类也被@Configuration注解修饰,也同样会被加载到容器中,因此就会被加载。

2 个性化配置某个类

在WebMvcAutoConfiguration的内部类WebMvcAutoConfigurationAdapter中有这样一段代码

@Configuration(proxyBeanMethods = false)@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})@Order(0)public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {public void configurePathMatch(PathMatchConfigurer configurer) {configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());configurer.setUseRegisteredSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());// 配置路径解析this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {String servletUrlMapping = dispatcherPath.getServletUrlMapping();if (servletUrlMapping.equals("/") && this.singleDispatcherServlet()) {// 配置urlPathHelperUrlPathHelper urlPathHelper = new UrlPathHelper(); // 打断点urlPathHelper.setAlwaysUseFullPath(true);configurer.setUrlPathHelper(urlPathHelper);}});}}

如果我们需要自定义这个UrlPathHelper,那么我们有以下三种方式:

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则

新建一个类继承**WebMvcConfigurer**并重写需要的方法,然后使用@Configuration修饰

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

声明 WebMvcRegistrations 改变默认底层组件

使用自定义的**WebMvcRegistrations类来替换springboot帮我们定义的WebMvcRegistrations**

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

这里采用第一种方式。因此我们写了以下代码

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {//重写代码UrlPathHelper urlPathHelper = new UrlPathHelper();urlPathHelper.setAlwaysUseFullPath(false); // 打断点configurer.setUrlPathHelper(urlPathHelper);}
}

以及

/**
*这里我没有什么好方式,就直接写了controller,通过访问本机ip:端口/test来调用这个方法,打印注册的WebMvcConfigurer.class
*这里实现了ApplicationContextAware接口用于拿到上下文对象
*/
@RestController
public class HelloController implements ApplicationContextAware {@AutowiredApplicationContext ac;@RequestMapping("/test")public void test01(){String[] beanNamesForType = ac.getBeanNamesForType(WebMvcConfigurer.class);for (String s : beanNamesForType) {System.out.println(s);}}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {ac = applicationContext;}}

上文两个地方打了断点,分别是系统实现的WebMvcAutoConfigurationAdapter以及我们自己实现的MvcConfig,我们看看系统注册了哪些继承了WebMvcConfigurer的类

第一次断点:

可以看到先注册了系统配置的类,是从WebMvcConfigurationSupport调用的。这个设置其实是在一个普通方法内,而上层的逻辑是会调用这个方法的,所以才走到了这里。

我们放行,进入到第二个方法:

这里可以看到都出现了这个WebMvcConfigurerComposite这个configurePathMatch方法。

这个方法其实是

// WebMvcConfigurerComposite
public void configurePathMatch(PathMatchConfigurer configurer) {Iterator var2 = this.delegates.iterator();while(var2.hasNext()) {WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();delegate.configurePathMatch(configurer);}}

所以就是一前一后的进行调用而已。

Springboot如何保证我们自定义的WebMvcConfigurer实现类在系统自定义的之后调用呢?

因为这个delegate是有序的。了解Spring初始化的朋友会知道,在初始化类的时候因为依赖输入,每个类初始化的时间是不一定的。所以最后注入到List中的所有WebMvcConfigurer的实现类顺序也是不定的。但是我们这里可以按照顺序来注册,大家会想到WebMvcAutoConfigurationAdapter有一个注解@Order(0)意思就是需要排序,排在最前面,代码如下:

// DefaultListableBeanFactory
@Nullable
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {if (result instanceof List) {if (((List<?>) result).size() > 1) {//如果获取到的结果是一个List并且长度大于一,那就找是否存在合适的比较器,存在就比较Comparator<Object> comparator = adaptDependencyComparator(matchingBeans); //OrderComparatorif (comparator != null) {((List<?>) result).sort(comparator);}}}
}

原本result是自定义的排在第一,系统定义的排在第二,比较后就变化了。

事实上是先初始化了自定义的类。但是在初始化DelegatingWebMvcConfiguration时对其中的configurers进行依赖注入时获取对象,在获取对象的中途,又初始化了WebMvcAutoConfigurationAdapter,就按照系统配置先,而用户自定义后,这样实现用户自定义在后面调用,从而覆盖系统的配置。反言之,如果你给自定义的加一个@Order(0)注解,这样设置就失败了。

@Configuration(proxyBeanMethods = false
)
public 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);}}
}

这里还有一个细节,如何判断有@Order注解和没有@Order注解的类谁先谁后,根据前面我们认为没有注解的话,就排最后,这里我们可以看OrderComparator$doComparator代码。因为两个都没有实现PriorityOrdered接口,所以前面都是false,最后使用getOrder方法取出这个取出这个值才行。

系统的类标注了@Order(0),自定义的没有标注,就是返回最大值LOWEST_PRECEDENCE=2147483648

private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {//o1 是PriorityOrdered 则为trueboolean p1 = o1 instanceof PriorityOrdered;//o2 是PriorityOrdered 则为trueboolean p2 = o2 instanceof PriorityOrdered;if (p1 && !p2) {// p1是而p2不是,则返回小于return -1;} else if (p2 && !p1) {// 反之返回大于,也就是说没有注解@Order的是比较大的return 1;} else {// 都实现了,就看谁注解的小int i1 = this.getOrder(o1, sourceProvider);int i2 = this.getOrder(o2, sourceProvider);return Integer.compare(i1, i2);}}

总结

这里大家大概对Springboot的自动装配有点点了解了。一般前面的装配流程大部分博文将的比较多,Springboot根据应用场景将大部分的配置都配置好了,所以你基本就添加一个Springboot的依赖就可以开始开发业务的流程。

但是就我个人来说,我觉得boot对新手(不了解Spring系列底层源码)来说不是特别友好,虽然你可以什么都不想就直接用,那非常方便。但是碰到需要自定义的一些场景就会容易出问题,而且容易知其然而不知其所以然。例如我对SpringMVC就总觉得很抽象,虽然前端这样写,后端这样写就行,但是因为tomcat之类的服务器帮我们处理了太多,导致总觉得有点不太顺畅,但是也不影响开发。但是在时间充分的情况下还是多了解了解远离比较好,毕竟不要被卡脖子嘛。

后面的流程因为在学习的时候没找到答案,所以我是一步步debug出来的,虽然有些博主大概说过,但是从源码的角度看更加的直接,希望能对大家了解这个装配流程和自定义有所帮助。

Springboot 个性化配置SpringMVC相关推荐

  1. SpringBoot之配置嵌入式Servlet容器

    1.概述 文章目录 1.概述 2.如何修改SpringBoot的默认配置 3.定制和修改Servlet容器的相关配置 4.注册Servlet三大组件 5.替换为其他嵌入式Servlet容器 6.嵌入式 ...

  2. 这样讲 SpringBoot 自动配置原理,你应该能明白了吧

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:你在我家门口 juejin.im/post/5ce5effb ...

  3. SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置入门介绍

    springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...

  4. springboot起步配置和自动配置原理

    一.起步配置 1.起步配置Ctrl+左键进入 2.springboot自动识别resources目录下以application开头的配置文件 3.spring-boot-starter-parent又 ...

  5. SpringBoot | 自动配置原理

    微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 这个月过去两天了,这篇文章才跟大家见面,最近比较累,大家见谅下.下班后闲着无聊看了下 SpringBoot 中的自动配置,把我 ...

  6. 注解不自动导包_玩转SpringBoot2.X:SpringBoot自动配置原理大揭秘

    我们在使用SpringBoot的时候,是不是觉得特方便,根本不需要我们去配置什么端口号,应用名称,又比如我们再整合redis的时候,其实也不需要我们去指定端口号,IP,都会有默认的.是不是特方便.那么 ...

  7. SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理

    springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...

  8. 第9步 spring 配置 springmvc配置

    spring配置 有5个网址   springboot 再讲一遍  spring的学习最好的方法是运行  官方demo  学习它里面的配置   . 我们不可能一下子理解spring里面的源码 spri ...

  9. SpringBoot 自动配置原理

    创建项目 通过Spring Initialize创建SpringBoot项目 而接下来要说的是关于配置文件的事情.关乎配置文件可以参考官方文档. 对于配置文件来说到底在配置文件里面可以进行配置那些内容 ...

最新文章

  1. 字符串处理函数C语言实现(一)
  2. codevs 4560 NOIP2015 D2T2 子串
  3. 五天带你学完《计算机网络》·第三天·传输层
  4. MySQL性能优化最佳实践 - 02 MySQL数据库性能衡量
  5. 十万个为什么儿童版_《虹猫蓝兔十万个为什么》上架爱奇艺奇巴布绘本馆
  6. java请求并行方案_让 Yar Java Client 支持执行并行请求,ExecutorService 的使用
  7. 命令行cmd跳转到其他地址
  8. VIIRS SDR数据预处理(一)
  9. C++:使用类方法根据四点计算四面体体积
  10. EPUB、CAJ 、PDF 格式的区别,windows上有什么好用的epub阅读器
  11. Font Awesome 是一套绝佳的图标字体库和CSS框架
  12. 【备读学术论文总览】研究方向论文清单
  13. Mysql中的循环语句
  14. 7款最流行的在线项目管理工具
  15. 宝塔面板可以建立静态网站吗?如何部署一个静态页面?
  16. Linux函数exec
  17. 筑基期第一式:SpringMVC源码解析
  18. 华师在线计算机考试登陆不上,华师在线首页登录网址
  19. 你还在问我Bean的生命周期?带你看看一个Spring Bean从诞生到逝去的九次人生转折!
  20. 手把手带你写代码,完美实现Java分页功能

热门文章

  1. NAS数据迁移到对象存储太麻烦?90分钟纳管1000万文件了解一下
  2. 硬盘柱面损坏怎么办_硬盘有坏道就不能用了吗?别再吃哑巴亏了,今天跟人人再说一次...
  3. HJZS-E202电源监视继电器(断电延时)
  4. SQL 创建数据库,创建表
  5. 北京交通大学计算机科学与技术研究生分数线,2019北京交通大学研究生分数线汇总(含2016-2019历年复试)...
  6. vue实现Tab切换功能
  7. 用c++写一百以内的质数
  8. 使用MobLink点击链接打开app
  9. 全球最大的黑客门户网站--黑客基地
  10. Selenium之悬浮菜单定位