一、web
1、SpringBoot对静态资源的映射规则
在WebMvcAuotConfiguration.class中(可以点两次shift全局搜索)
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
//配置欢迎页映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return welcomePageHandlerMapping;
}
1)将所有 /webjars/**,都去 classpath:/META-INF/resources/webjars/ 找资源;webjars:以jar包的方式引入静态资源
WebJars - Web Libraries in Jars
需要引入依赖
<!-- 引入jquery‐webjar‐‐>在访问的时候只需要写webjars下面资源的名称即可-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
2)"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
"classpath:/META‐INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/""/":当前项目的根路径
localhost:8080/abc=== 去静态资源文件夹里面找abc
3)欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
localhost:8080/ 找index页面,默认在resources/resources文件夹下
2、模板引擎
SpringBoot推荐的Thymeleaf,语法更简单,功能更强大;
引入thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
从ThymeleafAutoConfiguration的源代码中我们可以得知ThymeleafProperties
中配置了Thymeleaf的规则
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
我们使用html作为模板,而且默认的前缀是放在classpath:/templates/下,后缀是.html
当然这些属性我们都可以通过application.properties来修改。我们采用默认即可。
示例:
-
在templates下创建一个success.html
-
在html中引入thymeleaf的命名空间
lang="en" xmlns:th="Thymeleaf">
-
创建一个Controller提供一个访问的方法
@RequestMapping("/success")
public String hello(Model model){
model.addAttribute("hello","欢迎您!");
return "success";
} -
在thymeleaf模板中取值 success.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot</title> </head> <body>springboot整合web
二、Spring Boot整合springmvc
Spring Boot 和 Spring MVC 的整合是自然的,因为 Spring Boot 默认就集成了 Spring MVC。这意味着,当你创建一个 Spring Boot 项目时,你其实已经在使用 Spring MVC 来处理 web 请求了。
- 当Spring Boot应用启动时,它会扫描项目中的组件并尝试自动配置所需的Bean。对于SpringMVC,它会检查项目中是否存在@Controller或@RestController注解的类,并自动配置相应的处理器映射和适配器。
- 如果开发者在项目中提供了自定义的SpringMVC配置类(如实现了WebMvcConfigurer接口的类),Spring Boot会检测到这些配置类,并调用其中的方法来覆盖或扩展默认配置。这允许开发者在保持Spring Boot自动配置带来的便利性的同时,还能够灵活地定制Web应用的行为。
WebMvcAutoConfiguration 是 Spring Boot 中用于自动配置 Spring MVC 的核心类。该类基于条件注解来决定哪些组件应该被自动配置,这取决于类路径中是否存在特定的类以及是否已经定义了某些类型的 Bean。
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
以下是 WebMvcAutoConfiguration 的一些关键部分的简要概述以及它是如何工作的:
- 条件注解:WebMvcAutoConfiguration 使用多种条件注解来确保只有在满足特定条件时才会进行自动配置。例如,@ConditionalOnWebApplication(type = Type.SERVLET) 确保这个自动配置只在 Servlet 类型的 Web 应用程序中生效。
- 视图解析器:基于类路径上的类和是否存在自定义的 Bean,WebMvcAutoConfiguration 会尝试自动配置各种视图解析器。例如,如果检测到 Thymeleaf 依赖,它会配置一个 ThymeleafViewResolver。
- 消息转换器:自动配置还会包括各种消息转换器(如 MappingJackson2HttpMessageConverter 用于 JSON),这些转换器用于在客户端和服务器之间转换消息体。
- 异常处理器:默认情况下,会配置一个全局的异常处理器来处理应用程序中的异常,并将其转换为适当的 HTTP 响应。
- 静态资源处理:自动配置还包括静态资源的处理,如 HTML、CSS、JavaScript 等文件。
- 拦截器和过滤器:可以自动配置一些常用的拦截器和过滤器,用于处理跨域请求、日志记录等。
- CORS 配置:如果需要,可以自动配置 CORS(跨源资源共享)支持。
- WebMvcConfigurer 复合:如果你的应用程序定义了自定义的 WebMvcConfigurer Bean,WebMvcAutoConfiguration 会将其与自动配置的 WebMvcConfigurer 实现合并,以确保你的自定义配置覆盖自动配置。
源码原理:
- WebMvcAutoConfiguration 类通过 @Configuration 注解标记为一个配置类。
- 使用 @Conditional* 注解来定义哪些条件必须满足才能应用这个自动配置。
- 在类内部,使用 @Bean 注解来定义要创建的 Bean,这些 Bean 是 Spring MVC 的组件,如 RequestMappingHandlerAdapter、ViewResolver 等。
- 对于每个组件,都会有一个或多个条件注解来检查是否应该创建该 Bean。例如,@ConditionalOnMissingBean 会检查是否已经有一个同类型的 Bean 存在,如果有,则不会创建新的 Bean。
- 在初始化过程中,Spring Boot 会扫描所有带有 @Configuration 注解的类,并解析其中的 @Bean 方法来创建 Bean。对于 WebMvcAutoConfiguration,它会根据条件注解来决定是否创建特定的 Bean。
1、springmvc的核心组件和功能
① 中央转发器(DispatcherServlet)
DispatcherServlet是Spring MVC的核心组件,负责请求的分发和处理。在Spring Boot中,DispatcherServlet会被自动配置和初始化。它会接收所有的HTTP请求,并根据请求的信息(如URL、HTTP方法、请求头、请求参数等)决定如何处理这些请求。
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
@AutoConfigureOrder(-2147483648)
@AutoConfiguration(
after = {ServletWebServerFactoryAutoConfiguration.class}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({DispatcherServlet.class})
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
public DispatcherServletAutoConfiguration() {
}
...
@Bean
@ConditionalOnBean({MultipartResolver.class})
@ConditionalOnMissingBean(
name = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver;
}
在Spring Boot中,DispatcherServletAutoConfiguration是负责自动配置DispatcherServlet(Spring MVC的核心组件)的类。这个自动配置类基于Spring Boot的自动配置机制,在检测到Spring MVC在类路径下时自动进行配置。
条件化配置
DispatcherServletAutoConfiguration类使用了Spring的条件注解(如@ConditionalOnClass和@ConditionalOnMissingBean)来确定是否应该创建特定的Bean。
- @ConditionalOnClass:检查类路径下是否存在特定的类(指那些用于触发自动配置类是否应该被加载的类),对于DispatcherServletAutoConfiguration,它检查是否存在DispatcherServlet和ServletRegistrationBean类。
- @ConditionalOnMissingBean:检查Spring容器中是否已经存在某个类型的Bean。如果存在,则不会创建新的Bean。
自动配置Bean
如果满足上述条件,DispatcherServletAutoConfiguration将自动配置以下Bean:
- DispatcherServlet: 创建DispatcherServlet的Bean实例。这通常是一个标准的DispatcherServlet,除非有其他的条件化Bean覆盖了它。
- ServletRegistrationBean: 创建一个ServletRegistrationBean,用于将DispatcherServlet注册到Servlet容器中。这个Bean会配置DispatcherServlet的URL映射(通常是/),以及其他可能的Servlet配置。
定制化
Spring Boot的自动配置旨在提供合理的默认设置,但允许开发者通过定义自己的Bean来覆盖自动配置的Bean。这意味着,如果你需要在你的Spring Boot应用中定制DispatcherServlet或它的配置,你可以通过简单地定义你自己的DispatcherServlet或ServletRegistrationBean类型的Bean来实现。
整合流程
- Spring Boot应用启动时,DispatcherServletAutoConfiguration被Spring Boot的自动配置机制扫描到。
- Spring Boot检查类路径上是否存在必要的类(如DispatcherServlet),以及Spring容器中是否缺少相关的Bean。
- 如果满足条件,DispatcherServletAutoConfiguration将创建并注册DispatcherServlet和ServletRegistrationBean。
- 如果开发者提供了自己的DispatcherServlet或ServletRegistrationBean,那么这些自定义的Bean将覆盖自动配置的Bean。
- 应用启动完成后,DispatcherServlet开始接收并处理HTTP请求。
注意事项
- 开发者在覆盖自动配置的Bean时,需要确保自己定义的Bean与自动配置的Bean类型匹配,以便正确覆盖。
- 通常情况下,除非有特定的需求,否则不需要手动配置DispatcherServlet,因为Spring Boot的自动配置已经提供了很好的默认设置。
② 控制器(Controller)
控制器是处理业务逻辑和返回响应的组件。在Spring MVC中,你可以使用@Controller或@RestController注解来标记一个类作为控制器。Spring Boot会自动扫描这些带有注解的类,并将它们作为控制器进行注册和管理。
③ 视图解析器(ViewResolver)
视图解析器负责将逻辑视图名称解析为具体的视图实现。Spring Boot会自动配置一些常用的视图解析器,如ThymeleafViewResolver(如果你添加了Thymeleaf依赖)或InternalResourceViewResolver(用于JSP视图)。你也可以自定义视图解析器并注册到Spring容器中,以覆盖默认的配置。
在Spring Boot中,视图解析器的自动配置通常发生在WebMvcAutoConfiguration类中。这个类负责自动配置Spring MVC的各种组件,包括视图解析器。这是springmvc的核心类。
Spring Boot会根据项目的依赖和类路径上的类来自动配置视图解析器。例如,如果你的项目中包含了Thymeleaf的依赖,Spring Boot会自动配置一个Thymeleaf视图解析器。同样地,如果包含了JSP的依赖,它会尝试配置一个InternalResourceViewResolver用于解析JSP视图。
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
④ 静态资源访问
在Web应用中,通常需要访问静态资源,如HTML、CSS、JavaScript、图片等。Spring Boot会自动配置静态资源的访问路径。默认情况下,它会从src/main/resources/static、src/main/resources/public、src/main/resources/templates和src/main/resources/META-INF/resources目录下加载静态资源。你可以通过配置来调整这些路径。
⑥ 消息转换器(MessageConverter)
消息转换器负责将HTTP请求和响应体中的消息转换为Java对象。Spring Boot会自动配置一系列的消息转换器,如StringHttpMessageConverter、MappingJackson2HttpMessageConverter(用于JSON)等。这些转换器会根据请求或响应的内容类型自动选择使用。你也可以添加自定义的消息转换器来满足特殊需求。
以下是springboot的WebMvcAutoConfiguration中关于消息转换的源码
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider.ifAvailable((customConverters) -> {
converters.addAll(customConverters.getConverters());
});
}
//用于解析消息代码,通常用于处理错误消息的代码。
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
} else {
return null;
}
}
这段代码的作用是检查是否有可用的自定义HTTP消息转换器,如果有,就将这些转换器添加到主消息转换器列表中。这样,当Spring Boot应用程序需要转换HTTP请求或响应时,它就可以使用这些自定义转换器了
HTTP消息转换器是Spring框架中用于在HTTP请求和响应之间转换对象的关键组件。例如,它们可以将Java对象转换为JSON或XML格式,以便发送到客户端,或者将客户端发送的JSON或XML数据转换为Java对象。
⑦ 格式化
在Spring MVC中,格式化通常指的是对请求参数和模型属性的格式化。Spring Boot会自动配置一些格式化器,如日期格式化器、数字格式化器等。这些格式化器会根据注解(如@DateTimeFormat、@NumberFormat)或配置进行自动应用。如对前端传过来xxx/xx/xx格式的日期进行解析
以下是springboot的WebMvcAutoConfiguration中关于格式化的源码
@Bean
public FormattingConversionService mvcConversionService() {
// 获取MvcProperties中的Format配置
Format format = this.mvcProperties.getFormat();
// 创建一个WebConversionService实例,并设置日期、时间和日期时间的格式
WebConversionService conversionService = new WebConversionService(
(new DateTimeFormatters()).dateFormat(format.getDate())
.timeFormat(format.getTime())
.dateTimeFormat(format.getDateTime())
);
// 向conversionService中添加额外的格式转换器
this.addFormatters(conversionService);
// 返回conversionService实例
return conversionService;
}
public void addFormatters(FormatterRegistry registry) {
ApplicationConversionService.addBeans(registry, this.beanFactory);
}
通过mvcConversionService方法,你可以定制日期、时间和日期时间的格式,并通过
addFormatters方法向格式转换服务中添加额外的格式化器。这些格式化器将在处理Web请求时用于转换数据。
⑧ 静态资源管理
静态资源管理主要涉及到如何组织和提供静态资源文件。除了上述的自动配置静态资源访问路径外,Spring Boot还提供了其他静态资源管理的功能,如资源缓存、资源版本控制等。这些功能可以通过配置或编程方式进行定制。
⑨ 文件上传
当我们做文件上传的时候我们也会发现multipartResolver是自动被配置好的,在DispatcherServletAutoConfiguration中,同时在MultipartProperties定义了默认上传的最大文件为1MB
@ConfigurationProperties(
prefix = "spring.servlet.multipart",
ignoreUnknownFields = false
)
public class MultipartProperties {
private boolean enabled = true;
private String location;
private DataSize maxFileSize = DataSize.ofMegabytes(1L); //单个文件大小
private DataSize maxRequestSize = DataSize.ofMegabytes(10L); //整个http请求文件大小
private DataSize fileSizeThreshold = DataSize.ofBytes(0L);
private boolean resolveLazily = false;
public MultipartProperties() {
}
...
可以通过配置文件修改
spring:
servlet:
multipart:
max-file-size: 30MB
案例:
<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:/";
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();
}
2、Springboot扩展springmvc
在实际开发中springboot并非完全自动化,很多跟业务相关我们需要自己扩展,springboot给我提供了接口。我们可以来通过实现WebMvcConfigurer接口来扩展。以下是WebMvcConfigurer接口
package org.springframework.web.servlet.config.annotation;
import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
public interface WebMvcConfigurer {
// 配置路径匹配策略
default void configurePathMatch(PathMatchConfigurer configurer) {
}
// 配置内容协商策略
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
// 配置异步支持设置
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
// 配置默认的Servlet处理,比如静态资源的访问
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
// 添加自定义的格式化器到格式化器注册表中
default void addFormatters(FormatterRegistry registry) {
}
// 添加拦截器到拦截器注册表中
default void addInterceptors(InterceptorRegistry registry) {
}
// 添加资源处理器到资源处理器注册表中,用于处理静态资源请求
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}
// 添加CORS映射到CORS注册表中,用于跨域资源共享
default void addCorsMappings(CorsRegistry registry) {
}
// 添加视图控制器到视图控制器注册表中,用于简单的URL到视图的映射
default void addViewControllers(ViewControllerRegistry registry) {
}
// 配置视图解析器
default void configureViewResolvers(ViewResolverRegistry registry) {
}
// 添加自定义的参数解析器到参数解析器列表中
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
// 添加自定义的返回值处理器到返回值处理器列表中
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
// 配置HTTP消息转换器,用于在请求和响应体之间转换对象
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
// 扩展现有的HTTP消息转换器列表,通常用于在现有转换器列表的基础上添加或修改转换器
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;
}
}
创建一个MyMVCCofnig实现WebMvcConfigurer接口
①在容器中注册视图控制器(请求转发)
实现一下addViewControllers方法,我们完成通过/tx访问,转发到success.html的工作
@Configuration
public class MyMVCCofnig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/sss").setViewName("success");
}
}
②注册格式化器
可以对(前端)请求过来的日期格式化的字符串来做定制化。前端传递的日期字符串自动转换为Java Date对象。通过配置文件也可以办到。
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new Formatter<Date>() {
@Override
public String print(Date date, Locale locale) {
return null;
}
@Override
public Date parse(String s, Locale locale) throws ParseException {
return new SimpleDateFormat("yyyy-MM-dd").parse(s);
}
});
}
在application.properties中,你可以设置spring.mvc.date-format属性来定义全局的日期格式:
spring.mvc.date-format=yyyy-MM-dd
在application.yml中,可以这样配置:
spring:
mvc:
date-format: yyyy-MM-dd
③ 消息转换器扩展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);
}
这段代码重写了configureMessageConverters方法,用于配置HTTP消息转换器(HttpMessageConverter)。HTTP消息转换器在Spring MVC中用于在HTTP请求和响应体之间转换对象。
下面是代码的详细解释:
-
@Override: 这是一个Java注解,表示该方法重写了父类或接口中的方法。在这里,它表示configureMessageConverters方法是从某个父类或接口中继承或实现的,并且在这里进行了重写。
-
public void configureMessageConverters(List> converters): 这是configureMessageConverters方法的定义。它接受一个HttpMessageConverter对象的列表作为参数。这个列表包含了Spring MVC中所有可用的消息转换器。
-
FastJsonHttpMessageConverter fc = new FastJsonHttpMessageConverter();: 这里创建了一个新的FastJsonHttpMessageConverter对象,它是一个基于fastjson库的HTTP消息转换器,用于处理JSON格式的数据。
-
FastJsonConfig fastJsonConfig = new FastJsonConfig();: 创建一个新的FastJsonConfig对象,用于配置fastjson库的行为。
-
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);: 配置fastjson的序列化特性。这里设置了SerializerFeature.PrettyFormat,它表示输出的JSON数据将会格式化(易于阅读)。
-
fc.setFastJsonConfig(fastJsonConfig);: 将配置好的fastJsonConfig对象设置到FastJsonHttpMessageConverter对象中,这样当转换器进行序列化和反序列化时,会使用这些配置。
-
converters.add(fc);: 将配置好的FastJsonHttpMessageConverter对象添加到消息转换器的列表中。这意味着在后续的请求和响应中,Spring MVC会使用这个转换器来处理JSON数据。
-
总的来说,这段代码的目的是在Spring MVC中配置一个使用fastjson库的JSON消息转换器,并设置其输出为格式化后的JSON数据。
在实体类上可以继续控制,在前端页面显示格式,后端传给前端的显示格式
@JSONField(format= "yyyy-MM-dd") //后端传给前端的显示格式
private Date date;@JsonFormat(pattern="yyyy-MM-dd") //后端传给前端的显示格式,也能约束前端传给后端的数据格式,但是数据类型需要是json
@DateTimeFormat(pattern="yyyy-MM-dd")//前端传递的日期字符串自动转换为Java Date对象时
spring.mvc.date-format=yyyy-MM-dd //前端传递的日期字符串自动转换为Java Date对象时
④拦截器注册
创建拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置拦截");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("后置拦截");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("最终拦截");
}
}
拦截器注册,在MyMVCCofnig中重写addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/hello2");
}
3、配置嵌入式服务器
① 定制和修改Servlet容器的相关配置
server.port=8081
server.context‐path=/tx
server.tomcat.uri‐encoding=UTF‐8
② 注册Servlet三大组件【Servlet、Filter、Listener】
SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。
1)注册Servlet
Servlet是Web应用程序的核心组件,它负责处理来自客户端的请求并生成响应。在Spring Boot中,你可以注册自定义的Servlet来处理特定的URL路径。Servlet可以读取请求参数、执行业务逻辑、访问数据库,并将结果写回到响应中。通过注册Servlet,你可以扩展Spring Boot应用程序的功能,实现自定义的请求处理逻辑。
1.1)使用@WebServlet注解
尽管Spring Boot推荐使用Java配置,但如果你仍然想使用@WebServlet注解,你可以直接在Servlet类上使用它:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 处理GET请求
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// 处理POST请求
}
}
然后确保你的@SpringBootApplication类所在的包或其父包能够扫描到这个Servlet。
1.2)使用RegistrationBean
你可以创建一个配置类,并在其中使用ServletRegistrationBean来注册Servlet:
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ServletConfig {
@Bean
public ServletRegistrationBean<MyServlet> myServletRegistrationBean() {
ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>();
registrationBean.setServlet(new MyServlet());
registrationBean.addUrlMappings("/myServlet");
return registrationBean;
}
}
2)注册Filter
Filter是Servlet规范中定义的组件,用于在请求到达Servlet之前或响应返回给客户端之后执行预处理或后处理操作。在Spring Boot中,你可以注册自定义的Filter来执行诸如身份验证、日志记录、编码设置、请求/响应的修改等任务。Filter可以对请求和响应进行拦截和修改,从而实现对Web应用程序的增强和定制。
2.1)使用@WebFilter注解
和Servlet类似,你可以直接在Filter类上使用@WebFilter注解:
import javax.servlet.annotation.WebFilter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
@WebFilter("/filteredPath/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化代码
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 过滤逻辑
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 销毁代码
}
}
2.2) 使用FilterRegistrationBean
你也可以使用FilterRegistrationBean来注册Filter:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistrationBean() {
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new MyFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return registrationBean;
}
}
3) 注册Listener
Listener是Servlet规范中定义的组件,用于监听Web应用程序中的特定事件,并在事件发生时执行相应的操作。在Spring Boot中,你可以注册自定义的Listener来监听应用程序的生命周期事件(如上下文初始化、上下文销毁)或会话事件(如会话创建、会话销毁)。通过监听这些事件,你可以在应用程序启动时加载资源、执行初始化任务,或在应用程序关闭时清理资源、执行清理任务。此外,Listener还可以用于监听其他自定义事件,以实现更复杂的业务逻辑。
3.1)使用@WebListener注解
在Spring Boot中,你可以直接在Listener类上使用@WebListener注解来注册Listener:
import javax.servlet.annotation.WebListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 上下文初始化时调用
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 上下文销毁时调用
}
}
3.2)使用ServletListenerRegistrationBean
@Bean
public ServletListenerRegistrationBeanmyListener(){
ServletListenerRegistrationBean<MyListener> registrationBean= new
ServletListenerRegistrationBean<>(new MyListener());
return registrationBean;
}
4、使用外置的Servlet容器
嵌入式Servlet容器:应用打成可执行的jar
优点:简单、便携;
缺点:默认不支持JSP、优化定制比较复杂.;
外置的Servlet容器:外面安装Tomcat---应用war包的方式打包;
四、springBoot数据层开发
1、数据源自动管理
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
创建application.yml
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/hbu
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
# type: org.apache.commons.dbcp2.BasicDataSource
如果Hikari可用, Springboot将使用它。 如果Commons DBCP2可用, 我们将使用它。
我们可以自己指定数据源配置,通过type来选取使用哪种数据源。
2、配置druid数据源
引入druid的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
修改spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
在application.yml中加入
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/hbu
driver-class-name: com.mysql.cj.jdbc.Driver
# type: com.zaxxer.hikari.HikariDataSource
# type: org.apache.commons.dbcp2.BasicDataSource
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
创建数据源注册类
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource(){
return new DruidDataSource();
}
配置druid运行期监控
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(),
"/druid/*");
Map<String,String> initParams = new HashMap<>();
initParams.put("loginUsername","root");
initParams.put("loginPassword","root");
initParams.put("allow","");//默认就是允许所有访问
initParams.put("deny","192.168.15.21");
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean;
bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
打开监控页面
可以看到数据库的图形化界面情况
3、springboot整合jdbcTemplate
创建Controller
@Controller
public class TestController {
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@RequestMapping("/query")
public List<Map<String, Object>> query() {
List<Map<String, Object>> maps = jdbcTemplate.queryForList("SELECT * FROM users");
return maps;
}
/**
* 获取用户列表
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping("/getlist")
public List<User> getUserList() throws Exception{
List<User> userList=jdbcTemplate.query("select id,account,password from users",new UserRowMapper());
System.out.println(userList);
return userList;
}
/**
* 根据用户id获取用户
* @param id
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping("/getById")
//http://localhost:8080/getById?id=1
public User getUserById(Integer id) throws Exception{
//queryForObject:找不到会报异常 query:找不到则Null
//User user=jdbcTemplate.queryForObject("select id,account,password from users where id=?",new Object[]{id},new UserRowMapper());
List<User> userList=jdbcTemplate.query("select id,account,password from users where id=?",new Object[]{id},new UserRowMapper());
User user=null;
if (!userList.isEmpty()){
user=userList.get(0);
}
System.out.println(user);
return user;
}
/**
* 插入用户数据
* @param user
* @return
* @throws Exception
*/
public int saveUser(final User user) throws Exception{
int resRow=jdbcTemplate.update("INSERT INTO users(id,account,password) VALUES(NULL,?,?)",new Object[]{
user.getAccount(),user.getPassword()
});
System.out.println("插入操作结果记录数: "+resRow);
return resRow;
}
@ResponseBody
@RequestMapping("/insert")
public int insert() throws Exception {
User user = new User();
user.setAccount("zz");
user.setPassword("123");
int res = saveUser(user);
return res;
}
/**
* 插入用户数据-防止sql注入
* @param user
* @return
* @throws Exception
*/
public int saveUserWithSafe(final User user) throws Exception{
int resRow=jdbcTemplate.update("INSERT INTO users(id,account,password) VALUES(NULL,?,?)", new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1,user.getAccount());
ps.setString(2,user.getPassword());
}
});
System.out.println("防止sql注入,插入操作结果记录数: "+resRow);
return resRow;
}
/**
* 插入用户数据-防止sql注入-可以返回该条记录的主键(注意需要指定主键)
* @param user
* @return
* @throws Exception
*/
public int saveUserWithKey(final User user) throws Exception{
String sql="INSERT INTO user(id,name,email) VALUES(NULL,?,?)";
KeyHolder keyHolder=new GeneratedKeyHolder();
int resRow=jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
PreparedStatement ps=conn.prepareStatement(sql,new String[]{"id"}); //指定 id 为主键
ps.setString(1,user.getAccount());
ps.setString(2,user.getPassword());
return ps;
}
},keyHolder);
System.out.println("插入操作结果记录数: "+resRow+" 主键: "+keyHolder.getKey());
return Integer.parseInt(keyHolder.getKey().toString());
}
/**
* 更新用户信息
* @param user
* @return
*/
public int updateUser(final User user) throws Exception{
String sql="update users set account=?,password=? where id=?";
int resRow=jdbcTemplate.update(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1,user.getAccount());
preparedStatement.setString(2,user.getPassword());
preparedStatement.setInt(3,user.getId());
}
});
System.out.println("更新操作结果记录数: "+resRow);
return resRow;
}
@ResponseBody
@RequestMapping("/update")
public int update() throws Exception {
User user = new User();
user.setId(7);
user.setAccount("ww");
user.setPassword("111111");
int res = updateUser(user);
return res;
}
/**
* 删除用户
* @param user
* @return
* @throws Exception
*/
public int deleteUser(final User user) throws Exception{
int resRow=jdbcTemplate.update("DELETE FROM users WHERE id=?", new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1,user.getId());
}
});
System.out.println("删除操作结果记录数: "+resRow);
return resRow;
}
@ResponseBody
@RequestMapping("/del")
//http://localhost:8080/del?id=8
public int del(Integer id) throws Exception {
User user = new User();
user.setId(id);
int res = deleteUser(user);
return res;
}
/**
* 根据用户名查找用户-用于判断用户是否存在
* @param user
* @return
* @throws Exception
*/
public User getUserByAccount(final User user) throws Exception{
String sql="select id,account,password from users where account=?";
List<User> queryList=jdbcTemplate.query(sql,new UserRowMapper(),new Object[]{user.getAccount()});
if (queryList!=null && queryList.size()>0){
return queryList.get(0);
}else{
return null;
}
}
@ResponseBody
@RequestMapping("/getByAccount")
//http://localhost:8080/getByAccount?account=xx
public User getByAccount(String account) throws Exception {
User user = new User();
user.setAccount(account);
User res = getUserByAccount(user);
return res;
}
/**
* 获取记录数
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping("/getCount")
public Integer getCount() throws Exception{
String sql="select count(id) from users";
//jdbcTemplate.getMaxRows();
Integer total=jdbcTemplate.queryForObject(sql,Integer.class);
System.out.println("操作结果记录数: "+total);
return total;
}
}
/**
* 行映射
*/
public class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user=new User();
user.setId(resultSet.getInt("id"));
user.setAccount(resultSet.getString("account"));
user.setPassword(resultSet.getString("password"));
return user;
}
}
Springboot中提供了JdbcTemplateAutoConfiguration的自动配置org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,
4、Springboot整合mybatis注解版
引入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
创建mapper
@Mapper
public interface UserMapper {
@Select("select * from users")
public List<User> getUsers();
@Select("select * from users t where t.id = #{id}")
public User getUserById(int id);
@Options(useGeneratedKeys =true, keyProperty = "id")
@Insert("insert into users(id, account, password)" +
" values(#{id}, #{account}, #{password})")
public void insert(User user);
@Delete("delete from users where id = #{id}")
public void update(int id);
}
解决驼峰模式和数据库中下划线不能映射的问题。
@MapperScan("com.qcby.sb_mybatis.mapper")
@Configuration
public class MybatisConfig {
@Bean
public ConfigurationCustomizer getCustomizer(){
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
5、Springboot整合mybatis配置文件
创建sqlMapConfig.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTDConfig 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
创建映射文件UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.mapper.UserMapper">
<select id="getUsers" resultType="User">
select * from user
</select>
</mapper>
在application.yml中配置mybatis的信息
mybatis:
config-location: classpath:mybatis/sqlMapConfig.xml
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: com.qcby.springboot.domain