Javaweb
SpringMVC
Spring MVC是Spring框架的一个模块,专门用于构建Web应用程序的模型-视图-控制器(MVC)架构。它通过清晰的分离关注点,简化了Web应用各部分的开发。Spring MVC提供了强大的绑定机制,能够将请求参数绑定到控制器方法的参数上,支持灵活的验证和数据转换。它还内置了对RESTful API的支持,使得开发者可以轻松构建REST风格的Web服务。Spring MVC的分发器DispatcherServlet负责将请求路由到相应的控制器,而视图解析器则负责渲染响应的视图。此外,Spring MVC与Spring的其他模块如Spring Security和Spring Data无缝集成,提供了全面的安全、数据访问和事务管理功能,使得构建健壮、可维护的Web应用程序变得更加容易。
导入Spring整合SpringMVC的坐标
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.7</version>
</dependency>
创建Spring的配置文件applicationContext.xml,配置Spring包扫描
<context:component-scan base-package="com.mem.service"/>
在web.xml中配置SpringMVC的前端控制器ServletDispatcher
<!-- 创建Servlet WebApplicationContext容器的配置 -->
<servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><!--加载Spring MVC的配置文件--><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><!--可以设置该servlet在加载时的优先级以及是否在容器中加载该servletTomcat依次执行的是DispatcherServlet中的静态代码块,构造方法,init()方法--><load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
这里的/表示这是一个根路径模式,意味着DispatcherServlet将映射到应用的根路径,并且可以处理进入应用的所有HTTP请求。
编写一个控制器Controller,配置映射信息,并交给SpringMVC容器管理
@Controller//交给spring容器进行管理
public class QuickController {@RequestMapping("/show")//配置映射路径public void show(){System.out.println("show ...");}
}
控制台正常打印show …
报错原因:show()方法应该返回视图名字
改进: 将controller层中的show()方法返回值改为String,并添加上相应的页面
@Controller
public class QuickController {@RequestMapping("/show")public String show(){System.out.println("show ...");return "/show.jsp";}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title></title>
</head>
<body><h1>show</h1>
</body>
</html>
Controller中访问容器中的Bean
创建service层
public interface QuickService {
}@Service
public class QuickServiceImpl implements QuickService {
}
创建Spring的配置文件applicationContext.xml,配置Spring包扫描
<!--组件扫描-->
<context:component-scan base-package="com.mem.service"/>
在web.xml中配置ContextLoadListener及初始参数
<!-- 创建Root WebApplicationContext容器的配置 -->
<!--加载Spring的配置文件-->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置ContextLoaderListener(官方提供的)-->
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在Controller层注入QuickService对象
@Controller
public class QuickController {@Autowiredprivate QuickService quickService;@RequestMapping("/show")public String show(){System.out.println("show ...");System.out.println("quickService:"+quickService);return "/show.jsp";}
}
测试:控制台打印出quickService的地址quickService:com.mem.service.impl.QuickServiceImpl@5f87226c
Spring笔记(四)(黑马)(web层解决方案-SpringMVC)
SpringMVC关键组件浅析
SpringMVC的默认组件,SpringMVC 在前端控制器 DispatcherServlet加载时,就会进行初始化操作,在进行初始化时,就会加载SpringMVC默认指定的一些组件,这些默认组件配置在 DispatcherServlet.properties 文件中,该文件存在与spring-webmvc-5.3.7.jar包下的 org\springframework\web\servlet\DispatcherServlet.properties
这些默认的组件是在DispatcherServlet中进行初始化加载的,在DispatcherServlet中存在集合存储着这些组件, SpringMVC的默认组件会在 DispatcherServlet 中进行维护,但是并没有存储在与SpringMVC的容器中
public class DispatcherServlet extends FrameworkServlet {//存储处理器映射器private List<HandlerMapping> handlerMappings;//存储处理器适配器private List<HandlerAdapter> handlerAdapters;//存储视图解析器private List<ViewResolver> viewResolvers;// ... 省略其他代码 ...protected void initStrategies(ApplicationContext context) {this.initMultipartResolver(context);this.initLocaleResolver(context);this.initThemeResolver(context);this.initHandlerMappings(context); // 以这个为例this.initHandlerAdapters(context);this.initHandlerExceptionResolvers(context);this.initRequestToViewNameTranslator(context);this.initViewResolvers(context);this.initFlashMapManager(context);}private void initHandlerMappings(ApplicationContext context) {// 获取DispatcherServlet.properties文件中的三个类this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);}
}
配置组件代替默认组件,如果不想使用默认组件,可以将替代方案使用Spring Bean的方式进行配置,例如,在 spring-mvc.xml中配置RequestMappingHandlerMapping
<!--使用自定义的HandlerMapping,替代默认的HandlerMapping-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
当我们在Spring容器中配置了HandlerMapping,则就不会在加载默认的HandlerMapping策略了,原理比较简单, DispatcherServlet 在进行HandlerMapping初始化时,先从SpringMVC容器中找是否存在HandlerMapping,如果 存在直接取出容器中的HandlerMapping,在存储到 DispatcherServlet 中的handlerMappings集合中去。
Spring MVC 的请求处理
@RequestMapping注解,主要使用在控制器的方法上,用于标识客户端访问资源路径,常用的属性有value、path 、method、headers、params等。当@RequestMapping只有一个访问路径需要指定时,使用value属性、path属 性或省略value和path,当有多个属性时,value和path不能省略
@RequestMapping(value = "/show")//使用value属性指定一个访问路径
public String show(){}
@RequestMapping(value = {"/show","/haohao","/abc"})//使用value属性指定多个访问路径
public String show(){}
@RequestMapping(path = "/show")//使用path属性指定一个访问路径
public String show(){}
@RequestMapping(path = {"/show","/haohao","/abc"})//使用path属性指定多个访问路径
public String show(){}
@RequestMapping("/show")//如果只设置访问路径时,value和path可以省略
public String show(){}
@RequestMapping({"/show","/haohao","/abc"})
public String show(){}
当@RequestMapping 需要限定访问方式时,可以通过method属性设置
//请求地址是/show,且请求方式必须是POST才能匹配成功
@RequestMapping(value = "/show",method = RequestMethod.POST)
public String show(){}
@GetMapping
当请求方式是GET时,我们可以使用@GetMapping替代@RequestMapping
@PostMapping
当请求方式是POST时,我们可以使用@PostMapping替代@RequestMapping
@RequestMapping 在类上使用,@RequestMapping 、@GetMapping、@PostMapping还可以使用在 Controller类上,使用在类上后,该类所有方法都公用该@RequestMapping设置的属性,访问路径则为类上的映射 地址+方法上的映射地址,例如:
@Controller
@RequestMapping("/xxx")
public class UserController implements ApplicationContextAware, ServletContextAware {@GetMapping("/aaa")public ModelAndView aaa(HttpServletResponse response) throws IOException, ModelAndViewDefiningException {return null;}
}
请求数据的接收
客户端传递多个同名参数时,也可以使用单列集合接收,但是需要使用@RequestParam告知框架传递的参数是要同名设置的,不是对象属性设置的
收实体JavaBean属性数据
接收实体JavaBean属性数据,单个JavaBean数据:提交的参数名称只要与Java的属性名一致,就可以进行自动封装
public class User {private String username;private Integer age;private String[] hobbies;private Date birthday;private Address address;//... 省略get和set方法 ...
}
public class Address {private String city;private String area;
}@GetMapping("/show")
public String show(User user){System.out.println(user);return "/index.jsp";
}
接收Json数据格式数据
接收Json数据格式数据,Json数据都是以请求体的方式提交的,且不是原始的键值对格式的,所以我们要使用 @RequestBody注解整体接收该数据。
@PostMapping("/show")
public String show((@RequestBody String body){System.out.println(body);return "/index.jsp";
}
使用Rest风格:
接收Restful风格数据,Restful请求数据一般会在URL地址上携带,可以使用注解 @PathVariable(占位符参数名称)
http://localhost:8080/user/100
@PostMapping("/user/{id}")
public String findUserById(@PathVariable("id") Integer id){System.out.println(id);return "/index.jsp";
}
请求URL资源地址包含多个参数情况:http://localhost:8080/user/haohao/18
@PostMapping("/user/{username}/{age}")
public String findUserByUsernameAndAge(@PathVariable("username") String username,@PathVariable("age") Integer age){System.out.println(username+"=="+age);return "/index.jsp";
}
服务器端,由于映射器适配器需要文件上传解析器,而该解析器默认未被注册,所以手动注册
<!--配置文件上传解析器,注意:id的名字是固定写法-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8"/><!--文件的编码格式 默认是ISO8859-1--><property name="maxUploadSizePerFile" value="1048576"/><!--上传的每个文件限制的大小 单位字节--><property name="maxUploadSize" value="3145728"/><!--上传文件的总大小--><property name="maxInMemorySize" value="1048576"/><!--上传文件的缓存大小-->
</bean>
而CommonsMultipartResolver底层使用的Apache的是Common-fileuplad等工具API进行的文件上传
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version>
</dependency>
使用MultipartFile类型接收上传文件
@PostMapping("/fileUpload")
public String fileUpload(@RequestBody MultipartFile myFile) throws IOException {System.out.println(myFile);//获得上传的文件的流对象InputStream inputStream = myFile.getInputStream();//使用commons-io存储到C:\haohao\abc.txt位置FileOutputStream outputStream = new FileOutputStream("C:\\Users\\haohao\\"+myFile.getOriginalFilename());IOUtils.copy(inputStream,outputStream);//关闭资源inputStream.close();outputStream.close();
return "/index.jsp";
}
若接收多个文件,变为数组即可
Javaweb常用对象的获取
获得Javaweb常见原生对象,有时在我们的Controller方法中需要用到Javaweb的原生对象,例如:Request、 Response等,我们只需要将需要的对象以形参的形式写在方法上,SpringMVC框架在调用Controller方法时,会自动传递实参:
@GetMapping("/javawebObject")
public String javawebObject(HttpServletRequest request, HttpServletResponse response,
HttpSession session){System.out.println(request);System.out.println(response);System.out.println(session);return "/index.jsp";
}
请求静态资源
静态资源请求失效的原因,当DispatcherServlet的映射路径配置为 / 的时候,那么就覆盖的Tomcat容器默认的缺省 Servlet,在Tomcat的config目录下有一个web.xml 是对所有的web项目的全局配置,其中有如下配置:
url-pattern配置为 / 的Servlet我们称其为缺省的Servlet,作用是:当其他Servlet都匹配不成功时,就找缺省的Servlet ,静态资源由于没有匹配成功的Servlet,所以会找缺省的DefaultServlet,该DefaultServlet具备二次去匹配静态资源的功能。但是我们配置DispatcherServlet后就将其覆盖掉了,而DispatcherServlet会将请求的静态资源的名称当成Controller的映射路径去匹配,即静态资源访问不成功了!
第一种方案:
在web.xml中,可以再次激活Tomcat的DefaultServlet,Servlet的url-pattern的匹配优先级是:精确匹配>目录匹配> 扩展名匹配>缺省匹配,所以可以指定某个目录下或某个扩展名的资源使用DefaultServlet进行解析:
注解驱动mvc:annotation-driven标签
静态资源配置的第二第三种方式我们可以正常访问静态资源了,但是Controller又无法访问了,报错404,即找不到对应的资源
又结合组件浅析知识点,一旦SpringMVC容器中存在 HandlerMapping 类型的组件时,前端控制器 DispatcherServlet在进行初始化时,就会从容器中获得HandlerMapping ,不在加载 dispatcherServlet.properties 中默认处理器映射器策略,那也就意味着RequestMappingHandlerMapping不会被加载到了。
RequestMappingHandlerMapping的作用是:解析@RequestMapping(“”)注解的,最后容器中没有RequestMappingHandlerMapping的bean 也就没办法识别里面的内容了
解决方法:
手动将RequestMappingHandlerMapping也注册到SpringMVC容器中就可以了,这样DispatcherServlet在进行初始化时,就会从容器中同时获得RequestMappingHandlerMapping存储到DispatcherServlet中名为 handlerMappings的List集合中,对@RequestMapping 注解进行解析。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
根据上面的讲解,可以总结一下,要想使用@RequestMapping正常映射到资源方法,同时静态资源还能正常访问, 还可以将请求json格式字符串和JavaBean之间自由转换,我们就需要在spring-mvc.xml中进行如下配置:
<!--使用RequestMappingHandlerAdapter,内部添加messageConverters: 实现遇到json格式自动转换为对象格式-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="messageConverters"><list><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/></list></property>
</bean>
PS: mvc:annotation-driven/ 标签在不同的版本中,帮我们注册的组件不同。
Spring 3.0.X 版本注册是 DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter,由于框架的发展,从Spring 3.1.X 开始注册组件变为 RequestMappingHandlerMapping和RequestMappingHandlerAdapter
Spring MVC 的响应处理
响应模型数据,响应模型数据本质也是转发,在转发时可以准备模型数据
@RequestMapping("/resp3")
public ModelAndView resp3(ModelAndView modelAndView){// ModelAndView封装模型数据和视图名// 设置模型数据User user = new User();user.setUsername("haohao");user.setAge(18);modelAndView.addObject("user",user);// 设置试图名,在页面中展示模型数据modelAndView.setViewName("/show.jsp");return modelAndView;
}
直接回写数据,直接通过方法的返回值返回给客户端的字符串,但是SpringMVC默认的方法返回值是视图,可以通过 @ResponseBody 注解显示的告知此处的返回值不要进行视图处理,是要以响应体的方式处理的
@RequestMapping("/resp4")
@ResponseBody
public String resp4(){return "hello world!";
}
前后端分类异步业务数据响应
回写Json格式的字符串,即将直接拼接Json格式的字符串或使用工具将JavaBean转换成Json格式的字符串回写
@GetMapping("/ajax/resp1")
@ResponseBody
public String resp1(){return "{\"username\":\"haohao\",\"age\":18}";
}@GetMapping("/ajax/resp2")
@ResponseBody
public String resp2() throws JsonProcessingException {//创建JavaBeanUser user = new User();user.setUsername("haohao");user.setAge(19);//使用Jackson转换成json格式的字符串String json = new ObjectMapper().writeValueAsString(user);return json;
}
在讲解SringMVC接收请求数据时,客户端提交的Json格式的字符串,也是使用Jackson进行的手动转换成JavaBean ,可以当我们使用了@RequestBody时,直接用JavaBean就接收了Json格式的数据,原理其实就是SpringMVC底层 帮我们做了转换,此处@ResponseBody也可以将JavaBean自动给我们转换成Json格式字符串回响应
@GetMapping("/ajax/resp3")
@ResponseBody
public User resp3() throws JsonProcessingException {//创建JavaBeanUser user = new User();user.setUsername("haohao");user.setAge(20);//直接返回User对象return user;
}
进一步优化,可以使用@RestController替代@Controller和@ResponseBody,@RestController内部具备的这两个 注解的功能
@RestController
public class ResponseController2 {@GetMapping("/ajax/resp1")// @ResponseBodypublic String resp1(){return "{\"username\":\"haohao\",\"age\":18}";}@GetMapping("/ajax/resp2")// @ResponseBodypublic String resp2() throws JsonProcessingException {//创建JavaBeanUser user = new User();user.setUsername("haohao");user.setAge(19);//使用Jackson转换成json格式的字符串String json = new ObjectMapper().writeValueAsString(user);return json;}
}
Spring MVC 的拦截器
实现了HandlerInterceptor接口,且被Spring管理的Bean都是拦截器,接口定义如下:
public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
}
拦截器快速入门
public class MyInterceptor1 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Controller方法执行之前...");return true; // 放行}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Controller方法执行之后...");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("渲染视图结束,整个流程完毕...");}
}
在spring-mvc.xml中, 配置Interceptor
<!--配置拦截器-->
<mvc:interceptors><mvc:interceptor><!--配置对哪些资源进行拦截操作--><mvc:mapping path="/**"/><bean class="com.mem.interceptor.MyInterceptor1"></bean></mvc:interceptor>
</mvc:interceptors>
controller层,业务代码
// 测试拦截器
@RequestMapping("/interceptor_req")
public String interceptor_req(){System.out.println("interceptor_req ...");return "/show.jsp";
}
Controller方法执行之前...
interceptor_req ...
Controller方法执行之后...
渲染视图结束,整个流程完毕...
拦截器执行顺序
Spring MVC 的全注解开发
跟之前全注解开发思路一致, xml配置文件使用核心配置类替代,xml中的标签使用对应的注解替代
<!--1. 组件扫描 -->
<!--组件扫描web层-->
<context:component-scan base-package="com.mem.controller"/><!--2. 非自定义的Bean -->
<!--配置文件上传解析器,注意:id的名字是固定写法-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8"/><!--文件的编码格式 默认是ISO8859-1--><property name="maxUploadSizePerFile" value="1048576"/><!--上传的每个文件限制的大小 单位字节--><property name="maxUploadSize" value="3145728"/><!--上传文件的总大小--><property name="maxInMemorySize" value="1048576"/><!--上传文件的缓存大小-->
</bean><!--3. 非Bean的配置 -->
<!--访问静态资源的方式3:底层注册一个DefaultServletHttpRequestHandler 来处理静态资源-->
<mvc:default-servlet-handler/>
<!--mvc的注解驱动-->
<mvc:annotation-driven/>
<!--配置拦截器-->
<mvc:interceptors><mvc:interceptor><!--配置对哪些资源进行拦截操作--><mvc:mapping path="/**"/><bean class="com.mem.interceptor.MyInterceptor1"></bean></mvc:interceptor>
</mvc:interceptors>
第一步,第二步,可以利用之前所学的spring的配置类来搞定
组件扫描,可以通过@ComponentScan注解完成;
文件上传解析器multipartResolver可以通过非自定义Bean的注解配置方式,即@Bean注解完成
@Configuration
// <context:component-scan base-package="com.mem.controller"/>
@ComponentScan("com.mem.controller")
public class SpringMVCConfig {/*** <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">* <property name="defaultEncoding" value="UTF-8"/><!--文件的编码格式 默认是ISO8859-1-->* <property name="maxUploadSizePerFile" value="1048576"/><!--上传的每个文件限制的大小 单位字节-->* <property name="maxUploadSize" value="3145728"/><!--上传文件的总大小-->* <property name="maxInMemorySize" value="1048576"/><!--上传文件的缓存大小-->* </bean>*/@Beanpublic CommonsMultipartResolver multipartResolver(){CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();multipartResolver.setDefaultEncoding("UTF-8");multipartResolver.setMaxUploadSizePerFile(1048576);multipartResolver.setMaxUploadSize(3145728);multipartResolver.setMaxInMemorySize(1048576);return multipartResolver;}
}
第三步,非Bean的配置(mvc:default-servlet-handler/、mvc:annotation-driven/、mvc:interceptors) 该怎么办呢?
SpringMVC 提供了一个注解@EnableWebMvc,我们看一下源码,内部通过@Import导入了DelegatingWebMvcConfiguration类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();// 从容器中注入 WebMvcConfigurer 类型的Bean@Autowired(required = false)public void setConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers);}}
}
首先先看下父类WebMvcConfigurationSupport:
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {// 将 RequestMappingHandlerMapping 放入容器@Beanpublic RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {RequestMappingHandlerMapping mapping = this.createRequestMappingHandlerMapping();// 中间省略return mapping;}// 将 RequestMappingHandlerAdapter 放入容器@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcValidator") Validator validator) {RequestMappingHandlerAdapter adapter = this.createRequestMappingHandlerAdapter();return adapter;}
}
这一步的效果等同于mvc:annotation-driven/注解驱动
实现:
创建MyWebMvcConfigurer实现WebMvcConfigurer接口,实现addInterceptors 和 configureDefaultServletHandling方法
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {// 替代 <mvc:interceptors>@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 创建拦截器对象,进行注册// Interceptor 的执行顺序也取决于添加顺序registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");}// 替代 <mvc:default-servlet-handler/>@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {// 开启DefaultServlet,就可以处理静态资源了configurer.enable();}
}
最后,在SpringMVC核心配置类上添加@EnableWebMvc注解
@Configuration
// <context:component-scan base-package="com.mem.controller"/>
@ComponentScan("com.mem.controller")
@EnableWebMvc
public class SpringMVCConfig {/*** <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">* <property name="defaultEncoding" value="UTF-8"/><!--文件的编码格式 默认是ISO8859-1-->* <property name="maxUploadSizePerFile" value="1048576"/><!--上传的每个文件限制的大小 单位字节-->* <property name="maxUploadSize" value="3145728"/><!--上传文件的总大小-->* <property name="maxInMemorySize" value="1048576"/><!--上传文件的缓存大小-->* </bean>*/@Beanpublic CommonsMultipartResolver multipartResolver(){CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();multipartResolver.setDefaultEncoding("UTF-8");multipartResolver.setMaxUploadSizePerFile(1048576);multipartResolver.setMaxUploadSize(3145728);multipartResolver.setMaxInMemorySize(1048576);return multipartResolver;}
}
DispatcherServlet 加载核心配置类
现在是使用SpringMVCConfig核心配置类替代了spring-mvc.xml,怎么加载呢?
<!-- 创建Servlet WebApplicationContext容器的配置 -->
<servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--加载Spring MVC的核心配置文件--><!-- <init-param>--><!-- <param-name>contextConfigLocation</param-name>--><!-- <param-value>classpath:spring-mvc.xml</param-value>--><!-- </init-param>--><!--加载Spring MVC的核心配置类--><init-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value></init-param><init-param><param-name>contextConfigLocation</param-name><param-value>com.mem.config.SpringMVCConfig</param-value></init-param><!--可以设置该servlet在加载时的优先级以及是否在容器中加载该servletTomcat依次执行的是DispatcherServlet中的静态代码块,构造方法,init()方法--><load-on-startup>2</load-on-startup>
</servlet>
方法2: 参照Spring的 ContextLoaderListener加载核心配置类的做法,定义了一个AnnotationConfigWebApplicationContext,通过 代码注册核心配置类
Spring MVC 的组件原理刨析
前端控制初始化
结论:SpringMVC 的ApplicationContext容器创建时机,Servlet 规范的 init(ServletConfig config) 方法经过子类重写 ,最终会调用 FrameworkServlet 抽象类的initWebApplicationContext() 方法,该方法中最终获得 一个根 Spring容器(Spring产生的),一个子Spring容器(SpringMVC产生的)。
Spring MVC 的异常处理机制
SpringMVC的异常处理方式
初始化:
新建一个异常测试Controller:
@RestController
public class ExceptionController {/*** 模拟运行时异常* @return*/@RequestMapping("/exception1")public String exceptionMethod1(){int i = 1/0;return "Hello Exception";}/*** 模拟编译异常* @return*/@RequestMapping("/exception2")public String exceptionMethod2() throws FileNotFoundException {FileInputStream inputStream = new FileInputStream("C:/xx/xx/xx.xx");return "Hello Exception";}
}
改善1:加上简单异常处理器(SimpleMappingExceptionResolver),对不同的异常进行不同的跳转友好页面,操作如下
在配置类上加一个SimpleMappingExceptionResolver类型的Bean
@Configuration
@ComponentScan("com.mem.controller")
@EnableWebMvc
public class SpringMVCConfig {// 配置简单的异常处理器类@Beanpublic SimpleMappingExceptionResolver simpleMappingExceptionResolver(){SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();// 不管是什么异常,统一的响应一个友好页面// simpleMappingExceptionResolver.setDefaultErrorView("/error1.html");// 区分异常类型,根据不同的异常类型,跳转不同的视图Properties properties = new Properties();// 键值对,key:异常的全限定名,value:跳转的视图名properties.setProperty("java.lang.RuntimeException","/error1.html");properties.setProperty("java.io.FileNotFoundException","/error2.html");simpleMappingExceptionResolver.setExceptionMappings(properties);return simpleMappingExceptionResolver;}
}
添加错误页面
改善2:自定义异常处理器,实现HandlerExceptionResolver接口,操作如下:
添加自定义异常处理器类
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {/**** @param request 请求* @param response 响应* @param handler Controller层的方法的封装* @param e 异常,可以用于判断* @return*/@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {System.out.println("request:"+request);System.out.println("response:"+response);System.out.println("handler:"+handler);System.out.println("e:"+e);// 1. 可以简单的响应一个友好的提示页面ModelAndView modelAndView = new ModelAndView();if(e instanceof RuntimeException ){modelAndView.setViewName("/error1.html");}else{modelAndView.setViewName("/error2.html");}return modelAndView;}
}
自定义异常处理器还可以以json形式返回:
修改MyHandlerExceptionResolver的resolveException方法:
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {/**** @param request 请求* @param response 响应* @param handler Controller层的方法的封装* @param e 异常,可以用于判断* @return*/@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {System.out.println("request:"+request);System.out.println("response:"+response);System.out.println("handler:"+handler);System.out.println("e:"+e);// 1. 可以简单的响应一个友好的提示页面// ModelAndView modelAndView = new ModelAndView();// if(e instanceof RuntimeException ){// modelAndView.setViewName("/error1.html");// }else{// modelAndView.setViewName("/error2.html");// }// 2. 前后端分离开发,响应json格式的字符串 {"code": 200,"message":"","data":{"username":"haohao","age":18}}String resultJson = "{\"code\": 500,\"message\":\"异常\",\"data\":{\"username\":\"haohao\",\"age\":18}}";try {response.getWriter().write(resultJson);} catch (IOException ex) {ex.printStackTrace();}return null;}
}
改善3:使用注解的方式,更加灵活(常用)
新建类(ExceptionByAnno):
@ControllerAdvice
public class ExceptionByAnno {@ExceptionHandler(RuntimeException.class)public ModelAndView RuntimeExceptionResolverMethod(Exception exception){System.out.println("exception:"+exception); // exception:java.lang.ArithmeticException: / by zeroModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("/error1.html");return modelAndView;}@ExceptionHandler(IOException.class)@ResponseBodypublic Result IOExceptionResolverMethod(Exception exception){System.out.println("exception:"+exception); // exception:java.io.FileNotFoundException: C:\xx\xx\xx.xx (系统找不到指定的路径。)Result result = new Result(500,"","");return result;}@ExceptionHandler(FileNotFoundException.class)public ModelAndView FileNotFoundExceptionResolverMethod(Exception exception){System.out.println("exception:"+exception); // exception:java.io.FileNotFoundException: C:\xx\xx\xx.xx (系统找不到指定的路径。)ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("/error2.html");return modelAndView;}
}
SpringMVC 常用的异常解析器
HandlerExceptionResolverComposite 是一个组合体,内部包含了ExceptionHandlerExceptionResolver + DefaultHandlerExceptionResolver + ResponseStatusExceptionResolver三个解析器