1 项目搭建
1.1 运行原理
Servlet容器中的SpringMVC核心前端控制器DispatcherServlet会拦截所有请求,将拦截的请求与@RequestMapping进行匹配,将请求交给匹配的Controller方法进行处理
1.2 创建web容器
初始化Servlet容器(web容器),用web配置类的方式替代web.xml(创建一个类继承AbstractDispatcherServletInitializer并重写方法)
/**
* 初始化Servlet容器,即web容器(替代web.xml)
*/
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
/**
* 加载SpringMVC对应的容器对象
* SpringMVCIOC容器是SpringIOC的子容器(子容器可以引用父容器,反之则不行)
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringConfig.class);
return context;
}
/**
* 哪些请求归SpringMVC处理
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};//表示所有请求都贵SpringMVC处理
}
/**
* 加载Spring对应的容器对象
* 可以将Spring和SpringMVC的容器对象合并成只有SpringMVC容器对象,因此此方法可以返回null即可
*/
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
简化开发(创建一个类继承AbstractAnnotationConfigDispatcherServletInitializer )
/**
* 初始化Servlet容器,即web容器(替代web.xml)
*/
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 加载SpringMVC对应的容器对象
* SpringMVCIOC容器是SpringIOC的子容器(子容器可以引用父容器,反之则不行)
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 被Servlet容器拦截的哪些请求归SpringMVC处理
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 加载Spring对应的容器对象
* 可以将Spring和SpringMVC的容器对象合并成只有SpringMVC容器对象,因此此方法可以返回null即可
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
/**
* 配置过滤器
* 编码过滤器(过滤器会优先拦截器执行):解决post乱码问题
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return new Filter[]{filter};
}
/**
* 配置文件上传解析器
* 不是用的:org.springframework.web.multipart.commons.CommonsMultipartResolver(额外导包commons-fileupload)
* 而是用的:org.springframework.web.multipart.support.StandardServletMultipartResolver(SpringMVC自带)
*/
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
super.customizeRegistration(registration);
String location = "";
long maxFileSize = 20971520; //文件大小限制byte(20M)
long maxRequestSize = 41943040; //请求大小限制byte(40M)
int fileSizeThreshold = 0; //文件大小阈值
registration.setMultipartConfig(new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold));//配置对multipart的支持
}
}
1.3 常用注解
分类 | 注解 | 说明 |
@RequestMapping(path="/path",method=RequestMapping.POST) | 映射请求(用在"类/方法"上) @GetMapping=@RequestMapping(method=RequestMapping.GET) @PostMapping @DeleteMapping @PutMapping | |
接收 | @RequestHeader("请求头key") | 接收请求头中的参数 |
@RequestParam("userNmae") String name | 给Controller类方法形参起别名 | |
@DatetimeFormat(pattern="yyyy-MM-dd HH:mm:ss:SSS") | 可以用在pojo类的属性上,将属性接收的对应格式的字符串转换为Date 也可以用在Controller方法形参前,接收对应格式的字符串转换为Date | |
@RequestBody User user | 接收POST请求体中的JSON字符串,并封装到JavaBean中 | |
@PathVariable | RESTful方式,接收"路径变量" | |
响应 | @ResponseBody | 将"类/方法"返回的数据转换为JSON对象 |
@RestController | 用在类上,相当于@Controller+@ResponseBody |
注解选择:
请求参数有多个时,将多个参数写成json格式,后台以json格式接收,@RequestBody使用广泛
请求参数非json时,用@RequestParam接收
请求参数通常只有1个时,可以用@PathVariable接收
1.4 代码示例
@Controller
public class TeacherController {
private static final Logger log = LoggerFactory.getLogger(TeacherController.class);
@Autowired
private TeacherService userService;
@RequestMapping("/save")
@ResponseBody
public Teacher save(@RequestBody Teacher teacher) {
teacher.setName("王五");
teacher.setAge(44);
teacher.setPosition("英语老师");
log.info(String.valueOf(teacher));
return teacher;
}
}
2 REST风格
REST(Representational State Transfer)表现层资源状态转换
RESTful按照REST风格访问资源
2.1 资源访问对比
(1) 传统风格
http://localhost/user/getById?id=1 //查询用户(GET请求)
http://localhost/user/deleteById?id=1 //删除用户(GET请求)
http://localhost/user/addUser //新增用户(POST请求)
http://localhost/user/updateUser //修改用户(POST请求)
(2) REST风格
http://localhost/users/1 //查询用户或删除用户
http://localhost/users //添加用户或修改用户
REST风格通常在的映射地址后加s(此s不是复数形式,而是style的缩写,例如users)
REST风格优点:书写简化,隐藏访问路径(更加安全)
2.2 实现原理
REST风格访问资源是用行为动作进行区分,可以用同一个请求路径,实现不同的功能
http://localhost/users/1 GET方式(查询),DELETE方式(删除)
http://localhost/users POST方式(新增),PUT方式(修改)
2.3 示例代码
前端代码:
addTeacher() {
axios.post("/teachers", {
position: '体育老师',
name: '赵四',
age: 54
}, {
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
alert(response.data)
})
},
updateTeacher() {
axios.put("/teachers", {
position: '物理老师',
name: '龙武',
age: 77
}, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
alert(response.data)
})
},
deleteTeacher(){
axios.delete("/teachers/lisa/66", {
}, {
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
alert(response.data)
})
},
queryTeacher(){
axios.get("/teachers/gods", {
}, {
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
alert(response.data)
})
},
queryAllTeacher(){
axios.get("/teachers", {
}, {
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
alert(response.data)
})
}
后台代码:
@RestController
@RequestMapping("/teachers")
public class TeacherController {
@Autowired
private TeacherService teacherService;
private static final Logger log = LoggerFactory.getLogger(TeacherController.class);
@PostMapping
public String add(@RequestBody Teacher teacher) {
log.info("新增teacher信息为:" + teacher);
return "{'info':'add success'}";
}
@PutMapping
public String update(@RequestParam String position, @RequestParam String name, @RequestParam Integer age) {
Teacher teacher = new Teacher();
teacher.setPosition(position);
teacher.setName(name);
teacher.setAge(age);
log.info("修改teacher为:" + teacher);
return "{'info':'update success'}";
}
/**
* {name}将"请求路径"倒数第二个作为路径变量
* {age}将"请求路径"最后一个作为路径变量
*
* @PathVariable 接收名为id或age的路径变量
*/
@DeleteMapping("/{name}/{age}")
public String delete(@PathVariable String name, @PathVariable Integer age) {
log.info("删除name为" + name + ",age为" + age + "的teacher");
return "{'info':'delete success'}";
}
@GetMapping("/{name}")
public String query(@PathVariable String name) {
log.info("查询name为" + name + "的teacher");
return "{'info':'query success'}";
}
@GetMapping
public String queryAll() {
log.info("查询全部的teacher");
return "{'info':'query all success'}";
}
}
3 放行访问
因为所有的请求都会被Servlet容器拦截,而通过web配置类设置的又是将所有拦截的请求交给SpringMVC去处理,导致即使访问一些静态资源(html,css,js,img等)也会被拦截后交给SpringMVC处理,而SpringMVC通过请求路径映射又找不到对应的@RequestMaping,就会报404错误
我们需要的是访问接口交给SpringMVC映射处理,访问静态资源放行,因此需要一个SpringMVESupport配置类继承WebMvcConfigurationSupport类,并重写addResourceHandlers方法
/**
* 放行资源访问
* 此类需要被SpringIOC容器扫描到
*/
@Configuration //注意此类虽然用@Configuration注解了,但要区别于Spring容器的@Configuration,所以不能在Spring配置类中用@Import引入这个类,也不能Spring配置类自己继承WebMvcConfigurationSupport,而只能单独使用这个类并加上@Configuration
public class SpringMVCSupportConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//被servlet拦截的"请求路径"如果是/html/**,不要被交给MVC处理,而是交给Tomcat处理(将/html/**请求放行,并用html目录下的资源去匹配)
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
registry.addResourceHandler("/index.html").addResourceLocations("/index.html");
}
}