SpringMVC笔记：报文信息转换|拦截器|异常处理器|全注解配置MVC
springMVC笔记包括报文信息转换器，拦截器源码分析，异常处理器，通过注解配置springMVC代替web.xml和springMVC.xmlHttpMessageConverter报文信息转换器，报文包含有两个方面，请求报文和响应报文。请求报文就是从浏览器发送到服务器，属于request对象，把请求报文转换成java中的一个对象。响应报文是由服务器响应给浏览器的 ，服务器中使用java语言，将java对象转换成响应报文。@RequestBodyRequestEntity@ResponseBody(☆)ResponseEntity@RequestBody在控制器方法中设置形参，并用@RequestBody进行标识，形参所表示的就是当前请求的请求体 //在控制器中处理该请求 @RequestMapping("/testRequestBody") //@RequestBody表示当前的请求参数 public String testRequestBody(@RequestBody String requestBody) throws UnsupportedEncodingException { System.out.println("requestBody : " + URLDecoder.decode(requestBody,"UTF-8")); return "success"; } RequestEntity将所有的浏览器请求信息封装成一个对象，在形参中可以添加，获取请求信息。 @RequestMapping("/testRequestEntity") public String testRequestEntity(RequestEntity<String> requestEntity){ //当前requestEntity表示整个请求报文的信息 System.out.println("获取请求头信息:" + requestEntity.getHeaders()); System.out.println("获取请求体信息:" + requestEntity.getBody()); return "success"; }要想对浏览器进行响应，可以实现一个页面跳转，然后响应浏览器一个完整的页面(转发或重定向)；或者是使用HttpServletResponse中的 response.getWriter().print("); 。回顾原生方法 @RequestMapping("/testResponse") public void testResponse(HttpServletResponse response) throws IOException { /*不需要返回值，不需要返回视图名称实现页面跳转，要么return null 要么设置为void 将print直接作为响应报文的响应体响应到浏览器 响应体是什么我们在浏览器中看到的就是什么 */ response.getWriter().print("hello,response"); }@ResponseBody(☆)在springMVC中实现响应浏览器的功能,为我们提供了一个注解@ResponseBody @RequestMapping("/testResponseBody") @ResponseBody /*@RequestBody用来标识当前的控制器方法，添加注解之后， 返回值不再是视图名称了，而是当前响应的响应体*/ public String testResponseBody(){ return "success"; } 通过注解明显发现比原生API要简单一些。现在我需要响应回去一个对象，而不是一个字符串或文本之类的，就需要使用到JSON，那么就需要导入jackson的依赖pom.xml <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency> json有两种格式，一种叫对象，一种叫数组。最外层是大括号就是json对象，如果最外层是[]是json数组。要想实现自动将 [label color="red"]java对象转换为json[/label] ，需要以下步骤：导入jackson的依赖springMVC.xml中开启mvc注解驱动 <mvc:annotation-driven/>处理器方法上添加@ResponseBody标识将java对象直接作为控制器方法返回值返回，就会自动转换为json格式的字符串 [label color="red"]不是json对象[/label][note type="primary flat"]补充json信息：①json是javascript中的一种数据格式，json是一种数据交互格式(xml也是)，现在xml最多作为配置文件，而json由于数据结构比较简单、解析简单、生成的数据量少...xml解析上和dom4j有关，dom4j(文档对象模型)在解析的时候将当前整个xml当成一个document然后先读取根标签，再获取子标签，一层层解析。json两种格式，都非常方便获取里面的数据。②经常把java对象转换成json数据，实体类转换为json对象，map集合转换为json对象，list集合转换为json数组。[/note]springMVC处理ajaxajax本身就是不刷新(页面不发生跳转的情况下)与服务器进行交互。index.html SpringMVC处理ajax @RequestMapping("/testAxios") @ResponseBody //当前方法返回值就是响应浏览器的数据 public String testAxios(String username,String password){ System.out.println("username = " + username + ",password=" + password); return "hello,axios"; } [video title="springMVC处理ajax " url="https://img.kaijavademo.top/typecho/uploads/2023/08/videos/2023-08-16%2015-17-53.mkv " container="bvza650muge" subtitle=" " poster=" "] [/video]仅仅是把ajax和springMVC响应浏览器数据一起使用了。只有要把java对象响应到浏览器的时候才需要使用jackson。@RestController注解(☆)以后浏览器和服务器之间的交互使用的都是json，在SpringBoot和SpringCloud的微服务中，微服务中就是以业务逻辑为边界，将完整工程分成一个一个单独运行、单独部署的小工程中。每个小工程中都是一个微服务，微服务之间使用json + http数据交互。微服务里面每一个控制器方法都要加 [label color="red"]@ResponseBody [/label] ，注解将使用非常之多，所以说mvc为我们提供了一个派生注解 [label color="blue"]@RestController注解[/label]当前控制器中所有的控制器方法，所有的返回值都是作为响应浏览器数据的响应体存在的。使用此注解相当于同时为当前方法提供了@Controller和@ResponseBody注解ResponseEntity可以将ResponseEntity属于一个自定义的响应报文，此控制器方法返回值就是响应到浏览器的响应报文。可以配合下面的文件上传下载使用。文件下载和上传文件下载通过ResponseEntity实现下载@Controller public class FileUpAndDownController { @RequestMapping("/testDown") //控制器方法 ResponseEntity public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException { //获取ServletContext对象(表示当前整个工程) ServletContext servletContext = session.getServletContext(); //获取服务器中文件的真实路径 String realPath = servletContext.getRealPath("/static/img/1.png"); //创建输入流 InputStream is = new FileInputStream(realPath); //创建字节数组 is.available() 获取当前输入流对应文件的所有字节数 byte[] bytes = new byte[is.available()]; //将输入流所有的字节读到字节数组中,把数组响应到浏览器，就是要下载的文件 is.read(bytes); //创建HttpHeaders对象设置响应头信息，接口，使用实现类创建对象HttpHeaders MultiValueMap<String, String> headers = new HttpHeaders(); /*设置要下载方式以及下载文件的名字，只有=后面的文件名可以修改。 attachment以附件的方式下载 filename为下载文件设置默认名*/ headers.add("Content-Disposition", "attachment;filename=1.png"); //设置响应状态码 HttpStatus statusCode = HttpStatus.OK; //转换成ResponseEntity,创建ResponseEntity对象，有三个参数(响应报文) /* 参数一：bytes存放了要下载到文件中所有字节(响应体) 参数二：响应头 参数三：状态码 */ ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode); //关闭输入流 is.close(); //responseEntity响应到浏览器 return responseEntity; } } 对于下载来说，可改动的地方很少，格式比较固定。文件上传对于文件上传来说，是不可以使用get请求首先需要写一个上传的form表单，然后写具体的控制器方法控制器中方法的书写流程pom文件中导入jackson依赖导入上传功能的依赖jar包 commons-fileuploadpom.xml<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> 添加MultipartFile以及对应input标签名(name)photo上传的文件不能直接转换成MultipartFile，设置文件上传解析器（配置bean标签）springMVC.xml <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>这里一定要配置bean，我差点把bean标签忘记配置id属性了(555，还没反应过来被老师骂了)获取对应的文件名获取项目运行目录的路径判断最终路径拼接上传到服务器 @RequestMapping("/testUp") public String testUp(MultipartFile photo,HttpSession session) throws IOException { //实现上传功能 /* 导入jackson依赖，和jar包.添加形参名和上传的input标签中的name保持一致， 在mvc中将上传文件封装到了一个MultipartFile,跟我们上传文件的信息 已经操作已经封装好了，当前上传的文件不能直接转换成MultipartFile 获取服务器路径需要有session */ String fileName = photo.getOriginalFilename();//获取所上传的文件名 ServletContext servletContext = session.getServletContext();//根据session获取到servletContext对象 //servletContext.getRealPath("photo");获取项目运行目录的路径。获取到photo的路径 String photoPath = servletContext.getRealPath("photo"); File file = new File(photoPath); //判断photoPath所对应路径是否存在 if(!file.exists()){ //若不存在创建目录 file.mkdir(); } //最终路径 = photoPath + 路径分隔符(不知道使用哪个斜线使用File.separator替代) + 上传文件名 String finalPath = photoPath +File.separator + fileName; photo.transferTo(new File(finalPath));//上传/转移到服务器 //上传成功后跳转到success页面 return "success"; } 当前程序会有一个问题，文件名同名的情况下，会覆盖掉其他人上传的同名文件。使用UUID这样一个32位的随机序列作为文件名，对文件名处理替换，将当前文件名采用UUID替换，但是文件后缀还需要保留。对当前的控制层方法略加修改完善 @RequestMapping("/testUp") public String testUp(MultipartFile photo, HttpSession session) throws IOException { //获取上传的文件的文件名 //实现上传功能 /* 导入jackson依赖，和jar包.添加形参名和上传的input标签中的name保持一致， 在mvc中将上传文件封装到了一个MultipartFile,跟我们上传文件的信息 已经操作已经封装好了，当前上传的文件不能直接转换成MultipartFile 获取服务器路径需要有session */ String fileName = photo.getOriginalFilename();//获取所上传的文件名 //获取上传的文件的后缀名 String suffixName = fileName.substring(fileName.lastIndexOf(".")); //将UUID作为文件名 String uuid = UUID.randomUUID().toString().replaceAll("-",""); //将uuid和后缀名拼接后的结果作为最终的文件名 fileName = uuid + suffixName; //根据session获取到servletContext对象 ServletContext servletContext = session.getServletContext(); //通过servletContext获取服务器中photo目录的路径 String photoPath = servletContext.getRealPath("photo"); File file = new File(photoPath); //判断photoPath所对应路径是否存在 if (!file.exists()) { //若不存在创建目录 file.mkdir(); } //最终路径 = photoPath + 路径分隔符(不知道使用哪个斜线使用File.separator替代) + 上传文件名 String finalPath = photoPath + File.separator + fileName; photo.transferTo(new File(finalPath));//上传/转移到服务器 //上传成功后跳转到success页面 return "success"; } 拦截器-->|Request|A[Filter] --> B(DispatcherServlet) -->C|拦截器1|[preHandle] --> D(拦截器2) --> E[postHandle]除了preHandle、postHandle以外，还有一个afterComplation，是在我们渲染视图之后执行的。扩展一下mvc的注解驱动，主要是添加配置拦截器springMVC.xml <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="defaultCharset" value="UTF-8"/> <property name="supportedMediaTypes"> <value>text/html</value> <value>application/json</value> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <mvc:interceptors> <bean class="com.atguigu.mvc.interceptors.FirstInterceptor"></bean> </mvc:interceptors> [label color="red"]此时配置的拦截器是对DispatcherServlet所处理的所有的请求进行拦截 [/label]FirstInterceptor类@Component public class FirstInterceptor implements HandlerInterceptor { /*拦截器的两种方式 ①让该类实现HandlerInterceptor接口 ②让该类继承HandlerInterceptorAdapter类(已过时) 接口中是有方法体的默认方法，我们选择重写 按住ctrl + o 进行重写*/ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { /*此方法有当前是否进行放行，false表示拦截，true表示放行*/ System.out.println("FirstInterceptor --->preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("FirstInterceptor --->postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("FirstInterceptor --->afterCompletion"); } }我们可以使用MVC支持ant风格的路径，所谓的模糊查询，来进行测试当然也有另外一种方式给FirstInterceptor设置普通层组件(注解)，并且在springMVC.xml中添加ref标签 <mvc:interceptors> <ref bean="firstInterceptor"></ref> </mvc:interceptors>实际上这两种方式是一致的，因为无法对于bean标签设置一定的拦截规则，所以这两种方式 [label color="red"]都默认对所有请求进行拦截[/label] 。手动配置拦截器当然interceptors标签中我们可以配置多个interceptor，第三种拦截方式就是这样编写的，我们可以手动编写拦截规则。我们可以编写一个访问首页不会被拦截，而点击按钮跳转到对应页面触发拦截器的规则。<mvc:mapping path="/*"/> 表示拦截表示我们访问上下文路径下面的一层目录。我们可以使用 [label color="red"]/**[/label] 拦截多级路径springMVC.xml <mvc:interceptors> <!--<bean class="com.atguigu.mvc.interceptors.FirstInterceptor"></bean>--> <!--<ref bean="firstInterceptor"></ref>--> <mvc:interceptor> <mvc:mapping path="/*"/> <mvc:exclude-mapping path="/"/> <ref bean="firstInterceptor"></ref> </mvc:interceptor> </mvc:interceptors> 多个拦截器的执行顺序[note type="primary flat"]preHandle()会按照配置的顺序执行，而postHandle()和afterComplation()会按照配置的反序执行[/note]通过书写两个拦截器，来进行测试翻看源码：如果说我在preHandle这个布尔方法中传递false，那么在底层源码中postHandle一定一个都不会执行，而afterCompletion会执行，而afterCompletion执行的时候，遍历几个拦截器和interceptorIndex(拦截器返回false之前的拦截器索引)有关。也就是说假设有多个拦截器，第三个拦截器返回false。那么 interceptor.preHandle(request, response, this.handler) 返回false取反执行if中的语句，然后从第二个拦截器倒着遍历（执行 this.triggerAfterCompletion(request, response, (Exception)null); ），遍历到第一个拦截器。preHandle返回false和之前的拦截器一定会执行postHandle一个都不会执行afterCompletion中preHandle返回false之前的拦截器会执行，根据索引执行，索引最大 = 返回false之前的拦截器索引源码 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } return true; } 异常处理器springMVC提供了一套异常处理器HandlerExceptionResolver，它有多个实现类我们可以通过配置文件或者是注解的方式来实现自定义配置异常处理，将可能会出现的异常配置到异常映射中，出现指定异常就会跳转到对应页面。基于配置的异常处理在bean标签中为 [label color="blue"]properties属性赋值[/label] springMVC.xml <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.ArithmeticException">error</prop> </props> </property> <property name="exceptionAttribute" value="ex"></property> </bean>基于注解的异常处理springMVC也为我们提供了一套注解，通过注解也可以来实现异常处理@ControllerAdvice //说明注解包含了@Component的功能,也具有将类标识为组件的功能 public class ExceptionController { @ExceptionHandler(value = { //书写当前可能会出现的异常,书写某个类型的Class对象 ArithmeticException.class, NullPointerException.class //如果当前出现了上述异常，就会通过注解所标识的方法来作为新的控制器方法执行 }) public String testException(Exception ex, Model model) { /*控制器出现了指定异常，执行我们当前指定的方法作为新方法 通过添加形参Exception可以获得异常信息,然后往请求域中共享数据*/ model.addAttribute("ex",ex); return "error"; } }通过注解配置SpringMVC(☆)通过配置类和注解代替web.xml和SpringMVC配置文件(springMVC.xml)功能。初始化类代替web.xmlweb.xml是作为当前web工程的描述符，每当启动Tomcat服务器，web.xml第一个作为加载的配置文件。Spring3.2引入了一个便利的WebApplicationInitializer基础实现，名为AbstractAnnotationConfigDispatcherServletInitializer，当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候，容器会自动发现它，并用它来配置Servlet上下文。创建初始化类WebInit/** * TODO web工程的初始化类 * 用来代替web.xml * 除了注册DispatcherServlet以外还需要注册过滤器 */ public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { /* 此方法来获取当前的一个根配置，指定的就是spring的配置类 在没有对应配置类的情况下创建一个长度为0Class数组 有对应SpringConfig配置类的情况下，将配置类放在数组中返回 */ return new Class[]{SpringConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { /* 此方法是来指定SpringMVC的配置类 */ return new Class[]{WebConfig.class}; } @Override protected String[] getServletMappings() { /* 此方法是来获取DispatcherServlet的映射路径(<url-pattern>) */ return new String[]{"/"}; } @Override protected Filter[] getServletFilters() { /*此方法发是用来注册过滤器的，两个过滤器*/ CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); characterEncodingFilter.setForceResponseEncoding(true); HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter}; } } 配置视图解析器这里我们基于之前所学将一个最完整的springMVC.xml配置出来,一个完整的配合文件不光需要视图模板，也需要配置其他的很多组件。我在注释中一 一列举出来，之后使用原生配置xml也是一个比较完整的配置思路。在我们创建出的 [label color="blue"]WebConfig类[/label] 中配置package com.atguigu.mvc.config; import com.atguigu.mvc.interceptor.TestInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.*; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.spring5.view.ThymeleafViewResolver; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresolver.ServletContextTemplateResolver; import java.util.List; import java.util.Properties; /** * TODO 代替SpringMVC的配置文件(springMVC.xml) * 1.扫描组件 2.视图解析器 3，view-controller对首页的访问 4.default-servlet-handler开放对静态资源的访问 * 5.mvc注解驱动 6.文件上传解析器 7.异常处理 8.拦截器 * */ //将当前类标识为一个配置类 @Configuration //1.开启MVC的自动扫描组件 @ComponentScan("com.atguigu.mvc.controller") //5.开启MVC的注解驱动 @EnableWebMvc public class WebConfig implements WebMvcConfigurer {//添加接口，实现除了thymeleaf的其他组件 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { /*4.default-servlet-handler开放对静态资源的访问*/ configurer.enable(); } @Override public void addInterceptors(InterceptorRegistry registry) { /*8.拦截器 创建一个拦截器，实现HandlerInterceptor接口，并重写.(可以参照上面文章拦截器) 创建对象，添加一定的拦截规则 addPathPatterns()添加拦截路径(可写多个) excludePathPatterns()排除拦截路径(可写多个) */ TestInterceptor testInterceptor = new TestInterceptor(); registry.addInterceptor(testInterceptor).addPathPatterns("/**"); } @Override public void addViewControllers(ViewControllerRegistry registry) { /*3，view-controller对首页的访问 addViewController("") 设置路径 setViewName 设置视图名称 */ registry.addViewController("/hello").setViewName("hello"); } @Bean public MultipartResolver multipartResolver(){ /* 6.文件上传解析器 因为是MultipartResolver一个接口，所以说使用返回它的实现类对象 平时是在bean标签中配置，所以说要添加@Bean注解 */ CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(); return commonsMultipartResolver; } @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { /*7.异常处理 可以配置成Bean的形式，也可以使用这个方法实现 创建异常处理解析器，并添加到形参的集合中 创建Properties对象，使用setProperty/getProperty setExceptionAttribute设置异常信息请求域中的键 */ SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties prop = new Properties(); prop.setProperty("java.lang.ArithmeticException","error"); exceptionResolver.setExceptionMappings(prop); exceptionResolver.setExceptionAttribute("exception"); resolvers.add(exceptionResolver); } /* 配置视图解析器 */ //配置生成模板解析器thymeleaf(可复制，比起jsp比较麻烦) @Bean//加上该注解返回值就是IoC容器中的一个Bean public ITemplateResolver templateResolver() { WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); // ServletContextTemplateResolver需要一个ServletContext作为构造参数，可通过WebApplicationContext 的方法获得 ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( webApplicationContext.getServletContext()); templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setTemplateMode(TemplateMode.HTML); return templateResolver; } //生成模板引擎并为模板引擎注入模板解析器 , //同下面viewResolver方法一致的是形参位置使用了自动装配@Autowired @Bean public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } //生成视图解析器并未解析器注入模板引擎 @Bean public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setCharacterEncoding("UTF-8"); viewResolver.setTemplateEngine(templateEngine); return viewResolver; } } SpringMVC的常用组件DispatcherServlet：前端控制器，不需要工程师开发，由框架提供统一处理请求响应，整个流程的控制中心，通过它调用其他组件处理用户请求。HandlerMapping：处理器映射器，不需要工程师开发，由框架提供请求的url、方法与控制器之间创建映射。 [label color="blue"]找[/label]Controller(Handler)：控制器(处理器)，需要工程师开发在DispatcherServlet的控制下控制器对用户请求进行处理。HandlerAdapter：处理器适配器，不需要工程师开发，由框架提供处理器适配器，来调用对应的控制器方法。 [label color="blue"]执行[/label]ViewResolver：视图解析器，不需要工程师开发，由框架提供页面跳转，得到对应的视图，视图就是由视图解析器解析，不同的视图名称，创建不同的视图，进行渲染。有三种MVC视图View：视图视图解析器解析视图名称得到的视图，为用户展示模型数据。我书写这篇文章的初衷就是总结学习的进度，遗忘之际可以拿出来翻看，如有不对的地方还望指正，多多海涵。
SpringMVC笔记：框架搭建|请求映射注解|获取请求参数注解
基于SpingMVC的学习笔记，包括框架的基本搭建，请求于请求映射，还有请求参数的注解，以及通过过滤器解决了乱码问题。2023/11/29 更新@RequestParam解释配置web.xml手动创建maven工程的操作在基于SpringMVC手动添加web工程中添加之后我们需要配置SpringMVC的web.xml有关servlet配置可以查看让请求对应类的配置如果使用JDK17+Tomcat8要注意导入mvc为5.3.17(之前我使用5.3.1报错，我在这里找了好久问题，还换了一个模块重写。。) <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.17</version> </dependency>采用默认的配置方式需要放在WEBINF下，位置默认，名称默认，这样不好，学完maven工程应该统一将文件放在resources文件下.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"> <!--<servlet>里面<servlet-name>和<servlet-mapping>里面的<servlet-name>保持一致--> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>扩展配置方式对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"> <!--<servlet>里面<servlet-name>和<servlet-mapping>里面的<servlet-name>保持一致--> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--添加<init-param>标签，初始化参数，伴随当前servlet的初始化来初始化的--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> <!--servlet第一次访问会初始化,初始化中会执行很多内容，都放在第一次访问.会严重影响第一次访问的速度, 添加标签<load-on-startup>将SpringMVC的前端控制器DispatcherServlet初始化时间提前到服务器启动时 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>实际上添加了一个 <init-param> 和 <load-on-startup> 开启组件扫描SpringMVC还有一个Thymeleaf视图解析器，负责视图跳转。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" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <context:component-scan base-package="com.atguigu.mvc.controller"></context:component-scan> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="order" value="1"/> <property name="characterEncoding" value="UTF-8"/> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML5"/> <property name="characterEncoding" value="UTF-8" /> </bean> </property> </bean> </property> </bean> </beans>补充HelloController类/* SpringMVC不认识 要将当前类作为SpringIoC容器中一个组件的时候进行管理 有两种方式，一种是bean标签配置。第二种注解+扫描 通过注解+扫描的方式配置控制器 注解有四个component controller service repository 表示之后需要扫描才能作为bean进行管理 */ @Controller //@ComponentScan public class HelloController { //请求路径 // "/" -->/WEB-INF/tmplates/index.html //控制器里的方法才是处理请求的方法 //方法名无关，处理请求匹配到这个方法与方法名无关 //value = / 表示当前文件的上下文路径 // localhost:8080/springMVC/ @RequestMapping(value = "/") //请求映射注解，将我们当前的请求和控制器方法床创建对应关系 public String index() { /* 返回值：返回视图名称，视图名称决定了要跳转的页面， 因为当前HTML无法直接访问，最终跳转的页面由视图名称决定， 当我们返回一个视图名称，就会被我们刚配置的视图解析器解析 在视图解析器中通过加上视图前缀和后缀就是我们最终要访问的页面 /WEB-INF/tmplates/index.html 去掉视图前缀和后缀 index 最后还需要加上注解 */ return "index"; /* 最后真实情况就是 @RequestMapping(value = "/") 当我们浏览器发送的请求是/ ，也就是上下文路径的时候， 就会执行我们当前注解中所标识的方法，这个方法返回的视图名称index 被视图解析器解析，加上前后缀就可以跳转到我们的页面 */ }@RequestMapping(value = "/") 非常重要，当然里面不只有value这一个属性，除了通过请求地址配置请求控制器方法index之外，我们还可以通过请求方式、请求参数、请求报文等。当然只对value属性赋值的时候也可以省略直接写成@RequestMapping("/") 配置Tomcat访问当前的工程即可。访问指定页面通过index.html访问其他页面的方式。首先需要添加一个thymeleaf渲染的超链接标签 首页 首页 访问目标页面target.html 然后创建一个target.html在HelloController类中实现跳转的方法即可。 /* 想要跳转到target.html控制台中处理，写对应的方法 和方法名无关，但是起的时候最好见名知意 */ @RequestMapping("/target") public String toTarget(){ /* 返回值：视图名称 视图名称就是去掉 视图前缀，和 视图后缀 剩下的部分 视图名称会被视图解析器解析 最后加上注解@RequestMapping value值一定要和当前请求地址(访问目标页面target.html) 保持一致 /target */ return "target"; } 总结搭建框架(☆)快速搭建一个springMVC框架的流程相关依赖(pom文件) <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.17</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> </dependencies> 这依赖相对来说写的比较简单，在之后的学习中发现了并不完善补充在这里，主要是添加了package，可以在maven中重新打包项目 <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.17</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build>在main路径下部署webapp文件中的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"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app><param-value>classpath:springMVC.xml</param-value> 中指定了对应初始化的对应文件springMVC.xml,在类路径下寻找，所以说在resources中创建。 <load-on-startup>1</load-on-startup> 将控制器DispatcherServlet初始化时间提前到服务器启动时候，这样大大加快第一次请求执行速度。为了处理乱码问题，我们需要为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"> <filter> <filter-name>CharacterEncodingFilter</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>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--如果这样配置MVC是有默认的位置和名称，要想自定义位置和名称，设置初始化参数<init-param>--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>一个标配的web.xml的写法所谓标配就是，配置两个过滤器+一个前端控制器在学习到MVC后面之后，最固定的还需要加上一个 [label color="blue"]HiddenHttpMethodFilter[/label] 过滤器，调整编码顺序，这是web.xml的固定写法。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"> <filter> <filter-name>CharacterEncodingFilter</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>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--如果这样配置MVC是有默认的位置和名称，要想自定义位置和名称，设置初始化参数<init-param>--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>配置springMVC.xml一会我们需要创建控制层，对应添加@Controller注解，有注解就需要在springMVC.xml文件中开启自动扫描的功能，需要修改包的位置，对应的包就是控制类的前一层。我们写@(四个)注解就需要对应开启多个自动扫描，而开启多个自动扫描需要使用,(逗号)隔开,如同 <context:component-scan base-package="com.atguigu.rest.controller,com.atguigu.rest.dao"></context:component-scan> 同时描绘dao层和controller层，当然也可以跳出一层路径如同 <context:component-scan base-package="com.atguigu.rest"></context:component-scan> 在 [label color="orange"]springMVC.xml[/label] 文件中需要配置context命名空间，添加扫描对应包下的组件，并添加Thymeleaf视图解析器。<?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" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <context:component-scan base-package="com.atguigu.mvc.controller"></context:component-scan> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="order" value="1"/> <property name="characterEncoding" value="UTF-8"/> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML5"/> <property name="characterEncoding" value="UTF-8" /> </bean> </property> </bean> </property> </bean> </beans>在后面有视图控制器，对应不添加@RequestMapping注解了，需要配置完整的mvc的命名空间。<?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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">这里只补充命名空间，如果想要看完整的，就跳到视图控制器去翻看全部源码。在我书写RESTFul案例完后我也发现了更加详细的xml写法，包括一定的静态资源访问和注解驱动，我提供了跳转，也可以参考案例完整的源码。视图前缀在现有包中不存在，添加一个templates目录，并在里面存放对应的需要渲染的页面(html页面)index.htmlindex.html Title 首页 创建控制层现在需要有一个返回index视图名称加载当前templates中的这个html文件。<context:component-scan base-package="com.atguigu.mvc.controller"></context:component-scan> 没有当前包，在java包下创建对应路径，在Controller层中添加一个控制类(TestController)，添加方法返回对应的视图名称，交给视图解析器渲染。 [label color="red"]注意：TestController需要添加@Controller注解，控制器是一个普通的类，要想让SpringMVC找到我们的控制器，那么我们的控制器必须是IoC容器中的组件。[/label] 注意这里的 @RequestMapping("/") 之前有注解有解释，对应出来就是上下文路径后面加上的内容/@Controller public class TestController { @RequestMapping("/") public String index() { return "index"; } } 基础的框架搭建成功全注解配置MVC在学习到后期发现一个MVC实际上没有太固定的格式，每次需要去调整xml文件，每次需要来扩展xml配置文件的内容，以此来达到比较完整的效果，我在后面一篇文章中有提到全注解配置MVC，通过配置类 + 注解的形式主要完成了web.xml和springMVC.xml这两个文件的配置，其中springMVC.xml的注解我写的也很详细，里面几乎包含了所有需要配置的部分，就算是使用xml文件也可以参考，里面的内容基本不变可能会比上面的注解中的要详细一点，请见谅，跳转查看。@RequestMapping注解@RequestMapping注解位置@RequestMapping标识一个类：设置映射请求的请求路径的初始信息@RequestMapping标识一个方法：设置映射请求请求路径的具体信息@Controller @RequestMapping("/hello") public class RequestMappingController { /* 有两个类，都写@RequestMapping("/")，处理的请求地址，请求路径是一样的， 当前的springMVC就不知道该找谁处理问题了，就会报错 在我们所有的控制器里面保证@RequestMapping("/")匹配的请求地址是唯一的 */ /* @RequestMapping("/") public String index() { return "target"; }*/ /* @RequestMapping源码 @Target({ElementType.TYPE, ElementType.METHOD})表示注解可以使用在类上，方法上 使用在类上的情况：设置映射请求的请求路径的初始信息 使用在方法上的情况：设置映射请求请求路径的具体信息.和当前浏览器发送的请求创建关联，来匹配我们浏览器所发送的请求 如果说一个类中，方法上也有@RequestMapping注解，而类本身也有RequestMapping注解。 根据对应情况，应该先访问请求路径的初始信息，才能访问到访问具体信息 */ @RequestMapping(value = "/testRequestMapping") public String success(){ return "success"; } } [label color="red"]如果说一个类中，方法上也有@RequestMapping注解，而类本身也有RequestMapping注解。 根据对应情况，应该先访问请求路径的初始信息，才能访问到访问具体信息[/label] [label color="red"]当前多个控制器中不能有多个@RequestMapping属性(value)注解是一样的!!![/label] 也就是说一个请求映射不能对应多个控制器,但是实现这种方法，就需要在不同的控制器(当前类上)加上一层路径，再在方法上加一层路径，相当于加一层文件夹，防止文件名冲突。@RequestMapping注解的value属性 @RequestMapping( /* value属性是一个String类型的数组，可以赋给多个值,表示多个请求地址， 一个请求只会对应一个请求地址 表示当前请求映射可以处理多个请求，只要请求地址满足其中任何一个，就能让请求映射匹配上*/ value = {"/testRequestMapping","/test"} )[label color="red"]@RequestMapping注解的value属性必须设置，至少通过请求地址匹配请求映射[/label] @RequestMapping注解的method属性 @RequestMapping( value = {"/testRequestMapping","/test"}, /* 不设置method属性，表明不以请求方式为条件，任何请求方式都可以匹配 method属性也是一个String(字符串)类型的数组 一个请求映射可以处理多个请求方式的请求，前提是value必须匹配成功，必须要有value 会先匹配value属性，然后匹配其他属性 */ method = {RequestMethod.GET,RequestMethod.POST} //数组中的方式只要满足其一即可 //method = {RequestMethod.GET} // Request method 'POST' not supported 请求方式不被支持不是当前请求映射支持 ) SpringMVC中提供了@RequestMapping的派生注解可以不用设置参数，即添加注解的方式添加。处理get请求的映射-->@GetMapping处理post请求的映射-->@PostMapping处理put请求的映射-->@PutMapping处理delete请求的映射-->@DeleteMapping /* 只需要设置value属性即可， 只需要设置value属性，那么可以直接写 */ @GetMapping("/testGetMapping") public String testGetMapping(){ return "success"; } @RequestMapping注解的其他属性@RequestMapping注解的params属性 @RequestMapping( value = "/testParamsAndHeaders", /* "username"当前匹配的请求参数中一定需要username参数 =后面可以设置值(如果加!，表示一定不能是该值) "!username"当前匹配的请求参数中一定不需要username参数 params中所设置的条件必须同时满足 */ params = {"username","password=123456"} ) public String testParamsAndHeaders(){ return "success"; } 测试RequestMapping注解的params属性-->testParamsAndHeaders @RequestMapping注解的headers属性 @RequestMapping( value = "/testParamsAndHeaders", params = {"username","password!=123456"}, /* {"header"} {"!header"} {"header = xxx"} {"header != xxx"} headers = {"Host=localhost:8080"}表名当前请求头中的一个键值对，表示要求请求映射请求头必须携带Host键值为8081 */ headers = {"Host=localhost:8081"}//404 ) public String testParamsAndHeaders(){ return "success"; }几种报错情况404：①如果当前请求没有和任何一个Requestmethod和value匹配；②如果请求头信息匹配不成功；405：如果请求方式匹配不了，表明请求方式不被支持。400：如果当前的请求参数匹配不成功。MVC支持ant风格的路径所谓ant风格就是模糊查询，给@RequestMapping传递value值的时候可以使用类似于通配符一样的属性进行路径的分配。 @RequestMapping("/a?a/testAnt") /*只写value表示不通过请求方式，请求头信息，请求参数进行匹配。 只会根据请求地址匹配 ?可以表示任意的单个字符,必须要有任意的单个字符(/ ? 除外) a?a表示类似于a1a都能匹配这个请求路径 */ public String testAnt(){ return "success"; } ？：表示任意的单个字符*：表示任意的0个或多个字符：表示任意的一层或多层目录** [label color="red"]在使用时，只能使用//xxx的方式[/label]MVC路径中占位符赋值(☆)通过 [label color="blue"]@PathVariable[/label] 注解，简化传参形式，在RESTful风格中常用。 /* 复制请求地址，表示请求参数里面用大括号表示当前的参数， 大括号表示路径中的占位符表示路径中的1 */ @RequestMapping("/testPath/{id}/{username}") public String testPath(@PathVariable("id") Integer id, @PathVariable("username") String username) { System.out.println("id:" + id + ",username:" + username); /* 用占位符表示路径中有占位符，请求地址中也必须要有这一层路径 只能通过@PathVariable注解获取，将我们的占位符代表的值和形参进行绑定 当我们程序解析之后，就会将我们占位符对应的值复制给它所对应的形参 */ return "success"; } 获取请求参数通过请求映射注解匹配到对应请求，接下来就需要通过请求获取一些参数。[note type="default flat"]原生servlet中获取请求参数有两种方式：getParameter 来获取请求参数请求参数如果有多个同名，只能获取第一个。参考复选框要想获取所有的getParameterValues，返回的是一个字符串类型的数组[/note]ServletAPI获取原生方法，相对麻烦，属于基础@Controller public class ParamController { //测试获取请求参数功能的类 /* DispatcherServlet里面为我们封装了很多的数据， 调用我们当前的控制器方法的时候，根据控制器方法参数，来为方法注入参数(赋值) */ @RequestMapping("/testServletAPI") //形参位置的request表示当前请求 public String testServletAPI(HttpServletRequest request){ /* 可以直接使用request，为当前参数赋值 现在DispatcherServlet根据控制器方法注入不一样的值 如果检测到当前方法形参HttpServletRequest对象， 将当前DispatcherServlet里面所获得的表示当前请求的request对象复制给形参 */ String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println("username:" + username + ",password:" + password); return "success"; } }控制器方法形参获取请求参数通过请求中 [label color="blue"]传递参数[/label] ，在对应控制器方法中添加形参与 [label color="blue"]请求参数[/label] 保持一致即可。相比于原生Servlet非常方便。 @RequestMapping("/testParam") public String testParam(String username,String password){ /* 保证控制器方法的形参和我们当前的请求参数名保持一致 @{/testParam(username='admin',password=123456)} 可以自动将我们请求参数赋值给对应形参 */ System.out.println("username:" + username + ",password:" + password); return "success"; } 如果传递过来有多个同名的请求参数，可以使用字符串，也可以使用字符串数组写入形参上。 @RequestMapping("/testParam") public String testParam(String username, String password, String[] hobby) { /* 多个同名的请求参数如何获取，复选框模拟 如果在原生Servlet中需要使用getParameterValues返回字符串数组来获取全部信息 如果使用getParameter只会获取到第一个值信息 SpringMVC中也可以使用一个字符串类型的数组来获取，放在形参上 */ //若请求参数中出现多个请求参数，可以在控制器方法的形参位置设置字符串类型，或字符串数组来接受此请求参数 //若使用字符串类型的形参，最终结果为请求参数的每一个值之间使用逗号(,)进行拼接的结果 System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby)); return "success"; } @RequestParam控制器方法的形参和我们当前的请求参数名不一致，可以通过注解@RequestParam来关联。简单来说@RequestParam为我们的请求参数和控制器方法的形参创建映射关系。意思就是谁给我传递的是username参数，未来我给某个地方发送请求，以我在@RequestParam中规定的名称发送出去。如果我在Controller里面写的@RequestParam注解，代表我需要接收一个user_name名的参数，如果我在Service中使用@RequestParam注解写的代表我要发出的参数叫user_name。一个注解用在两套不同的API上，产生的方式不同 @RequestMapping("/testParam") public String testParam( @RequestParam("user_name") String username, String password, String[] hobby) { System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby)); /* 控制器方法的形参和我们当前的请求参数名如果不一致，获取不到数据 SpringMVC中为我们提供请求参数的注解@RequestParam()括号里书写对应的请求参数 @RequestParam()注解中有个方法required 自动装配设置成true的时候找不到就报错，false的时候找不到就用默认值给赋值 required指明@RequestParam请求参数是必须传输的，如果不传输就会报错400默认值为true. */ return "success"; }[note type="info flat"]@RequestParam注解中的required()默认值为true，如同Spring自动装配的设置一样，指明了括号中必须传递参数，如果没有参数传递，默认报错400。而如果required()修改为false，如同 @RequestParam(value = "user_name" ,required = false) String username 可以不用传递，不会报错，没有值情况下设置null(默认值)。[/note][note type="primary flat"]@RequestParam注解中还有一个参数就是defaultValue，不管required的属性值为true还是false。defaultValue设置了一个初始值，可以减少判断或者三元运算符。有了这个注解，可以解决请求参数和形参之间的关系，也可以在没有传递的情况下，或者传递空字符串的情况下，来设置默认值。[/note]@RequestHeader和@CookieValue@RequestHeader注解通过处理请求头信息和控制器方法形参的创建映射关系类比于RequestParam，该注解属性值也有三个value,required,defaultValue。 @RequestMapping("/testParam") public String testParam( @RequestParam(value = "user_name", required = false, defaultValue = "hehe") String username, String password, String[] hobby, //通过形参host获取当前请求的请求头信息 @RequestHeader(value = "Host", required = false, defaultValue ="") String host //请求头localhost:8080 ) { System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby)); System.out.println("host = " + host); return "success"; }两种会话技术：Session和Cookiesession依赖于cookie,cookie是客户端的会话技术，而session是服务器端的会话技术[note type="default flat"]Cookie会话技术，默认的声明周期是浏览器开启到浏览器关闭，只要浏览器不关闭Cookie将一直存在。在方法中添加 HttpSession session = request.getSession(); 第一次访问方法，Cookie应该存在于当前的响应报文中。当前第一次执行getSession方法的时候，会先检测一下请求报文中是否会携带JSESSIONID的Cookie。如果没有，说明当前会话是第一次会话，会创建一个HttpSession对象。将Session放在服务器所维护的Map集合中，并且创建一个Cookie，Cookie的键值固定，键是JSESSIONID，值是一个随机序列(和UUID很像)。并且将我们的HttpSession对象存储到我们当前的服务器维护的Map集合中，以及JSESSIONID值(随机序列)作为Map集合的键，把Session对象作为Map集合的值。存储在服务器内部，再把咱们的Cookie响应到浏览器。所以第一次执行getSession方法的时候，Cookie会存在于响应报文中，从此之后将存在于请求报文中。[/note][note type="default flat"]@CookieValue是将cookie数据和控制器方法的形参创建映射关系@CookieValue注解一共有三个属性：value、required、defaultValue，用法同@RequestParam[/note] @RequestMapping("/testParam") public String testParam( //defaultValue默认值 @RequestParam(value = "user_name", required = false, defaultValue = "hehe") String username, String password, String[] hobby, //通过形参host获取当前请求的请求头信息 @RequestHeader(value = "Host", required = false, defaultValue ="") String host , @CookieValue("JSESSIONID") String JSESSIONID //请求头localhost:8080 ) { /* 保证控制器方法的形参和我们当前的请求参数名保持一致 @{/testParam(username='admin',password=123456)} 可以自动将我们请求参数赋值给对应形参 */ //若请求参数中出现多个请求参数，可以在控制器方法的形参位置设置字符串类型，或字符串数组来接受此请求参数 //若使用字符串类型的形参，最终结果为请求参数的每一个值之间使用逗号(,)进行拼接的结果 System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby)); System.out.println("host : " + host); System.out.println("JSESSIONID : " + JSESSIONID); /* 控制器方法的形参和我们当前的请求参数名如果不一致，获取不到数据 SpringMVC中为我们提供请求参数的注解@RequestParam()括号里书写对应的请求参数 @RequestParam()注解中有个方法required 自动装配设置成true的时候找不到就报错，false的时候找不到就用默认值给赋值 required指明@RequestParam请求参数是必须传输的，如果不传输就会报错400默认值为true. */ return "success"; }通过实体类(POJO)获取请求参数数据库同样对应实体类对象，在页面中添加的数据跟实体类添加的属性也是对应的。直接在形参位置书写一个实体形参，保证请求参数的名字和实体类对象的属性名保持一致，就可以自动通过实体类对象收集请求参数。User类public class User { private Integer id; private String username; private String password; private Integer age; private String sex; private String email; /* 通过反射创建一个类，默认使用无参构造，建议生成有参构造的同时加上无参构造 这里使用PTG插件一键生成 */ public User() { } public User(Integer id, String username, String password, Integer age, String sex, String email) { this.id = id; this.username = username; this.password = password; this.age = age; this.sex = sex; this.email = email; } /** * 获取 * @return id */ public Integer getId() { return id; } /** * 设置 * @param id */ public void setId(Integer id) { this.id = id; } /** * 获取 * @return username */ public String getUsername() { return username; } /** * 设置 * @param username */ public void setUsername(String username) { this.username = username; } /** * 获取 * @return password */ public String getPassword() { return password; } /** * 设置 * @param password */ public void setPassword(String password) { this.password = password; } /** * 获取 * @return age */ public Integer getAge() { return age; } /** * 设置 * @param age */ public void setAge(Integer age) { this.age = age; } /** * 获取 * @return sex */ public String getSex() { return sex; } /** * 设置 * @param sex */ public void setSex(String sex) { this.sex = sex; } /** * 获取 * @return email */ public String getEmail() { return email; } /** * 设置 * @param email */ public void setEmail(String email) { this.email = email; } public String toString() { return "User{id = " + id + ", username = " + username + ", password = " + password + ", age = " + age + ", sex = " + sex + ", email = " + email + "}"; } } ParamController层 @RequestMapping("/testBean") public String testBean(User user){ /* 通过实体类获取参数 直接通过实体类JavaBean对象(POJO)作为形参 */ System.out.println(user); return "success"; }乱码问题在上面图中的sex性别出现了乱码问题，这里予以解决。把设置编码代码放在具体处理请求的方法中来设置，没有任何效果。如果设置编码之前已经获取了某个请求参数， [label color="red"]获取编码没有任何作用[/label] 。获取请求参数出现的乱码问题有两种：get请求的乱码如果form表单中将method请求方式修改为get方式，在使用Tomcat8.x情况下，那么不会出现乱码。get请求的乱码实际上是由Tomcat造成的，get请求乱码，需要去找到Tomcat的配置文件，在修改端口号的旁边添加对应属性 URlEncoding="UTF-8" 。(Tomcat8.x已经解决了该问题，有需自行百度)post请求的乱码如果form表单中将method请求方式修改为post方式，出现乱码，此时是真正需要添加设置编码的代码。如果需要设置响应编码，必须在xml文件中添加给forceResponseEncoding传递值true，因为该方法if判断中只有一个。添加web.xml中过滤器中添加编码 <!--最早初始化的顺序ServletContextListener > Filter > Servlet Filter的书写顺序再web.xml中无关 --> <filter> <filter-name>CharacterEncodingFilter</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>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 翻看参考的CharacterEncodingFilter源码的关键过滤方法我也放这里 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String encoding = this.getEncoding(); if (encoding != null) { if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) { request.setCharacterEncoding(encoding); } if (this.isForceResponseEncoding()) { response.setCharacterEncoding(encoding); } } filterChain.doFilter(request, response); }我书写这篇文章的初衷就是总结学习的进度，遗忘之际可以拿出来翻看，如有不对的地方还望指正，多多海涵。