当前位置: 首页 > article >正文

SpringBoot整合springmvc

文章目录

  • 1.SpringMVC的自动管理
    • 1.1中央转发器
      • 1.1.1Spring boot配置多个DispatcherServlet
    • 1.2控制器
      • 1.2.1找到启动类的位置
        • 1.2.1.1SpringApplication.run()
        • 1.2.1.2SpringApplication 构造方法
        • 1.2.1.3deduceMainApplicationClass()
      • 1.2.2@ComponentScan 注解
    • 1.3视图解析器自动管理
    • 1.4静态资源访问
    • 1.5消息转换和格式化
    • 1.6欢迎页面的自动配置
  • 2.SpringBoot扩展springmvc
    • 2.1WebMvcConfigurer 接口
    • 2.2在容器中注册视图控制器(请求转发)
    • 2.3注册格式化器
    • 2.4消息转换器扩展fastjson
    • 2.5拦截器注册


1.SpringMVC的自动管理

中央转发器(DispatcherServlet)

控制器

视图解析器

静态资源访问

消息转换器

格式化

静态资源管理

1.1中央转发器

在之前的SSM项目中,中央转发器需要在.xml中配置

<servlet>
    <servlet-name>chapter2</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>chapter2</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

使用SpringBoot后,中央转发器被SpringBoot自动接管,不需要在web.xml中配置

image-20241229115030621

1.1.1Spring boot配置多个DispatcherServlet

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration中有个DispatcherServletRegistrationBean这里只能指定一个path

源码如下:

image-20241229142823511

image-20241229142947698

image-20241229143012138

如果想指定多个DispatcherServlet,我们需要自己写DispatcherServletRegistrationBean这个Bean

示例代码如下:

@Autowired
private WebMvcProperties webMvcProperties;
@Autowired
private MultipartConfigElement multipartConfig;

@Bean
@Primary
public DispatcherServletRegistrationBean dispatcherServlet1(DispatcherServlet dispatcherServlet) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
            dispatcherServlet, "/*");
    registration.setName("dispatcherServlet1");
    registration.setLoadOnStartup(
            this.webMvcProperties.getServlet().getLoadOnStartup());
    if (this.multipartConfig != null) {
        registration.setMultipartConfig(this.multipartConfig);
    }
    return registration;
}

@Bean
public DispatcherServletRegistrationBean dispatcherServlet2(DispatcherServlet dispatcherServlet) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
            dispatcherServlet, "/aaa/*");
    registration.setName("dispatcherServlet2");
    registration.setLoadOnStartup(
            this.webMvcProperties.getServlet().getLoadOnStartup());
    if (this.multipartConfig != null) {
        registration.setMultipartConfig(this.multipartConfig);
    }
    return registration;
}

@Bean
public DispatcherServletRegistrationBean dispatcherServlet3(DispatcherServlet dispatcherServlet) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
            dispatcherServlet, "/bbb/*");
    registration.setName("dispatcherServlet3");
    registration.setLoadOnStartup(
            this.webMvcProperties.getServlet().getLoadOnStartup());
    if (this.multipartConfig != null) {
        registration.setMultipartConfig(this.multipartConfig);
    }
    return registration;
}

这里是做了三个Bean,注意有一个一定要加上@Primary注解,否则启动会有报错。

如果我们系统有一个接口url是/api/test,那么通过/aaa/api/test或者/bbb/api/test也都可以访问了。

1.2控制器

控制器Controller在springboot的注解扫描范围内自动管理。底层逻辑为java反射。

Spring Boot 的 @ComponentScan 默认会扫描启动类所在包及其子包。这是通过 @SpringBootApplication 中的 @ComponentScan 实现的。因此,启动类的位置对组件扫描范围至关重要。

image-20241229144045927

1.2.1找到启动类的位置

1.2.1.1SpringApplication.run()

Spring Boot 启动器的定位通过,SpringBoot的入口类SpringApplication.run() 方法触发。

image-20241229145006785

SpringApplication.run() 是启动流程的入口,其源码如下:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

这里的 primarySource 就是启动类(启动器)。

  • primarySource 的来源: 通过调用 SpringApplication 的构造方法传入。
