入门案例
导入依赖时spring-webmvc需要是5.3版本以下的
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.14.RELEASE</version>
</dependency>
</dependencies>
控制层
package com.changGe.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MVCController {
@RequestMapping("/save")
@ResponseBody
public String test(){
return "{'model':'servlet'}";
}
}
springMVC仍然属于Spring中的技术
package com.changGe.configs;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.changGe.controller")
public class SpringMVCConfig {}
配置SpringMVC的容器能够被tomcat检测到
package com.changGe.configs;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringMVCConfig.class);
return context;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
工作流程
先将springMVC的配置加载到tomcat容器中,再根据路径去对应的MVC注册中找到方法执行.
web容器是先将所有的路径都注册进ServletContext,再定义所有的请求都由SpringMVC来处理.

控制bean的加载
@ComponentScan(value = "com.changGe",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
简化Servlet容器配置
controller里的bean是由SpirngMVC加载的
mybatis是自动代理
简化Servlet上下文容器配置
package com.changGe.configs;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMVCConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
请求:发送参数
普通参数
post请求参数属于请求体内
请求体中参数form-data可以发送文件

public Filter[] getServletFilters(){
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
@RequestMapping(value = "/save",produces = "text/json;charset=utf-8")
@ResponseBody
public String test(String name){
System.out.println(name);
return name;
}
pojo参数
SpringMVC中是先创建参数对象,然后通过对象的变量名和传参名一一对应匹配
当User对象中嵌套了Student对象时
public String test(User user){
Student student = user.getStudent();
return student.getName();
}
@ResponseBody
public String test(@RequestParam List likes){
return likes.toString();
}
public String test(String[] likes){
String s = "";
for (String like : likes) {
s += like;
}
return s;
}
日期参数格式转换
ctlr+alt+b查看当前类的所有子类
java默认是用Converter的子类来做数据格式转换的
package org.springframework.core.convert.converter;
import org.springframework.lang.Nullable;
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S source);
}
public String test(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")Date date){
return date.toString();
}
@EnableWebMvc
public class SpringConfig {}
响应
ResponseBody:当当前控制器返回值作为响应体
@RequestMapping(value = "/save")
public String test(){
return "index.jsp";
}
当复杂类型返回
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
@RequestMapping(value = "/save")
@ResponseBody
public List<Object> test(){
List<Object> arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
return arrayList;
}
@ResponseBody由它的子类的类型转换器实现
package org.springframework.http.converter;
public interface HttpMessageConverter <T> {
RESTful:资源访问形式
json数据载体
<script>
var value = {
name:"长歌",
addr:["长安","洛阳","海口"]
}
alert(value.addr[2]);
var json = {
"name":"长歌",
"addr":["长安","洛阳","海口"]
}
alert(json.addr[2]);
</script>
和java对象转换
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
FastJson是根据JavaBean 规范来写的,他是通过反射获得对象的get() 方法来获取对象属性的值
@Data
@AllArgsConstructor
public class User {
private String name;
private String[] addr;
}
User user = new User("长歌",new String[]{"长安","洛阳","海口"});
String s = JSON.toJSONString(user);
user = JSON.parseObject(s,User.class);
web核心内容
B/S架构:Browser/Server
http协议
请求与响应格式

HTTP 状态码

-
get:查询 -
post:新增 -
put:修改 -
delete:删除
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
@ResponseBody
public int test(@PathVariable Integer id){
return id;
}
接收多个变量(对象)
@RequestMapping(value = "/users",method = RequestMethod.POST)
@ResponseBody
public User test(@RequestBody User user){
return user;
}
raw可以规定上传任何格式的信息
 
- Requestparam:接收前端路径参数
- RequestBody:接收前端请求体
- Pathvariable:接收前端变量
简化开发
@RestController
@RequestMapping("/users")
public class MVCController {
@PostMapping
public User test(@RequestBody User user){
return user;
}
过滤放行
不要忘记更新SpringMVC配置类的扫描路径
package com.changGe.configs;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class MVCHandlerConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
}
Axios:封装后的Ajax
在线cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios({
method:"post",
url:"http://localhost:8080/users/",
data:{'name':'长歌','age':'18'}
}
).then(function (response){
alert(JSON.stringify(resp.data));
})
简化
axios.post("http://localhost:8080/users/",{'name':'长歌','age':'18'}).then(function (resp){
alert(JSON.stringify(resp.data));
})
箭头函数
axios.post("http://localhost:8080/users/",{'name':'长歌','age':'18'}).then(resp=>{
alert(JSON.stringify(resp.data));
})
整合
spirngMVC的容器里包含了Spring容器
fastjson会自动把请求过来的json数据作编码过滤
开发习惯
service的方法要见名知意,有文档说明.向增删改要有boolean返回值
表现层与前端数据传输协议
结果类
package com.changGe.utils;
import lombok.Data;
@Data
public class Result {
private Object data;
private String msg;
private Integer code;
public Result(Integer code,Object data, String msg) {
this.data = data;
this.msg = msg;
this.code = code;
}
}
定义状态码
package com.changGe.domain;
public class Code {
public static final Integer GETALL_OK = 20011;
public static final Integer GETALL_ERR = 20010;
}
测试
@GetMapping()
public Result findAllStudent(){
List<Student> studentAll = studentService.findStudentAll();
Integer code = studentAll != null ? GETALL_OK : GETALL_ERR;
String message = studentAll != null ? null : "查询失败,请检查参数";
return new Result(code,studentAll,message);
}
前端接收的结果
{
"data": [
{
"name": "admin",
"id": 1,
"age": 18,
"money": 1000.0
},
{
"name": "user",
"id": 2,
"age": 20,
"money": 1000.0
}
],
"msg": null,
"code": 20011
}
异常处理
一般是将异常不断向上抛,最后抛到表现层,由Spring的AOP思想来做总的异常处理返回前端
package com.changGe.controller;
import com.changGe.utils.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
public Result advice(Exception e){
return new Result(10,null,e.getMessage());
}
}
运行时异常可以不处理,直接抛
项目异常处理
将项目异常分为用户级和系统级两大类
先自定义异常
package com.changGe.exception;
import lombok.Data;
@Data
public class SystemException extends RuntimeException{
private Integer code;
public SystemException(Integer code) {
this.code = code;
}
public SystemException(Integer code,String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
package com.changGe.exception;
public class BusinessException extends RuntimeException{
private Integer code;
public BusinessException(Integer code) {
this.code = code;
}
public BusinessException(Integer code,String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
业务层捕捉异常,变成我们的自定义异常,抛给表现层
public List<Student> findStudentAll() {
try{
int i = 1/0;
return studentDao.getAll();
}catch (Exception e){
throw new SystemException(Code.SYSTEM_ERR,"系统超时,请等待",e);
}
}
Spring异常处理类处理对应的异常
package com.changGe.controller;
import com.changGe.domain.Code;
import com.changGe.exception.SystemException;
import com.changGe.utils.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
public Result advice(Exception e){
return new Result(Code.SYSTEM_UNKOWN_ERR,null,"系统繁忙,请稍候再试");
}
@ExceptionHandler(SystemException.class)
public Result systemException(SystemException e){
return new Result(e.getCode(),null,e.getMessage());
}
}
案例:数据添加和遍历
springMVC启动之后默认打开的首页是webapp/index.jsp。位置和页面名称都不能错,否则将报404错误。
<div id="app">
<input v-model:name="name" value="name">
<input v-model:name="id" value="id">
<input v-model:name="age" value="age">
<input v-model:name="money" value="money">
<input type="submit" value="提交" @click="show()">
</div>
<script>
new Vue({
el:"#app",
data(){
return{
name:"",
id:"",
age:"",
money:""
}
},
methods:{
show(){
axios.post("http://localhost:8080/students",{name:this.name,id:this.id,age:this.age,money:this.money})
.then(resp=>{
alert(resp.data.msg);
location.href = "html/showList.html"
})
}
}
})
</script>
<div id="app">
<table>
<tr v-for="(stu,index) in students" :key="index">
<th>{{index}}</th>
<td>{{stu.name}}</td>
<td>{{stu.id}}</td>
<td>{{stu.age}}</td>
<td>{{stu.money}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el:"#app",
data(){
return{
students:[]
}
},
created(){
axios.get("http://localhost:8080/students").then(resp=>{
this.students = resp.data.data;
console.log('y',this.students);
})
}
})
</script>
视图解析器
package com.changGe.configs;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class ViewHandler extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index.html");
}
}
修改
使用的是springmvc_11_page官方项目
把黑马的vue综合案例页面拿过来,然后已经查询,分页还是以前的代码,现在是只做修改和删除功能.
自定义修改的弹窗
dialogUpdate:false,
修改的弹框用的和新增的一样,只不过需要重新绑定数据为自定义的dialogUpdata
<el-dialog
title="编辑品牌"
:visible.sync="dialogUpdate"
width="30%"
>
<el-form ref="form" :model="brand" label-width="80px">
<el-form-item label="品牌名称">
<el-input v-model="brand.brandName"></el-input>
</el-form-item>
<el-form-item label="企业名称">
<el-input v-model="brand.companyName"></el-input>
</el-form-item>
<el-form-item label="排序">
<el-input v-model="brand.ordered"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input type="textarea" v-model="brand.description"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="brand.status"
active-value="1"
inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleUpdate(scope.row)">提交</el-button>
<el-button @click="dialogUpdate = false">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
点击修改时展示弹窗并调用方法
<el-button type="primary" @click="dialogUpdate = true,handleUpdate(scope.row)">修改</el-button>
brand:[{
brandName: '华为',
companyName: '华为科技有限公司',
ordered: '100',
status: "1",
id:"3",
description: ""
}],
tableData: [{
brandName: '华为',
companyName: '华为科技有限公司',
ordered: '100',
status: "1",
id:"3"
}, {
brandName: '华为',
companyName: '华为科技有限公司',
ordered: '100',
status: "1"
}]
把原先表格中的默认数据tableData全部换成真实数据brand
<template>
<el-table
:data="brand"
style="width: 100%"
:row-class-name="tableRowClassName"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
type="index"
width="50">
</el-table-column>
<el-table-column
prop="brandName"
label="品牌名称"
align="center"
>
</el-table-column>
<el-table-column
prop="companyName"
label="企业名称"
align="center"
>
</el-table-column>
<el-table-column
prop="ordered"
align="center"
label="排序">
</el-table-column>
<el-table-column
prop="statusStr"
align="center"
label="当前状态">
</el-table-column>
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-button type="primary" @click="dialogUpdate = true,handleUpdate(scope.row)">修改</el-button>
<el-button type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
这样个row里面存储了当前行的所有信息,包括brand.id
handleUpdate(row) {
axios.get("/books/"+row.id).then((res)=>{
if(res.data.code == 20041){
this.brand = res.data.data;
this.dialogUpdate = true;
}else{
this.$message.error(res.data.msg);
}
});
},
他们自己的写法
handleUpdate(row) {
axios.get("/books/"+row.id).then(resp=>{
if(resp.data.code == 20041){
this.dialogFormVisible4Edit = true;
this.formData = resp.data.data;
}else {
this.$message.error(resp.data.msg);
}
})
},
handleEdit() {
axios.put("/books",this.formData).then(resp=>{
if(resp.data.code == 20031){
this.$message.success("修改成功");
this.dialogFormVisible4Edit = false;
}else {
this.$message.success("修改失败");
}
}).finally(()=>{
this.getAll();
})
},
删除
handleDelete(row) {
this.$confirm("确认删除吗","提示",{
type: 'info'
}).then(()=>{
axios.delete("/books/"+row.id).then(resp=>{
if(resp.data.code == 20021){
this.$message.success("删除成功");
}else {
this.$message.success("删除失败");
}
}).finally(()=>{
this.getAll();
})
}
).catch(()=>{
this.$message.info("取消删除")
})
}
拦截器
浏览器发送请求后,tomcat在访问资源前,会先经过过滤器再进入spring,然后中央控制器调用controller,由controller响应给前端

