目录
1 SpringMVC处理Ajax请求
1.1 @RequestBody
1.2 @RequestBody获取json格式的请求参数
1.3 @ResponseBody
1.4、@ResponseBody响应浏览器json数据
1.5 @RestController注解
2 文件上传和下载
2.1 文件下载
2.2 上传文件
3 拦截器
3.1 拦截器的三个抽象方法
3.2 拦截器的配置
3.3 多个拦截器的执行顺序
4 异常处理器
4.1 基于配置的异常处理
4.2 基于注解的异常处理
5 注解配置SpringMVC
5.1 创建初始化类代理web.xml
5.2 创建SpringConfig配置类,代替spring的配置文件
5.3 创建WebConfig配置类,代替SpringMVC的配置文件
1 SpringMVC处理Ajax请求
1.1 @RequestBody
<form th:action="@{/test/requestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
@RequestMapping(value = "/test/requestBody",method = RequestMethod.POST)
public String testRequesttBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
一个注解就能获得请求体的信息非常的方便。不过这里使用中文会发生乱码,暂时不知道啥情况
1.2 @RequestBody获取json格式的请求参数
在使用了axios发送ajax请求之后,浏览器发送到服务器的请求参数有两种格式:
(1)name=value&name=value...,此时的请求参数可以通过request.getParameter()获取,对应 SpringMVC中,可以直接通过控制器方法的形参获取此类请求参数
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
第二步:SpringMVC的配置文件中设置开启mvc的注解驱动
<mvc:annotation-driven/>
第三步:在控制器方法的形参位置,设置json格式的请求参数要转换成的java类型(实体类或map)的参数,并使用@RequestBody注解标识
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<div id="app">
<h1>index.html</h1>
<input type="button" value="使用@RequestBody注解处理json格式的请求参数" @click="testRequestBody()"><br>
<a th:href="@{/test/ResponseBody}">测试@ResponseBody注解响应浏览器数据</a><br>
<input type="button" value="使用@ResponseBody注解响应json格式的数据" @click="testResponseBody()"><br>
</div>
<script type="text/javascript" th:src="@{/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/js/axios.min.js}"></script>
<script type="text/javascript">
/**
* axios({
url:"",//请求路径
method:"",//请求方式
//以name=value&name=value的方式发送的请求参数
//不管使用的请求方式是get或post,请求参数都会被拼接到请求地址后
//此种方式的请求参数可以通过request.getParameter()获取
params:{},
//以json格式发送的请求参数
//请求参数会被保存到请求报文的请求体传输到服务器
//此种方式的请求参数不可以通过request.getParameter()获取
data:{}
}).then(response=>{
console.log(response.data);
});
*/
var vue = new Vue({
el:"#app",
methods:{
testRequestBody(){
axios.post(
"/springmvc/test/RequestBody/json",
{username:"admin",password:"123456",age:23,gender:"男"}
).then(response=>{
console.log(response.data);
});
},
testResponseBody(){
axios.post("/springmvc/test/ResponseBody/json").then(response=>{
console.log(response.data);
});
}
}
});
</script>
</body>
</html>
需要注意的是要想访问我们复制过来的js等静态文件需要在springmvc配置文件假如配置:
<mvc:default-servlet-handler />
<mvc:annotation-driven/>
因为:配置默认的servlet处理静态资源,当前工程的web.xml配置的前端控制器DispatcherServlet的url-pattern是/。tomcat的web.xml配置的DefaultServlet的url-pattern也是/ 。此时,浏览器发送的请求会优先被DispatcherServlet进行处理,但是DispatcherServlet无法处理静态资源。若配置了<mvc:default-servlet-handler />,此时浏览器发送的所有请求都会被DefaultServlet处理。若配置了<mvc:default-servlet-handler />和<mvc:annotation-driven /> 浏览器发送的请求会先被DispatcherServlet处理,无法处理在交给DefaultServlet处理。
@RequestMapping(value = "/test/RequestBody/json")
public void testRequestBody(@RequestBody String requestBody, HttpServletResponse response) throws IOException {
System.out.println(requestBody);
response.getWriter().write("hello,ajax");
}
这是使用一个字符串类型参数接收请求体,我们可以使用类来接收。
public void testRequestBody(@RequestBody User user, HttpServletResponse response) throws IOException {
System.out.println(user);
response.getWriter().write("hello,RequestBody");
}
1.3 @ResponseBody
我们之前控制器方法返回值是会跳转到一个页面如下:
<a th:href="@{/test/ResponseBody}">测试@ResponseBody注解响应浏览器数据</a><br>
@RequestMapping("/test/ResponseBody")
public String testResponseBody(){
//此时会跳转到逻辑视图success所对应的页面
return "success";
}
1.4、@ResponseBody响应浏览器json数据
public User testResponseBodyJson(){
User user = new User(1001, "admin", "123456", 20, "男");
return user;
}
我们可以响应实体类,也可以响应list集合和map集合:
public List<User> testResponseBodyJson(){
User user1 = new User(1001, "admin1", "123456", 20, "男");
User user2 = new User(1002, "admin2", "123456", 20, "男");
User user3 = new User(1003, "admin3", "123456", 20, "男");
List<User> list = Arrays.asList(user1, user2, user3);
return list;
}
public Map<String, Object> testResponseBodyJson(){
User user1 = new User(1001, "admin1", "123456", 20, "男");
User user2 = new User(1002, "admin2", "123456", 20, "男");
User user3 = new User(1003, "admin3", "123456", 20, "男");
Map<String, Object> map = new HashMap<>();
map.put("1001", user1);
map.put("1002", user2);
map.put("1003", user3);
return map;
}
1.5 @RestController注解
2 文件上传和下载
2.1 文件下载
<a th:href="@{/test/down}">下载图片</a>
@RequestMapping("/test/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws
IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("img");
realPath = realPath + File.separator + "1.jpg";
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
以上代码可以当做一个模板,需要下载别的文件时,我们只要改一下路径即可。
2.2 上传文件
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
第二步:在SpringMVC的配置文件中添加配置:
这里一定要配置id名称而且必须是这个名称
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
第三步:控制器方法和前端页面
<form th:action="@{/test/up}" method="post" enctype="multipart/form-data">
头像:<input type="file" name="photo"><br>
<input type="submit" value="上传">
</form>
@RequestMapping("/test/up")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//获取上传的文件的后缀名
String hzName = fileName.substring(fileName.lastIndexOf("."));
//获取uuid
String uuid = UUID.randomUUID().toString();
//拼接一个新的文件名
fileName = uuid + hzName;
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取当前工程下photo目录的真实路径
String photoPath = servletContext.getRealPath("photo");
//创建photoPath所对应的File对象
File file = new File(photoPath);
//判断file所对应目录是否存在
if(!file.exists()){
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
//上传文件
photo.transferTo(new File(finalPath));
return "success";
}
注意我们用以下代码拼接一个新的文件名,因为假如我们上传的文件名相同的文件,它会覆盖前面的文件,我们可以手动将文件名修改:
3 拦截器
3.1 拦截器的三个抽象方法
- preHandle():在控制器方法执行之前执行,其返回值表示对控制器方法的拦截(false)或放行(true)
- postHandle():在控制器方法执行之后执行
- afterCompletion():在控制器方法执行之后,且渲染视图完毕之后执行
我们创建一个类实现这三个方法
package com.itzw.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
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");
}
}
3.2 拦截器的配置
<mvc:interceptors>
<bean class="com.itzw.interceptor.FirstInterceptor"/>
</mvc:interceptors>
第二种方式:在外面配置bean然后再引入
<bean id="interceptor" class="com.itzw.interceptor.FirstInterceptor"/>
<mvc:interceptors>
<!--<bean class="com.itzw.interceptor.FirstInterceptor"/>-->
<ref bean="interceptor"/>
</mvc:interceptors>
第三种方式:不在外面配置bean,直接使用注解,控制加扫描的方式
<mvc:interceptors>
<!--<bean class="com.itzw.interceptor.FirstInterceptor"/>-->
<ref bean="firstInterceptor"/>
</mvc:interceptors>
值得注意的是:bean和ref标签所配置的拦截器默认对DIspatcherServlet处理的所有的请求进行拦截,即使是不存在路径也会拦截,那我要是不想拦截某个路径呢?
进行如上配置,如果配置的是“/*”则表示拦截所有一层目录比如“/x”,但是“/x/y”就不会拦截了。
3.3 多个拦截器的执行顺序
4 异常处理器
4.1 基于配置的异常处理
编写异常处理器:
其中key的值表示处理器方法执行过程中出现的异常,而它的值表示出现异常时跳转到指定视图
我们还可以通过配置输出异常信息:
<property name="exceptionAttribute" value="ex"></property>
<p th:text="${ex}"></p>
4.2 基于注解的异常处理
我们需要单独写一个类来代替配置:
package com.itzw.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ExceptionTest {
@ExceptionHandler(ArithmeticException.class)
public String test(Model model, Exception ex){
model.addAttribute("ex",ex);
return "error";
}
}
@ControllerAdvice将当前类标识为异常处理器的组件;@ExceptionHandler用于设置所表示方法处理的异常;ex表示当前请求处理中出现的异常对象
5 注解配置SpringMVC
5.1 创建初始化类代理web.xml
package com.itzw.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定Spring的配置类
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定SpringMVC的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,即url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 设置当前的过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
//创建编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
//创建处理请求方式的过滤器
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};
}
}
5.2 创建SpringConfig配置类,代替spring的配置文件
package com.itzw.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
}
这个以后需要我们再详细配置,之前我们学spring的时候就使用过这个。@Configuration的作用是将类标识为配置类
5.3 创建WebConfig配置类,代替SpringMVC的配置文件
SpringMVC配置文件配置的内容包括:扫描组件、视图解析器、默认的servlet、mvc的注解驱动 、视图控制器、文件上传解析器、拦截器、异常解析器
我们一个个配置:
首先我们实现接口WebMvcConfigurer
注解@EnableWebMvc用来开启mvc注解驱动,对应配置文件的<mvc:annotation-driven/>
注解@ComponentScan用来扫描组件,对应配置文件的<context:component-scan base-package="com.itzw"/>
package com.itzw.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
//扫描组件
@ComponentScan("com.itzw.controller")
//开启mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
}
我们再重写这个接口的方法实现其它功能:
配置默认的servlet处理静态资源(css、js等资源),固定配置如下:
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
对应配置文件的:<mvc:default-servlet-handler />
配置视图解析器:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
对应配置文件的:<mvc:view-controller path="/" view-name="index"/>
配置文件上传解析器:固定写法
@Bean
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
这个方法需要我们自己写,其中@Bean注解可以将表示的方法的返回值作为bean进行管理,bean的id为方法的方法名。对应配置文件中的:
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
配置拦截器:
配置拦截器首先我们要有个拦截器,还记得拦截器怎么创建吗?
package com.itzw.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
然后配置拦截器:
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
//拦截所有路径
registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}
对应的配置文件为:
<mvc:interceptors>
<!--<bean class="com.itzw.interceptor.FirstInterceptor"/>-->
<ref bean="firstInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/a/b"/>
<ref bean="firstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
配置异常解析器:
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException","error");
exceptionResolver.setExceptionMappings(prop);
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}
对应配置文件:
<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>
配置视图解析器:
//配置生成模板解析器
@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;
}
//生成模板引擎并为模板引擎注入模板解析器
@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;
}
对应的配置文件是:
<!-- 配置Thymeleaf视图解析器 -->
<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>
这样就配置完成了,之前我们遇到的需要配置的东西都用注解的方式实现了。
下面我们就可以将SSM三个框架整合在一起了。