1.2.1.2SpringApplication 构造方法
public SpringApplication(Class<?>... primarySources) {
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = deduceWebApplicationType();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

this.primarySources:存储传入的启动类。

deduceMainApplicationClass():尝试自动推断启动类。

1.2.1.3deduceMainApplicationClass()
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    } catch (ClassNotFoundException ex) {
        // 忽略异常
    }
    return null;
}
  • 通过分析当前线程的调用堆栈,找到调用 main 方法的类。
  • 找到第一个包含 main 方法的类并返回。

这就是为什么 Spring Boot 能自动定位启动类的位置。

1.2.2@ComponentScan 注解

@ComponentScan 是 Spring 框架用于自动扫描和注册组件的注解。@SpringBootApplication 中自带了 @ComponentScan,默认会扫描启动类所在的包及其子包。

通过excludeFilters来过滤掉,到底应该扫描哪些包

1.3视图解析器自动管理

Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

ContentNegotiatingViewResolver:组合所有的视图解析器的;

曾经的配置文件无需再配

<bean id="de" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"></property>
    <property name="suffix" value="*.jsp"></property>
</bean>

源码:

public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
 resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
    resolver.setOrder(-2147483648);
    return resolver;
}

用不同的模板引擎会有不同的自动配置

Thymeleaf 示例

image-20241229154806853

如果你的视图文件路径或者文件名不同,可以通过配置文件来修改。

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

当我们做文件上传的时候我们也会发现是multipartResolver自动被配置好的

<form action="/upload" method="post" enctype="multipart/form-data">
    <input name="pic" type="file">
    <input type="submit">
</form>
@ResponseBody
@RequestMapping("/upload")
public String upload(@RequestParam("pic")MultipartFile file, HttpServletRequest request){
    String contentType = file.getContentType();
    String fileName = file.getOriginalFilename();
    /*System.out.println("fileName-->" + fileName);
    System.out.println("getContentType-->" + contentType);*/
    //String filePath = request.getSession().getServletContext().getRealPath("imgupload/");
    String filePath = "D:/imgup/";
    try {
        this.uploadFile(file.getBytes(), filePath, fileName);
    } catch (Exception e) {
        // TODO: handle exception
    }

    return "success";
}


public static void uploadFile(byte[] file, String filePath, String fileName) throws Exception {
    File targetFile = new File(filePath);
    if(!targetFile.exists()){
        targetFile.mkdirs();
    }
    FileOutputStream out = new FileOutputStream(filePath+fileName);
    out.write(file);
    out.flush();
    out.close();
}

文件上传大小可以通过配置来修改

打开application.properties, 默认限制是10MB,我们可以任意修改

image-20241229155137711

1.4静态资源访问

Spring Boot 会默认将以下路径作为静态资源的目录:

  • src/main/resources/static
  • src/main/resources/public
  • src/main/resources/resources
  • src/main/resources/META-INF/resources

将静态文件放在这些目录中,可以通过 HTTP 直接访问它们。

1.5消息转换和格式化

Springboot自动配置了消息转换器

源码部分

@Override
		public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
			this.messageConvertersProvider
					.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
		}

image-20241229160047694

格式化转换器的自动注册

image-20241229160130155

我们可以在配置文件中修改时间类型

image-20241229160348199

image-20241229160803147

1.6欢迎页面的自动配置

Springboot自动指定resources下的index.html

image-20241229161109110

Spring Boot 会自动配置静态资源的路径,默认情况下,resources/staticresources/publicresources/resourcesresources/META-INF/resources 等目录下的资源会被自动映射到 HTTP 请求的根路径 /

2.SpringBoot扩展springmvc

在实际开发中,Spring Boot 确实提供了很多自动配置功能,帮助我们简化了很多配置和开发工作。但由于每个项目的业务需求不同,Spring Boot 并不能覆盖所有的场景,因此,开发者常常需要根据具体的业务需求对其进行扩展。

2.1WebMvcConfigurer 接口

public interface WebMvcConfigurer {
    default void configurePathMatch(PathMatchConfigurer configurer) {
    }

