二 Web开发
(接上文)
2.7 文件上传
- 页面
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file"><br>
<input type="submit" value="提交">
</form>
- java
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上传的信息:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
if(!headerImg.isEmpty()){
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}
- 自动配置原理
文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
- 自动配置好了
StandardServletMultipartResolver 【文件上传解析器】 - 原理步骤
- 1.请求进来使用文件上传解析器判断(
isMultipart )并封装(resolveMultipart ,返回MultipartHttpServletRequest )文件上传请求 - 2.参数解析器解析请求中的文件内容封装成
MultipartFile - 3.将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>
- 4.使用
FileCopyUtils 实现文件流的拷贝
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos)
2.8 错误处理 (TODO)
1、默认规则
- 默认情况下,Spring Boot提供/error处理所有错误的映射
- 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。
- 对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
2.9 Web原生组件注入(Servlet、Filter、Listener)
第一种方式: 使用Servlet API (推荐)
@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet组件都放在那里@WebServlet(urlPatterns = "/my") :效果:直接响应,没有经过Spring的拦截器?@WebFilter(urlPatterns={"/css/*","/images/*"}) @WebListener
第二种方式: 使用XXRegistrationBean
@Configuration(proxyBeanMethods = true)
public class ServletConfiguration {
@Bean
public ServletRegistrationBean<MyServlet> servletRegistrationBean() {
return new ServletRegistrationBean<>(new MyServlet(), "/myServlet2");
}
@Bean
public FilterRegistrationBean<MyFilter> filterFilterRegistrationBean() {
MyFilter myFilter = new MyFilter();
FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<MyFilter>();
bean.setFilter(myFilter);
bean.setUrlPatterns(Collections.singletonList("/myFilter2"));
return bean;
}
@Bean
public ServletListenerRegistrationBean<MyListener> servletListenerRegistrationBean() {
return new ServletListenerRegistrationBean<>(new MyListener());
}
}
扩展: DispatchServlet 如何注册进来
- 容器中自动配置了
DispatcherServlet 属性绑定到 WebMvcProperties ;对应的配置文件配置项是 spring.mvc 。 - 通过
ServletRegistrationBean<DispatcherServlet> 把 DispatcherServlet 配置进来。 - 默认映射的是 / 路径
Tomcat-Servlet 多个Servlet都能处理到同一层路径,精确匹配原则,比如 A: /my/ B: /my/1 因此, 当同时存在 /my 和 /my/1 的URL 时, /my1 会首先命中; 这就意味着 下图中的 /my 会由Tomcat处理, 而 不会经过 dispatcherServlet, 这会让 /my不能被Spring捕获,那些拦截器可能会不起作用
2.10 servlet web server
2.10.1 如何切换
- 默认支持的webServer
- Tomcat, Jetty, or Undertow
ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
2.10.2 嵌入式servlet web server启动原理
- SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat starter
- web应用会创建一个web版的ioc容器
ServletWebServerApplicationContext ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactory (Servlet 的web服务器工厂—> Servlet 的web服务器)- SpringBoot底层默认有很多的WebServer工厂:
TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory - 底层直接会有一个自动配置类:
ServletWebServerFactoryAutoConfiguration ServletWebServerFactoryAutoConfiguration 导入了ServletWebServerFactoryConfiguration (配置类)ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory TomcatServletWebServerFactory 创建出Tomcat服务器并启动;TomcatWebServer 的构造器拥有初始化方法initialize —this.tomcat.start() ;- 内嵌服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在)
2.10.3 如何定制自己的Servlet Web
- 实现
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
- 把配置文件的值和
ServletWebServerFactory 进行绑定 - 修改配置文件 server.xxx
- 直接自定义
ConfigurableServletWebServerFactory
xxxxxCustomizer :定制化器,可以改变xxxx的默认规则.
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
2.11 ServletWebServer 定制原理
2.11.1 定制化的常见方式
- 方式一:修改配置文件
- 方式二:
xxxxxCustomizer - 方式三: 编写自定义的配置类
xxxConfiguration + @Bean 替换、增加容器中默认组件;视图解析器 - Web应用 编写一个配置类实现
WebMvcConfigurer 即可定制化web功能 + @Bean 给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
@EnableWebMvc + WebMvcConfigurer ——@Bean 可以全面接管SpringMVC,所有规则全部自己重新配置;实现定制和扩展功能; 原理如下:
- 1、
WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页… - 2、一旦使用
@EnableWebMvc 会 @Import(DelegatingWebMvcConfiguration.class) - 3、
DelegatingWebMvcConfiguration 的只保证SpringMVC最基本的使用, 不会把静态资源的处理也加上. 它会:
- 把系统所有的
WebMvcConfigurer (不管是一个类多个@Bean, 还是多个类)拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效 - 自动配置了一些非常底层的组件,
RequestMappingHandlerMapping 这些组件依赖的组件都是从容器中获取 public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport - 4、WebMvcAutoConfiguration 里面的配置要能生效 必须
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) - 5、
@EnableWebMvc 会导致 WebMvcAutoConfiguration 不生效。
2.11.2 原理分析套路
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项
|