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在加载时的优先级以及是否在容器中加载该servlet
Tomcat依次执行的是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 {
@Autowired
private 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 {
//创建JavaBean
User 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 {
//创建JavaBean
User user = new User();
user.setUsername("haohao");
user.setAge(20);
//直接返回User对象
return user;
}
进一步优化,可以使用@RestController替代@Controller和@ResponseBody,@RestController内部具备的这两个 注解的功能
@RestController
public class ResponseController2 {
@GetMapping("/ajax/resp1")
// @ResponseBody
public String resp1(){
return "{\"username\":\"haohao\",\"age\":18}";
}
@GetMapping("/ajax/resp2")
// @ResponseBody
public String resp2() throws JsonProcessingException {
//创建JavaBean
User 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 {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Controller方法执行之前...");
return true; // 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Controller方法执行之后...");
}
@Override
public 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>
*/
@Bean
public 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 放入容器
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = this.createRequestMappingHandlerMapping();
// 中间省略
return mapping;
}
// 将 RequestMappingHandlerAdapter 放入容器
@Bean
public 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>
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 创建拦截器对象,进行注册
// Interceptor 的执行顺序也取决于添加顺序
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
}
// 替代 <mvc:default-servlet-handler/>
@Override
public 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>
*/
@Bean
public 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在加载时的优先级以及是否在容器中加载该servlet
Tomcat依次执行的是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 {
// 配置简单的异常处理器类
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
// 不管是什么异常,统一的响应一个友好页面
// simpleMappingExcepti
onResolver.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
*/
@Override
public 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
*/
@Override
public 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 zero
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/error1.html");
return modelAndView;
}
@ExceptionHandler(IOException.class)
@ResponseBody
public 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三个解析器