SpringBoot
静态资源
1、静态资源目录
默认情况下,Spring Boot 提供来自类路径中名为/static
(或/public
或/resources
或/META-INF/resources
)的目录或从ServletContext
. 它使用ResourceHttpRequestHandler
来自 Spring MVC 的,以便您可以通过添加自己WebMvcConfigurer
类的addResourceHandlers
方法并覆盖该方法来修改该行为。
在独立的 Web 应用程序中,容器中的默认 servlet 也被启用并充当回退,ServletContext
如果 Spring 决定不处理它,则从根目录提供内容。大多数情况下,这不会发生(除非您修改默认的 MVC 配置),因为 Spring 始终可以通过DispatcherServlet
.
先走controller查找请求路径,如果未找到对应的controller,则查找静态资源
静态资源前缀:spring.mvc.static-path-pattern=/resources/**
默认情况下,资源映射在 上/**
(默认无前缀),但您可以使用spring.mvc.static-path-pattern
属性对其进行调整。例如,将所有资源重新分配到/resources/**
可以实现如下:spring.mvc.static-path-pattern=/resources/**
您还可以使用该spring.web.resources.static-locations
属性自定义静态资源位置(用目录位置列表替换默认值)。根 Servlet 上下文路径"/"
也自动添加为一个位置。
除了前面提到的“标准”静态资源位置之外,还为Webjars 内容制作了一个特殊情况。/webjars/**
如果它们以 Webjars 格式打包,则任何带有路径的资源都从 jar 文件提供。
spring:
mvc:
static-path-pattern: /res/** #给静态资源访问路径添加前缀:http://localhost:8080/res/login-logo.png 添加此配置则会导致欢迎页失效,如果配置此项,则需使用模板化欢迎页面,即编写controller来访问
web:
resources:
static-locations: classpath:/res/ #自定义静态资源文件存放的位置,测试发现,虽指定了静态资源文件路径,但是/META-INF/resources/下的文件依旧能够访问
2、欢迎页
Spring Boot 支持静态和模板化欢迎页面。它首先index.html
在配置的静态内容位置中查找文件。如果没有找到,它就会寻找一个index
(编写一个controller能处理index请求)模板。如果找到其中之一,它会自动用作应用程序的欢迎页面。
所有的请求映射都在HandlerMapping中
- SpringBoot自动配置欢迎页的WelcomePageHandlerMapping,访问"/"就能访问到index.html
- SpringBoot自动配置了默认的RequestMappingHandlerMapping
- 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息
- 如果有就找到对应的这个handler
- 如果没有就是下一个handlermapping
- 我们如果需要自定义一些handlermapping,就可以自己给容器中放handlermapping
请求参数
@RestController
public class ParameterTestController {
@GetMapping("/car/{id}/owner/{username}")
public Map<String, Object> getCar(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String, String> pv,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String, String> header,
@RequestParam("age") Integer age,
@RequestParam("interests") List<String> interests,
@RequestParam MultiValueMap<String, Object> params){
Map<String, Object> map = new HashMap<>();
map.put("id",id);
map.put("name",username);
/**
* @PathVariable
* If the method parameter is {@link java.util.Map Map<String, String>}
* then the map is populated with all path variable names and values.
*/
map.put("pv",pv);
/**
* @RequestHeader
* If the method parameter is {@link java.util.Map Map<String, String>},
* {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>},
* or {@link org.springframework.http.HttpHeaders HttpHeaders} then the map is
* populated with all header names and values.
*/
map.put("userAgent",userAgent);
map.put("header",header);
/**
* @RequestParam
* If the method parameter is {@link java.util.Map Map<String, String>} or
* {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}
* and a parameter name is not specified, then the map parameter is populated
* with all request parameter names and values.
* http://localhost:8080/car/2/owner/djx?age=20&interests=computer&interests=BBQ&interests=game
* 使用Map不能接收多选框的多个内容,推荐使用MultiValueMap,会将参数封装成LIst放入MultiValueMap中
* MultiValueMap封装的内容:"params":{"age":["20"],"interests":["computer","BBQ","game"]}
*/
map.put("age",age);
map.put("interests",interests);
map.put("params",params);
/**
* @CookieValue
* The method parameter may be declared as type {@link javax.servlet.http.Cookie}
* or as cookie value type (String, int, etc.).
* Note that with spring-webmvc 5.3.x and earlier, the cookie value is URL
* decoded. This will be changed in 6.0 but in the meantime, applications can
* also declare parameters of type {@link javax.servlet.http.Cookie} to access
* the raw value.
*通过此注解可以获取Cookie的值,可通过String或Cookie类型参数获取
*/
/**
* @RequestBody
* 当发送的请求是POST时,可以使用该注解获取请求体的内容
* @RequestBody String content
*/
/**
* @RequestAttribute
* 当进行请求转发时,可以使用此注解获取请求转发携带的数据
* @RequestAttribute("msg") String msg
*/
/**
* @MatrixVariable
* SpringBoot默认禁用了矩阵变量的功能
* 需要手动开启:
* 原理:对于路径的处理--》UrlPathHelper进行解析。
* 其中removeSemicolonContent方法会移除分号后面的内容
* 只要编写一个配置类,并实现WebMvcConfigurer接口,再重写configurePathMatch方法
* @Configuration
* public class WebConfig implements WebMvcConfigurer {
* @Override
* public void configurePathMatch(PathMatchConfigurer configurer) {
* UrlPathHelper urlPathHelper = new UrlPathHelper();
* urlPathHelper.setRemoveSemicolonContent(false);
* configurer.setUrlPathHelper(urlPathHelper);
* }
* }
* 或在配置类中
* @Bean
* public WebMvcConfigurer webMvcConfigurer(){
* return new WebMvcConfigurer() {
* @Override
* public void configurePathMatch(PathMatchConfigurer configurer) {
* UrlPathHelper urlPathHelper = new UrlPathHelper();
* urlPathHelper.setRemoveSemicolonContent(false);
* configurer.setUrlPathHelper(urlPathHelper);
* }
* };
* }
*
* 通过上面的方式进行矩阵变量的开启
*
* http://localhost:8080/car/{path;low=34;brand=byd,audi,yd} 矩阵变量
*
* /boss/1;age=20/2;age=18
* @GetMapping("/boss/{bossId}/{empId}")
* @MatrixVariable(pathVar = "bossId",value = "age") Integer bossAge
* @MatrixVariable(pathVar = "empId",value = "age") Integer empAge
*
* 当cookie被用户禁用后,可以使用矩阵变量的方式将cookie携带在url中
* /cars/sell/;low=34;brand=byd,audi,yd
* @GetMapping("/cars/sell")
* public Map carsSell(@MatrixVariable("low") Integer low, @MatrixVariable("brand" List<String> brand))
*
*/
return map;
}
}
参数解析器:HandlerMethodArgumentResolver
决定了@RequestMapping方法的参数的类型
Servlet API :ServletRequestMethodArgumentResolver
WebRequest
ServletRequest
MultipartRequest
HttpSession
Principal
InputStream
Reader
HttpMethod
Locale
TimeZone
ZoneId
PushBuilder
以上方法可以在参数中直接使用
复杂参数
Map、Model(map、model里面的数据会被放在request的请求域中 相当于 request.setAttribute)、Errors/BindingResult、RedirectAttributes(重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
自定义类型参数 封装POJO:ServletModelAttributeMethodProcessor
底层利用反射,WebDataBinder(数据绑定器)将请求参数的值绑定到指定的JavaBean里面,创建一个与参数中对应的POJO中的类,利用Converter的实现类实现了将http中携带的文本参数转换为POJO类中对应的属性的类型,并进行正确的属性赋值,这样便实现了从http文本到实体类的封装。
自定义转换器
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
//将前端传来的字符串进行格式化
//阿猫,3
if(StringUtils.hasLength(source)){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(Integer.valueOf(split[1]));
return pet;
}
return null;
}
});
}
};
}
响应JSON
1、返回值解析器:HandlerMethodReturnValueHandler
决定了支持的返回值类型
ModelAndView
Model
View
PesponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
标注了@ModelAttribute
标注了@ResponseBody:RequestResponseBodyMethodProcessor(返回值处理器)通过它来处理返回值标注了@ResponseBody注解的方法
再通过消息转换器(HttpMessageConverter),判断能给浏览器返回的数据类型
内容协商
浏览器(请求头)与服务器(根据请求头内容决定返回给浏览器的数据类型)之间的消息交互
/**
* Whether a request parameter ("format" by default) should be used to determine
* the requested media type.
* 是否应使用请求参数来确定请求的媒体类型。
* http://localhost:8080/test/person?format=json
* 上述请求则表明,向服务器请求的是json数据
* 默认只支持json和xml
*/
private boolean favorParameter = false;
application.yaml配置
spring:
mvc:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
自定义MessageConverter
实现多协议数据兼容。Json、xml
1、@ResponseBody响应数据出去,调用RequestResponseBodyMethodProcessor处理
2、Processor处理方法返回值。通过MessageConverter处理
3、所有MessageConverter合起来可以支持各种媒体类型数据的操作(读、写)
3、内容协商找到最终的MessageConverter
自定义一个MessageConverter
public class MyConverter implements HttpMessageConverter<Pet> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Pet.class);
}
/**
* 服务器要统计所有MessageConverter都能写出那些内容类型
* @return
*/
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-djx");
}
@Override
public Pet read(Class<? extends Pet> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Pet pet, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
String data = pet.getName()+";"+pet.getAge();
OutputStream body = outputMessage.getBody();
body.write(data.getBytes(StandardCharsets.UTF_8));
}
}
在配置类中
@Configuration
public class WebConfig{
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
//添加自定义的MessageConverter
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyConverter());
}
//自定义内容协商策略
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
HashMap<String, MediaType> map = new HashMap<>();
map.put("json",MediaType.APPLICATION_JSON);
map.put("xml",MediaType.APPLICATION_XML);
map.put("pet",MediaType.parseMediaType("application/x-djx"));
ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(map);
HeaderContentNegotiationStrategy headerStrategy = new HeaderContentNegotiationStrategy();
//即支持请求参数携带的媒体类型(上述定义的json、xml、pet),又支持请求头自带的媒体类型
configurer.strategies(Arrays.asList(parameterStrategy,headerStrategy));
}
};
}
}
最终得到自定义的解析数据,以分号";"分割每一个数据
文件上传
1、在配置文件中配置上传文件的大小限制
spring:
servlet:
multipart:
# 配置文件上传大小限制
max-file-size: 1000MB
max-request-size: 10000MB
2、编写控制器
@Slf4j
@Controller
public class FileController {
@RequestMapping("/")
public String index(){
return "index";
}
@RequestMapping("/upload")
public String file(@RequestParam(value = "username") String username,
@RequestPart("file") MultipartFile file,
@RequestPart("files") MultipartFile[] files,
Model model) throws IOException {
log.info("上传的信息:名字={},headerImg={},headerImg的ContentType={},photos={}",username,file.getOriginalFilename(),file.getContentType(),files.length);
//指定上传路径
String path = "C:\\JAVA\\upload";
//创建相应目录
File uploadDir = new File(path);
if (!uploadDir.exists()){
uploadDir.mkdirs();
}
//单文件上传
if (!file.isEmpty()){
//保存到文件服务器,OSS服务器
String filename = file.getOriginalFilename();
file.transferTo(new File(path+File.separator+filename));
}
//多文件上传
if (files.length>0){
for (MultipartFile f : files) {
if (!f.isEmpty()){
String filename = f.getOriginalFilename();
f.transferTo(new File(path+File.separator+filename));
}
}
}
model.addAttribute("msg",username+"上传成功!");
return "index";
}
}
错误页面
当出现错误后,SpringBoot提供/error
处理所有的错误映射
其会将错误信息封装成JSON响应给浏览器
自定义错误页面
只需将错误页面放在templates/error/
下并命名为5xx.html 404.html等
Web原生组件注入(Servlet、Filter、Listener)
1、使用Servlet API
-
在启动类上添加
@ServletComponentScan
注解 -
将三大组件统一放在servlet包下
-
@WebServlet(urlPatterns = "/my") public class Myservlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); resp.getWriter().write("你好"); } }
@WebFilter(urlPatterns = "/my") public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter工作了"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { Filter.super.destroy(); } }
@Slf4j @WebListener public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { log.info("监听到项目初始化完成"); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("监听到项目销毁完成"); } }
2、使用RegistrationBean(推荐使用)
将上面编写的三个类的注解去掉,在此类中注入
@Configuration
public class MyRegistionConfig {
@Bean
public ServletRegistrationBean servletRegistrationBean(){
Myservlet myservlet = new Myservlet();
return new ServletRegistrationBean(myservlet,"/my","/mytest");
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,servletRegistrationBean());
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(myFilter);
filterRegistrationBean.addUrlPatterns("my","/mytest");
// filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/mys"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean(){
MyServletContextListener myServletContextListener = new MyServletContextListener();
return new ServletListenerRegistrationBean(myServletContextListener);
}
}
阿里巴巴druid配置
spring:
datasource:
username: xxxxxx
password: xxxxxxxxxxxxx
url: jdbc:mysql://localhost:3306/mybatis-plus
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
stat-view-servlet:
enabled: true
login-password: xxxx
login-username: xxxx
reset-enable: true
web-stat-filter:
enabled: true
aop-patterns: com.djx
filters: stat,wall
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
wall:
enabled: true