    default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    }

    default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    }

    default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    }

    default void addFormatters(FormatterRegistry registry) {
    }

    default void addInterceptors(InterceptorRegistry registry) {
    }

    default void addResourceHandlers(ResourceHandlerRegistry registry) {
    }

    default void addCorsMappings(CorsRegistry registry) {
    }

    default void addViewControllers(ViewControllerRegistry registry) {
    }

    default void configureViewResolvers(ViewResolverRegistry registry) {
    }

    default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    }

    default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
    }

    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }

    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }

    @Nullable
    default Validator getValidator() {
        return null;
    }

    @Nullable
    default MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}

2.2在容器中注册视图控制器(请求转发)

创建一个MyMVCCofnig实现WebMvcConfigurer接口,实现一下addViewControllers方法,我们完成通过/tx访问,转发到success.html的工作

@Configuration
public class MyMVCCofnig implements WebMvcConfigurer{
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/tx").setViewName("success");
    }
}

image-20241229164535087

2.3注册格式化器

用来可以对请求过来的日期格式化的字符串来做定制化。当然通过application.properties配置也可以办到。

@Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new Formatter<Date>() {

            @Override
            public String print(Date date, Locale locale) {
                // 将 Date 对象转换为字符串(格式化)
                // 使用 SimpleDateFormat 将 Date 转换为指定格式的字符串
                return new SimpleDateFormat("yyyy-MM-dd").format(date);
            }

            @Override
            public Date parse(String s, Locale locale) throws ParseException {
                // 将字符串解析为 Date 对象
                // 假设日期格式是 "yyyy-MM-dd"
                return new SimpleDateFormat("yyyy-MM-dd").parse(s);
            }
        });
    }

2.4消息转换器扩展fastjson

在pom.xml中引入fastjson

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.47</version>
</dependency>

配置消息转换器,添加fastjson

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    FastJsonHttpMessageConverter fc = new FastJsonHttpMessageConverter();
    FastJsonConfig fastJsonConfig = new FastJsonConfig();
    fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
    fc.setFastJsonConfig(fastJsonConfig);
    converters.add(fc);
}
public class User {

    private String username;

    private String password;

    private int age;

    private int score;

    private int gender;

    @JSONField(format = "yyyy-MM-dd")
    private Date date;

2.5拦截器注册

  1. 创建拦截器
public class MyInterceptor implements HandlerInterceptor{

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
        System.out.println("前置拦截");
        return  true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("后置拦截");
    }

    public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {
        System.out.println("最终拦截");
    }
}

拦截器注册

//配置注册自己的拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new MyInterceptor())
                //配置拦截所有路径
                .addPathPatterns("/**")
                //配置不拦截路径  可变参数 可以使多个参数  数组
                .excludePathPatterns("/test2");
    }

http://www.kler.cn/a/457386.html

相关文章:

  • node内置模块之---path 模块
  • const修饰指针
  • Win11+WLS Ubuntu 鸿蒙开发环境搭建(二)
  • 杰盛微 JSM4056 1000mA单节锂电池充电器芯片 ESOP8封装
  • Rabbitmq追问1
  • 前端异常处理合集
  • 遗传算法——附python实现与各方法的作用与修改
  • 【强化学习】深度确定性策略梯度算法(DDPG)详解(附代码)
  • 碰一碰发视频矩阵系统源码搭建,支持OEM
  • Qt仿音乐播放器:QFileDialog添加本地文件
  • BMS存储模块的设计
  • idea 禁用/关闭 sonarlint 后台自动分析(默认开启可能会引起idea卡顿)
  • 【K8s】专题十五(6):Kubernetes 网络之 Pod 网络调试
  • Netty 2024 常见面试题
  • 个人用途虚拟机VM 17安装Ubuntu 16.04.5 图解
  • 大数据面试笔试宝典之Spark面试
  • Java网约车项目实战:实现抢单功能详解
  • golangb并发,Sync包中Mutes,WaitGroup, NewCond的适应
  • 八大排序——直接插入排序
  • 几款性能优秀的差分进化算法DE(SaDE、JADE,SHADE,LSHADE、LSHADE_SPACMA、LSHADE_EpSin)-附Matlab免费代码
  • CAN201 Introduction to Networking(计算机网络)Pt.2 传输层
  • 行为树详解(5)——事件驱动
  • 跨域请求问题
  • 一种基于动态部分重构的FPGA自修复控制器
  • 美国站群服务器如何帮助实现有效的多域名管理?
  • 【深入理解SpringCloud微服务】Sentinel源码解析——FlowSlot流控规则