拦截器可以==前后都拦截==
filter是所有请都可以过滤,而拦截器只对spirngmvc管理的资源生效
java中权限修饰符default的使用讲解和类优先于接口的规则
https://www.cnblogs.com/east7/p/15941584.html#:~:text=default%E5%85%B3%E9%94%AE%E5%AD%97%EF%BC%9A%E6%98%AF%E5%9C%A8java%208%E4%B8%AD%E5%BC%95%E5%85%A5%E7%9A%84%E6%96%B0%E6%A6%82%E5%BF%B5%EF%BC%8C%E4%B9%9F%E5%8F%AF%E7%A7%B0%E4%B8%BAVirtual,extension%20methods%E2%80%94%E2%80%94%E8%99%9A%E6%8B%9F%E6%89%A9%E5%B1%95%E6%96%B9%E6%B3%95%E4%B8%8Epublic%E3%80%81private%E7%AD%89%E9%83%BD%E5%B1%9E%E4%BA%8E%E4%BF%AE%E9%A5%B0%E7%AC%A6%E5%85%B3%E9%94%AE%E5%AD%97%EF%BC%8C%E4%B8%8E%E5%85%B6%E5%AE%83%E4%B8%A4%E4%B8%AA%E5%85%B3%E9%94%AE%E5%AD%97%E4%B8%8D%E5%90%8C%E4%B9%8B%E5%A4%84%E5%9C%A8%E4%BA%8Edefault%E5%85%B3%E9%94%AE%E5%AD%97%E5%A4%A7%E9%83%A8%E5%88%86%E9%83%BD%E7%94%A8%E4%BA%8E%E4%BF%AE%E9%A5%B0%E6%8E%A5%E5%8F%A3%E3%80%82%20default%E4%BF%AE%E9%A5%B0%E6%96%B9%E6%B3%95%E6%97%B6%E5%8F%AA%E8%83%BD%E5%9C%A8%E6%8E%A5%E5%8F%A3%E7%B1%BB%E4%B8%AD%E4%BD%BF%E7%94%A8%EF%BC%8C%E5%9C%A8%E6%8E%A5%E5%8F%A3%E4%B8%AD%E8%A2%ABdefault%E6%A0%87%E8%AE%B0%E7%9A%84%E6%96%B9%E6%B3%95%E5%8F%AF%E4%BB%A5%E7%9B%B4%E6%8E%A5%E5%86%99%E6%96%B9%E6%B3%95%E4%BD%93%EF%BC%8C%E8%80%8C%E6%97%A0%E9%9C%80%E4%BF%AE%E6%94%B9%E6%89%80%E6%9C%89%E5%AE%9E%E7%8E%B0%E4%BA%86%E6%AD%A4%E6%8E%A5%E5%8F%A3%E7%9A%84%E7%B1%BB%E3%80%82
入门案例
实现HandlerInterceptor,其中的三个方法都被default修饰了
把包建在和controller同级,springMVC就不用再扫描了
package com.changGe.controller.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("handler就是被拦截方法的Method对象");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("modelAndView可以设置springMVC响应时跳转页面");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("ex对象可以获取被拦截方法中出现的异常");
}
}

你也可以额外写个类extends WebMvcConfigurationSupport来实现效果
@Configuration
@ComponentScan("com.changGe.controller")
public class SpringMVCConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor interceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("/students","/students/*");
}
}
注意:WebMvcConfigurer和WebMvcConfigurationSupport不能同时同时出现,会冲突,造成拦截器没反应
拦截器链
多拦截器执行顺序

MyInterceptor1的preHandle return false;
运行结果:
pre pre1 after
@Autowired
private MyInterceptor1 interceptor1;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("/students","/students/*");
registry.addInterceptor(interceptor1).addPathPatterns("/students","/students/*");
|