便利之处
springMVC在web项目中主要的作用就是对请求和响应的处理;
处理请求
原先我们需要获取前端发送的简单参数需要通过httpServletRequest.getParameter来获取
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Integer pageNumber = Integer.valueOf(req.getParameter("pageNumber"));Integer pageSize = Integer.valueOf(req.getParameter("pageSize"));String keyword = req.getParameter("keyword");ResponseResult<List<Owner>> result = ownerService.pages(pageNumber, pageSize, keyword);asyResponse(result, resp);}
对于复杂的数据类型比如json数据需要通过ObjectMapper对象进行获取
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ObjectMapper objectMapper = new ObjectMapper();Owner owner = objectMapper.readValue(req.getInputStream(), Owner.class);ResponseResult responseResult = ownerService.add(owner);asyResponse(responseResult, resp);}
以上原始的方法太过于复杂,springMVC提供了更为简单的方式。
简单的get请求可以直接和方法的形参直接映射,只需要形参名字和请求带的参数名字一致;
甚至可以直接通过 @PathVariable来接收多个参数
对于json数据也可以通过 @RequestBody轻松解决
处理响应
原始的javaweb处理响应十分不便
1)需要通过response.setContentType来设置数据格式类型
2)需要通过ObjectMapper将数据转成string形式才能发送,这一步也需要我们手动完成
@SneakyThrowsprotected void asyResponse(ResponseResult result, HttpServletResponse response) {response.setContentType("application/json;charset=utf-8");objectMapper.registerModule(new JavaTimeModule());PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(result));}
springMVC对于以上步骤都进行了封装处理
当我们需要将数据传回前端时,只需要直接return,不需要关心数据类型的转换
@RequestMapping("/list")private ResponseResult list() {ResponseResult result = studentService.list();return result;}
配置文件
pom.xml文件————各种依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.example</groupId><artifactId>J10_Project02</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>com.wngz</groupId><artifactId>SpringMvc_Case</artifactId><packaging>war</packaging><name>SpringMvc_Case Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><!--scope范围,表示这个jar只能在测试环境中使用,即只能在test文件夹中使用--><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency>
<!-- springMVC的框架支持--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.25</version></dependency>
<!-- javaWeb的基础支持,为了防止在其他环境运行时和tomcat自带的servlet冲突,scope设置为只有在开发环境适用--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><!--表示所使用的jar仅仅只是在开发环境中使用,发布时不使用--><scope>provided</scope></dependency>
<!-- 页面渲染api--><dependency><groupId>javax.servlet.jsp</groupId><artifactId>javax.servlet.jsp-api</artifactId><version>2.3.3</version><scope>provided</scope></dependency>
<!-- 读取和返回json格式的依赖支持--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.14.2</version></dependency></dependencies><build><finalName>SpringMvc_Case</finalName></build>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0">
<!-- 编码过滤器,过滤器要在DispatcherServlet前面配置,不然不生效-->
<!-- 过滤器本质也是servlet,配置方法和servlet一样--><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param><init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value></init-param><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- <url-pattern>/</url-pattern>当配置 / 的时候,会匹配到路径型的如:/login,而不会匹配到后缀型的,如:/login.jsp当经过视图解析器返回jsp视图的时候,就不会进入到DispatcherServlet为什么jsp的请求不会命中到DispatcherServlet呢?因为servlet容器内的jsp的servlet将会被调用,此servlet已经默认映射在了*.jsp上,这样就不会经过DispatcherServlet,但是DispatcherServlet它能拦截到静态的资源/* 会匹配到所有的url,路径型和后缀型,包括静态资源,当经过视图解析器返回jsp视图的时候,就会进入到DispatcherServlet<filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>所以在过滤的时候就要处理返回的.jsp--><!-- DispatcherServlet是springmvc的核心部分,负责请求的接收,model and view 的各种分配和调度,控制各种handle来完成请求和响应--><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param>
<!-- springmvc依附于spring容器,因此需要告诉DispatcherServlet配置的spring容器的路径-->
<!-- 如果spring配置容器在web.xml同路径下就不需要配置--><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param>
<!-- DispatcherServlet需要配置成tomcat启动时就加载完成,否则接收不到请求--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
resources/springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"><context:component-scan base-package="com.wngz.springmvc.controller"></context:component-scan>
<!-- 表示静态资源有默认的servlet调用--><mvc:default-servlet-handler></mvc:default-servlet-handler>
<!-- 若没有该标签@Controller @RequestMapping("/hello")等标签不会生效--><mvc:annotation-driven></mvc:annotation-driven>
</beans>
特性
配置类WebMvcConfigurer
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyWrapHandler;import javax.annotation.Nullable;
import java.util.List;
import java.util.Properties;@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {// 配置路径匹配选项@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {// 默认情况下,使用后缀模式匹配(例如.html)和尾部斜杠匹配(/)。// 设置是否使用后缀模式匹配configurer.setUseSuffixPatternMatch(false);// 设置是否使用前缀模式匹配configurer.setUseTrailingSlashMatch(true);}// 配置内容协商选项@Overridepublic void configureContentNegotiation(ContentNegotiationConfigurer configurer) {// 默认不启用参数内容协商,只根据请求的文件扩展名进行内容协商。// 启用参数内容协商configurer.favorParameter(true);// 设置参数名称configurer.parameterName("mediaType");// 启用扩展名内容协商configurer.favorPathExtension(true);}// 配置异步请求支持选项@Overridepublic void configureAsyncSupport(AsyncSupportConfigurer configurer) {//默认超时时间为30秒。//使用默认的SimpleAsyncTaskExecutor作为异步任务执行器。// 设置默认的超时时间configurer.setDefaultTimeout(5000);// 添加自定义的异步任务执行器configurer.setTaskExecutor(new SimpleAsyncTaskExecutor());}// 配置默认Servlet处理选项@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {//默认情况下启用,映射所有不被@RequestMapping或其他处理器显式处理的请求到Servlet容器的默认Servlet。// 启用默认的Servlet处理configurer.enable();}// 添加格式化器和转换器@Overridepublic void addFormatters(FormatterRegistry registry) {// 添加自定义的格式化器registry.addFormatter(new DateFormatter("yyyy-MM-dd"));}// 添加拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 添加自定义的拦截器registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/login");}// 添加资源处理器,用于静态资源的处理@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//默认情况下,没有显式添加静态资源处理器。静态资源通常直接由Servlet容器处理,例如Tomcat或Jetty。// 将所有 /static/** 的请求映射到 classpath:/static/ 目录下的资源registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}// 添加跨域资源共享(CORS)映射@Overridepublic void addCorsMappings(CorsRegistry registry) {//默认情况下不启用任何跨域资源共享配置,即不允许跨域请求。// 允许来自任意源的跨域请求registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "PUT", "DELETE").allowCredentials(true).maxAge(3600);}// 添加视图控制器@Overridepublic void addViewControllers(ViewControllerRegistry registry) {//默认情况下,没有添加任何视图控制器或视图解析器,因此所有视图解析工作交给视图解析链中的默认解析器处理。// 将 / 请求映射到 home.html 视图registry.addViewController("/").setViewName("home.html");}// 配置视图解析器@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {// 配置JSP视图解析器registry.jsp("/WEB-INF/views/", ".jsp");}// 添加自定义的参数解析器@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {// 添加自定义的参数解析器resolvers.add(new MyCustomArgumentResolver());}// 添加自定义的返回值处理器@Overridepublic void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {// 添加自定义的返回值处理器handlers.add(new ResponseBodyWrapHandler());}// 配置消息转换器@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {// 添加自定义的消息转换器converters.add(new MappingJackson2HttpMessageConverter());}// 扩展消息转换器@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {// 在现有转换器之前添加自定义的消息转换器converters.add(0, new MyCustomMessageConverter());}// 配置异常解析器@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {// 添加简单的映射异常解析器SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();Properties mappings = new Properties();mappings.setProperty("org.springframework.web.servlet.PageNotFound", "error-404");mappings.setProperty("org.springframework.dao.DataAccessException", "error-database");exceptionResolver.setExceptionMappings(mappings);resolvers.add(exceptionResolver);}// 扩展异常解析器@Overridepublic void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {// 在现有解析器之前添加自定义的异常解析器resolvers.add(0, new MyCustomExceptionResolver());}// 获取验证器@Nullable@Overridepublic Validator getValidator() {// 返回自定义的验证器return new LocalValidatorFactoryBean();}// 获取消息码解析器@Nullable@Overridepublic MessageCodesResolver getMessageCodesResolver() {// 返回自定义的消息码解析器return new DefaultMessageCodesResolver();}
}
拦截器 HandlerInterceptor
Filter和HandlerInterceptor的区别
Filter 定义:
Filter是Java Servlet规范的一部分,定义在javax.servlet.Filter接口中,用于对请求和响应进行过滤。
作用范围:
Filter可以对几乎所有的请求进行拦截,包括静态资源(如HTML、CSS、JavaScript文件)和动态资源(如Servlet、JSP)。
生命周期:
Filter在整个应用程序启动时被初始化,并在应用程序关闭时销毁。每个请求都会调用Filter的doFilter方法。
用途:
-
认证和授权:检查用户是否登录,或者判断用户是否有权限访问某些资源。
-
日志记录:记录请求的详细信息,例如IP地址、请求时间、请求参数等。
-
压缩响应:在将响应发送回客户端之前对其进行压缩。
-
字符编码:设置请求和响应的字符编码,确保正确处理不同的字符集。
-
跨域资源共享(CORS):处理跨域请求,设置CORS相关的头信息。
示例:
public class MyFilter implements Filter {public void init(FilterConfig filterConfig) throws ServletException {// 初始化代码}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// 预处理代码System.out.println("Request received at MyFilter");chain.doFilter(request, response); // 将请求传递给下一个过滤器或目标资源// 后处理代码System.out.println("Response processed at MyFilter");}public void destroy() {// 销毁代码}
}
Spring MVC HandlerInterceptor 定义:
HandlerInterceptor是Spring框架的一部分,定义在org.springframework.web.servlet.HandlerInterceptor接口中,用于拦截处理器(controller)的执行过程。
作用范围:
HandlerInterceptor主要用于拦截Spring MVC的处理器方法。它不拦截静态资源,除非特别配置。
生命周期:
HandlerInterceptor的生命周期与Spring上下文相关。它们在Spring容器初始化时创建,并在容器关闭时销毁。
用途:
-
预处理和后处理:在处理请求之前执行逻辑(例如身份验证),在处理请求之后但在生成视- 图之前执行逻辑(例如日志记录)。
-
修改请求和响应:可以修改请求对象或响应对象中的数据。
-
执行链控制:可以决定是否继续执行处理器链。
方法:
-
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
:在处理器方法执行之前调用。 -
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
:在处理器方法执行之后且在视图渲染之前调用。 -
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
:在整个请求完成之后,即视图渲染之后调用。
示例:
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("Request received at MyInterceptor");return true; // 返回true表示继续处理,返回false表示中断处理}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.out.println("Handler executed at MyInterceptor");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.out.println("Request completed at MyInterceptor");}
}
配置拦截器
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {@AutowiredJwtTokenTemplate jwtTokenTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {List<String> whiteList = new ArrayList<>();//白名单whiteList.add("/**/*.html");whiteList.add("/js/**/*");whiteList.add("/");whiteList.add("/user/login");whiteList.add("/code/generate");registry.addInterceptor(new TokenInterceptor(jwtTokenTemplate)).excludePathPatterns(whiteList);//添加白名单}
}
@Controller,@Service,@Reposity
作用和javaweb的@WebServlet一样
标记为一个webServlet
注意:若用该三种注解标注Servlet,需要有返回值的方法必须用@ResponseBody标注
@Controller
@RequestMapping("/hello")
public class HelloController {public HelloController() {System.out.println("HelloController~~~");}//@RequestMapping默认为Get,Post请求都匹配@RequestMapping("/demo1")@ResponseBody//当需要返回值时,如果该Controller没有标记为RestController则需要加上@ResponseBody注解public String demo1() {return "woniu school";}@RequestMapping("/student")@ResponseBodypublic Student getStudent() {Student student = new Student();student.setName("张三");student.setAge(19);return student;}
}
@RestController
作用同样是标记为一个webServlet
不同的是使用该注解标记的Servlet类不需要在方法上标注 @ResponseBody
相当于 @Controller+@ResponseBody
@RestController
@RequestMapping("/subject")
public class SubjectController {@Autowiredprivate SubjectService subjectService;@RequestMapping("/list")private ResponseResult list() {ResponseResult result = subjectService.list();return result;}
}
@RequestMapping(请求路径匹配)
用作请求路径的匹配;
作用在类上标记该servlet映射的路径;
作用在方法上同样道理,
需要注意的是如果要访问servlet的具体方法时,url需要包含Servlet类上@RequestMapping("/exam")的路径和方法上@RequestMapping("/add")的路径
@RequestMapping("/exam")
public class ExamController {@Autowiredprivate ExamService examService;@RequestMapping("/add")protected ResponseResult add(@RequestBody Exam exam) {ResponseResult result=examService.add(exam);return result;}
}
元注解:
@Target({ElementType.TYPE, ElementType.METHOD})=>可以作用在类和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
}
@GetMapping
作用和@RequestMapping一样,不过只能标注在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = {RequestMethod.GET}
)
public @interface GetMapping {
}
@PostMapping
作用和@RequestMapping一样,不过只能标注在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = {RequestMethod.GET}public @interface PostMapping {
}
@PutMapping
作用和@RequestMapping一样,不过只能标注在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = {RequestMethod.GET}public @interface PutMapping {
}
@DeleteMapping
作用和@RequestMapping一样,不过只能标注在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = {RequestMethod.GET}public @interface DeleteMapping {
}
接收前端发送的参数!!!
普通get请求(参数在url上)
单个参数
后端获取方式:在方法的参数列表中写上参数,当请求匹配到时可自动映射
注意:方法列表中的参数名要和get请求带的参数名要一一对应
前端:
axios.get("/exam/remove?id=" + exam.id)
controller:
@RequestMapping("/remove")protected ResponseResult remove(Integer id) {ResponseResult result=examService.remove(id);return result;}
多个参数 @PathVariable
当通过get发送多个参数数据时,需要使用@PathVariable来指定
注意:@PathVariable指定的参数名要和路径的参数名保持一致
前端:
axios.get(`/owner/page/${this.pageNumber}/${this.pageSize}` )
controller:
@GetMapping("/page/{pageNumber}/{pageSize}")public ResponseResult page(@PathVariable Integer pageNumber,@PathVariable Integer pageSize) {PageHelper.startPage(pageNumber, pageSize);List<Owner> owners = ownerMapper.selectAll();PageInfo pageInfo = new PageInfo(owners);ResponseResult result = ResponseResult.success(pageInfo);return result;}
post发送复杂数据类型@RequestBody
前端:直接将json对象放在请求体中
axios.post("/owner/add", this.owner4Add)
controller:
在方法参数列表中通过@RequestBody注解来标注接收复杂类型对象
//接收单个JSON对象
@PostMapping("/add")
protected ResponseResult add(@RequestBody Owner owner) {ResponseResult responseResult = ownerService.add(owner);return responseResult;
}
//接收前端数组对象
@PostMapping("/batchDelete")
protected ResponseResult batchDelete(@RequestBody Integer[] ids) {ResponseResult responseResult = ownerService.batchDelete(ids);return responseResult;
}