快速掌握Springmvc0.为什么需要使用框架 框架类似于Java代码的模板 它已经完成了一部分(项目中的通用规范 接
0.为什么需要使用框架
框架类似于Java代码的模板 它已经完成了一部分(项目中的通用规范 接口 通用功能 ) 编程人员只需要根据框架的使用规则 添加需要 就可以快速的写一个符合开发标准的项目 提高效率
1.什么是springmvc
谈谈你对springmvc理解
springmvc是spring框架的一个子项目
,底层实现DispatcherServlet
可以实现项目中控制层
的功能(替换之前的Servlet)相比Servlet效率和性能都更高 并且springmvc和spring是无缝连接的
2.springmvc搭建步骤
-
创建maven项目
-
导入依赖 (jar) pom.xml
-
配置springmvc配置文件
//1.配置控制层扫描包:扫描@Controller注解,扫描到了就会创建该类的对象 <context:component-scan base-package="com.sceen.controller"/> //2.开启注解驱动:让@RequestMapping生效 <mvc:annotation-driven/> //3.配置上传组件:虽然要保证唯一 这个id必须特殊 必须叫multipartResolver //一个bean表示java一个可重用组件(对象) <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> //设置上传文件的编码 <property name="defaultEncoding" value="utf-8"/> //设置上传文件的大小 单位字节10M=1024*1024*10 <property name="maxInMemorySize" value="10240000"/> </bean> //4.springmvc会默认拦截静态资源(img css js) 需要让其放行 <mvc:default-servlet-handler/>
-
配置web.xml (配置springmvc核心)
//springmvc提供编码过滤器 <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> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/ *</url-pattern> </filter-mapping> //springmvc核心 DispatcherServlet <servlet> <servlet-name>springmvc</servlet-name> //作用:1.作为springmvc入口,所有请求都需要经过这个Servlet才//能到达Springmvc //作用:2.负责读取springmvc配置文件,默认读取WEB-INF下的文件 文件名固定好了:servlet名称(springmvc)-servlet.xml 通常配置文件 需要单独设置 不使用它默认的方式 <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>springmvc</servlet-name> //把进入控制层的请求配置好即可 不能写/* 因为包含 *.jsp *.css *.jpg 进入控制层请求:/user /email 只需要接收不带后缀的请求 / <url-pattern>/</url-pattern> </servlet-mapping>
-
随便写个类 随便写个方法 通过几个简单的注解 就可以接收请求处理请求
3.mvc三层架构
-
M:model 模型层 主要用于处理业务逻辑(service)和数据访问(Dao) service也叫业务模型 Dao也叫数据模型
-
service:业务逻辑层 相当于一个功能 一个线程 专门用于处理事务,因为以后的功能会很复杂 可能会包括多次数据库操作 需要保证同时成功失败
前端 用户实现用户下单功能: 用户选择好商品 选择好数量 下单 controller-- 接收下单的信息(商品信息 数量 价格 当前用户 ...) service-- 实现一个下单业务功能(包含5次jdbc操作 会调用5次dao层) 要保证这个5次dao层操作同时成功和失败 所以它适合做事务 同时处理异常一般也再业务层解决 记录日志一般也会在这里编写 商品dao-- 负责查询商品 用户dao-- 负责查询当前登录用户 库存dao-- 负责查询库存是否充足 如果充足更新库存(减少库存) 订单dao-- 负责添加订单
-
Dao:数据访问层 用于和数据库交互(jdbc mybatis)
-
-
V:view视图层,主要提供可视化操作界面(html jsp vue)
-
C:controller控制层,主要用于连接视图层和模型层 用于接收请求 处理请求 控制哪个视图对应哪个模型的(servlet springmvc springboot)
4.springmvc输入和输出
-
前后端不分离:前端代码 和后端代码在同一项目,涉及不到跨域问题
-
输入:前端如何传递数据给后端(表单提交 或者 超链接 地址传参)
1.如果提交的是一个简单的数据(String int double...) 通过springmvc方法 添加形参 要求形参名必须和提交的name一致 比如: 好处:不用像原来Servlet手动获取 也不用类型转换 会自动转换 2.如果提交的是多个数据(比如:批量删除) public void test(Inter[] ids){} 要求:前端提交的name值 和ids形参名一致 3.如果提交的是多个数据(比如:注册和新增) 可以在方法形参定义对象类型 要求name值必须跟属性名一致 4.如果提交的数据包含日期 会出现400错误 需要格式转换 在日期属性上添加一个注解@DateTimeFormat(日期格式) 5.如果传输文件:比较特殊
-
输出:后端处理完后 返回的数据给前端(作用域)
1.存储作用域:形参直接定义即可 如果是存储request:Model 如果是存储session:定义: HttpSession 2.跳转前端页面:方法返回值定义地址即可 如果转发:属于默认"/day1/show.jsp"; 如果重定向:"redirect:/day1/show.jsp" 3.想使用其他类型的对象 request response session ... 直接在形参定义对应的类型 springmvc帮你自动赋值
-
-
前后端分离:前端是一个独立项目(vscode)后端也是独立项目(idea) 两个项目是相互独立的 独立部署 两者没有影响的 功能的实现只需要通过前端项目发送请求到另一个后端项目接收处理,涉及到跨域问题
-
输入:前端 项目提交数据 如何给后端接收
-
前端:发送异步请求,同时要把提交的数据转换成json格式提交过来
axios.post('后端url',json数据).then((abc)=>{ //then() 就是回调函数 })
-
后端:方法形参添加@RequestBody 目的用于接收前端提交json转换后 后端的对象
@RequestMapping('/地址') public XXX get(@RequestBody User u){ }
-
-
-
输出:后端项目返回数据给前端使用
-
后端:方法上@ResponseBody 目的是为了表示返回值不是地址 是给前端项目响应的结果 也会自动转换成json
@RequestMapping("/地址") @ResponseBody public List<User> get(@RequestBody User u){ return 集合;//后端会自动把集合转换成json 给前端 }
-
前端:发送的异步请求 会由一个回调函数(请求发送后成功的函数) 这个回调函数的参数就是后端返回的json数据
axios.post('后端url',json数据).then((abc)=>{ //abc 就是后端返回集合得json数据 console.log(abc); });
-
5.文件上传和下载
-
文件上传
-
前提
-
前端:
1.请求方式只能是post方式 2.数据得传递方式不能是字符串提交 需要设置附件提交 <form action="" method="post" enctype="multipart/form-data"> </form>
-
后端:
1.springmvc配置文件 配置上传组件(允许接收文件) 2.方法形参上MultipartFile类型表示文件类型 3.方法形参名 也要和表单提交name一致得 bug: 切记name不能和对象属性名一致 否则400 @RequestMapping("") public String add(Usermvc u,MultipartFile 非head){ }
代码实现:
//上传通用方法 public static String upload(HttpServletRequest req, MultipartFile myHead){ //文件保存地址: 存储在服务器得特定包(后期可以存储在云服务器) //假设在服务器定义一个包: upload 专门保存上传得文件 //获取项目运行得真实路径+/upload/ String path=req.getServletContext().getRealPath("/upload/"); File file=new File(path); if(!file.exists()) file.mkdirs(); //文件名 :由于会替换不能拿上传名字保存 需要唯一处理 //1.jpg 文件名(随机得:随机数 时间戳 UUID)+后缀名(固定不变得) //1.获取提交得文件名 String oldName=myHead.getOriginalFilename();//1.1.1.jpg //2.获取后缀名 String suffix= oldName.substring(oldName.lastIndexOf(".")); //3.保存文件名需要随机 UUID: 32位永不重复得字符串 String name= UUID.randomUUID().toString(); //4.新文件名 String newName=name+suffix; File f=new File(path+newName); //开始上传 try { myHead.transferTo(f); } catch (IOException e) { e.printStackTrace(); } return newName; }
-
-
-
文件下载
//下载通用方法 : 使用于SSM public static ResponseEntity<byte[]> download(HttpServletRequest req,String fileName){ //1.获取请求头部信息 HttpHeaders headers=new HttpHeaders(); //2.设置文档类型 设置成流 headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); byte[] bs= new byte[0]; try { //2.1 设置文件名编码方式 如果文件名是中文也可以识别 fileName= URLEncoder.encode(fileName,"utf-8"); //3.指定附件形式下载 (参数1:属性名附件,参数2:下载后的文件名) headers.setContentDispositionFormData("attachment",fileName); //4.指定好返回值 ResponseEntity<byte[]> //参数1: 下载的文件 的字节数组 //参数2: 请求头部信息 //参数3: 响应实体的状态(新建状态) File file=new File(req.getServletContext().getRealPath("/upload/")+fileName); bs = FileUtils.readFileToByteArray(file); } catch (IOException e) { e.printStackTrace(); } ResponseEntity<byte[]> re=new ResponseEntity<byte[]>(bs,headers, HttpStatus.CREATED); return re; }
6.分页
分页是一种将所有数据分段展示给用户的技术,用户每次看到的不是全部的数据,而是将其中的一部分,用户可以通过指定页码数切换可见内容,类似于阅读书籍
6.1实现分页的步骤
-
会编写分页sql语句
select * from 表 where 条件 limit 起始下标,查询长度;
-
指定好页码数和每页条数
当前页:3 每页5条数据 第一页: limit 0,5 第二页:limit 5,5 当前页,第n页: limit (当前页-1)*每页条数,每页条数
-
封装分页信息( 工具类/ Mybatis框架分页插件 pageHelper)
当前页数 每页条数 总条数 总页数 每页数据的集合 ...
-
将封装好的分页信息 传递给前端
7.视图解析器
7.1什么是视图解析器
springmvc重要的组件 也是他工作流程必加项,目的是用于解析视图,如果把页面放入web-inf会有很多相同的地址 这样页面越多 代码冗余越多(地址前缀和地址后缀)
//比如: /WEB-INF/jsp .jsp 都是冗余
return "/WEB-INF/jsp/user/show.jsp"
return "/WEB-INF/jsp/study/show.jsp"
<!--6.springmvc视图解析器:添加统一的前缀和后缀-->
<!--bug: 这样设置所有跳转地址 都会添加前缀和后缀
解决方案: 如果一些请求不想加前缀和后缀
return "forward:/地址" "redirect:/地址"
-->
<!--class: 只能识别jsp视图 如果换其他视图 要换全类名-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--设置前缀-->
<property name="prefix" value="/WEB-INF/jsp"/>
<!--设置后缀-->
<property name="suffix" value=".jsp"/>
</bean>
7.2 静态资源的路径映射
springmvc提供路径映射的功能,如果项目静态资源放入web-inf里面(不能对外访问的) 或者静态资源路径很长 都可以借助于springmvc来重写地址 springmvc会帮你转发到真实地址
<mvc:resources mapping="/自定义/**" location="真实地址"/>
<mvc:resources mapping="/abc/**" location="/WEB-INF/js/"/>
前端:
<script src="/abc/my.js"></script>
8.springmvc工作流程
- 前后端不分离
- 前后端分离
8.1工作流程中的几个重要组件
- DispatherServlet:核心控制器,最核心的组件 所有请求 都必须经过它才能到达springmvc 其他核心组件也必须经过它才可以进行正常交互过程
- HandlerMapping:请求映射器,保存一些曾经@RequestMapping...配置过的请求地址 目的就是用于和用户发送的请求进行匹配,匹配不到则返回404,匹配到了就可以正常执行
- HandlerAdapter:代理对象,用于动态调用(底层JDK动态代理)@RequestMapping...对应的哪个Controller中的哪个Method方法
- Controller:控制层,经常编写的控制层代码@Controller @RequestMapping
- ViewResolver:视图解析器,用于解析ModelAndView对象 是为了解析成哪个Model(数据)对应哪个View(视图)
8.2工作流程 ---面试题
-
前后端不分离:
1.请求请求先到核心控制器(DispatherServlet)
2.核心控制器查询请求映射器(HandlerMapping)是否存在对应请求 如果不存在直接返回浏览器404 如果存在返回对应的Controller对象
3.核心控制器通过代理对象(HandlerAdapter)动态调用Controller对象的哪个Method的方法
4.执行控制层的方法进行请求处理 最后执行结束返回ModelAndView给核心控制器
5.核心控制器把ModelAndView调用视图解析器(ViewResolver)对其进行解析 会解析成Model对象和View对象 返回给核心控制器
6.核心控制器返回view视图给前端
-
前后端分离:
1.请求请求先到核心控制器(DispatherServlet)
2.核心控制器查询请求映射器(HandlerMapping)是否存在对应请求 如果不存在直接返回浏览器404 如果存在返回对应的Controller对象
3.核心控制器通过代理对象(HandlerAdapter)动态调用Controller对象的哪个Method的方法
4.执行控制层的方法进行请求处理 最后执行结束返回Json格式给核心控制器
5.核心控制器直接将json数据返回给前端浏览器
9.springmvc拦截器
springmvc拦截器 依赖于springmvc框架,类似于Servlet中的过滤器,底层实现通过反射实例化对象 功能实现是通过动态代理, 属于面向切面编程(AOP)重要的应用 拦截器主要用于拦截进入控制层的请求 并且再一个请求生命周期拦截器可以拦截多次 ...
- 应用场景:通过拦截器实现登录拦截 通过拦截器实现权限控制 ...
9.1拦截器使用方式
-
实现一个HandlerInterceptor接口
-
重写三个方法
-
springmvc配置文件配置拦截器组件(配置哪些请求拦截 哪些放行)
public class LoginInterceptor implements HandlerInterceptor{ //1.控制层方法执行之前调用进行拦截 //return true; 表示可以访问控制层方法 //return false; 无法访问 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Usermvc u=(Usermvc)request.getSession().getAttribute("user"); if(u==null){ response.sendRedirect("/index.jsp"); return false; } return true; } //2.在控制层方法调用之后 视图解析器之前调用 //一般通过它 对请求作用域的数据 进行二次请求 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } //3.再控制层方法执行结束并且返回了 视图解析器也完毕了 才调用 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
springmvc.xml
<!--7.配置拦截器--> <mvc:interceptors> <!--一个拦截器--> <mvc:interceptor> <!--配置哪些请求需要拦截--> <mvc:mapping path="/**"/> <!--配置哪些请求不需要拦截 可以配置多个--> <mvc:exclude-mapping path="/user/login"/> <bean class="com.sc.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
9.2拦截器(Interceptor) 和过滤器(Filter)的区别
- Interceptor:拦截器依赖于框架 拦截器只会拦截进入控制层请求 拦截器在一个请求中可以拦截多次 拦截器可以控制拦截规则(谁放行谁拦截)
- Filter:过滤器依赖于Servlet 过滤器几乎可以拦截所有资源 过滤器在一个请求中只会拦截一次 过滤器无法控制拦 截规则
10.Ajax ---重点
ajax: async(异步的) JavaScript and xml,是一种无需刷新整个浏览器情况下就可以和服务器进行异步通信 就可以提升整个浏览器页面响应速度 也叫局部刷新技术
注: ajax必须依赖于js可以发送异步请求
10.1 ajax异步请求优势
- 提高页面性能:ajax可以并行加载数据或者样式,提高页面加载速度和性能
- 减少带宽消耗: 由于只更新局部内容 不用更新整个浏览器
- 提高用户体验:使用ajax发送请求时不刷新浏览器 就不会影响用户的正常操作(视频 评论...)
- 实现内容更新: ajax可以更加方便的使用js和服务器进行数据交互 就可以动态进行内容加载 比如:实时搜索 验证用户名是否可用
- 也是前后端分离的基础: 前端项目和后端项目进行交互 都是发送异步请求 通过json交互的
缺点:
- 对搜索引擎不友好:由于页面内容的动态更新,搜索引擎难以获取完整的页面信息,影响了网站的SEO效果。
- 增加开发复杂度:相比传统的页面提交方式,使用Ajax需要编写更多的JavaScript代码和处理逻辑,增加了开发的复杂性。
- 跨域限制:由于浏览器的同源策略,Ajax在跨域访问资源时会受到限制,需要通过跨域资源共享(CORS)等机制进行处理。
- 前进和后退:ajax不适合做前进和后退 用户无法回到前一个页面的效果(只能回退到当时刚进入该页面的效果)
10.2 ajax异步请求实现方式
-
原生js实现ajax:步骤特别繁琐 不推荐使用
1.创建ajax对象 XMLHttpRequest 2.为ajax对象绑定监听(绑定什么时候成功 什么时候失败 回调函数) 3.为ajax对象绑定地址 4.发送异步请求 5.接收响应数据(如果成功了会执行成功回调函数 否则执行失败的)
-
jQuery封装好的方法发送异步请求:数据交互格式默认不是json格式 不适合做前后端分离的项目
1.先导入jQuery <script src=""></script> <!--<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>--> 2.通过$.方法() 来发送异步请求 $就是jQuery简称 $.ajax() $.post() $.get() 推荐使用后两个 第一种适合大型项目 $.post("url",data(可以拼接),function (res){ //res就是后端返回的结果 })
-
axios发送异步请求:目前最主流 非常适合前后端分离的项目 默认交互格式就是json 还可以和vue框架很好兼容
<!--1.导入axios环境 网络导入 本地导入 vue项目导入 --> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <!--2.通过axios.post() axios.get()发送异步请求 --> axios.post("url",data(和jQuery一样可以通过?拼接)).then(function (res){ res.data //后端返回的结果 }) axios.post("url",data).then(res=>{ res.data//后端返回的数据 })
10.3 springmvc如何处理异步请求
//springmvc如何处理异步请求:
//1.不能跳转
//2.方法的返回值就是给前端的响应结果 添加@ResponseBody
//如果是String返回值 需要修改响应格式否则中文乱码
//其他类型都不用处理
@RequestMapping(value ="/checkName",produces = "text/html;charset=utf-8")
@ResponseBody
public String checkName(String name){
if(us.checkName(name)){
return "姓名可以使用";
}
return "姓名已经重复了";
}
10.4 同步请求和异步请求的区别
-
同步请求: 通过正常表单 或者 超连接 一般发送的都是同步请求,发送请求后 需要等待 服务器响应 这个时期页面会被阻塞 只能响应了之后 才能执行后续代码 (说白了就是正常程序执行一行一行执行)
- 应用场景: 适用于需要立即响应结果需求 后续的功能需要依赖这个结果才能执行的 比如:登录成功后 才能访问后续页面
-
异步请求: 通过vue或者手写jQuery和axios 一般可以发送异步请求,发送请求后 不需要等待服务器响应 类似于有一个子线程专门负责 异步请求执行所以 服务器没有响应的话 依然可以执行后续的代码 (说白了 程序是由主线程执行 异步请求由一个子线程执行 两者不影响 )
- 应用场景: 适用于多个并发请求 或者不确定响应时间场景下使用 比如:订单支付 不确定用户何时付款 适合异步的
11.Json ---重点
Json是一种轻量级的数据交互格式,其本质就是一个字符串,但是它可以描述任意数据类型,具有良好的拓展性 体积小,传输效率很高 ,易于解析 目前适用于各种中大型系统 特别是前后端分离的项目 就是基于json传递的
11.1 json语法
Json是基于键值的形式组装数据的
-
key和value之间通过:隔开 多组key和value通过,隔开
-
key可以任意编写 类似于对象中属性名,双引号idea要加 vue可加可不加
key:"value","key2":"value2"
-
value类似于对象中属性值,可以存放任意数据
- value存储整型:key :10
- value存储字符串:key:"内容"
- value存储布尔:key: true
- value存储对象:key:{k:v,k2:v2}
- value存储集合或数组:key:[ {},{},{} ]
- value存储空值:key: null
-
测试案例
// 通过json表示用户对象(id,name,sex) var user={ id:10, name:"张三", sex:"男" } //使用:user.id 获取属性值 user.name user.sex //通过json表示一个班级对象(id,classname,用户集合 users,小组group) var classes={ id:100, classname:"sceen", users:[ {id:1,name:"李四",sex:"男"}, {id:1,name:"王五",sex:"男"}, {id:1,name:"张三",sex:"男"} ], group:["组1","组2","组3"] }
11.2 json优缺点
优点:
- 简单易于解析:json数据是可以直接转换成js对象 方便前端使用 而且一些成熟框架vue 也可以帮忙自动转换
- 传输效率高:属于轻量级的 占用资源小 比较轻便
- 广泛类型支持:几乎支持所有的数据结构(字符串 数字 布尔 数组 对象)
- 跨平台兼容性:由于JSON的简单和开放,它已经成为许多APIs和Web服务的标准数据格式,几乎所有的编程语言和平台都支持JSON。
缺点
- 不支持注释 :JSON不支持注释,因此无法在JSON数据中添加注释信息,这可能会影响代码的可读性和可维护性。
- 换行不友好 : 换行的时候不能灵活进行 因为需要进行转义
12.前后端分离的流程
-
前端:vue
- 所有功能通过axios发送异步请求
- 数据传递自动组装成json格式传递给后端
- 异步请求发送成功 等待后端响应 获取到响应数据 展示到前端
-
后端:
-
方法参数上如果接收json数据 添加@RequestBody注解
-
方法上添加@ResponseBody注解表示通过json返回给前端
-
返回值通过一个统一的Result格式返回 这样前端可以更加方便的解析
- 优化:所有请求如果都需要添加@ResponseBody,就可以把类上注解@Controller 修改成@RestController (等价于@Controller+@ResponseBody)下面所有的方法 默认都会添加@ResponseBody
-
-
跨域:前端是一个项目 后端也是一个项目 不同的项目之前想去访问 需要发送完整的URL地址才可以(协议://ip地址:端口号/项目) 这三者如果有一个不同都会产生跨域问题
-
解决方案:
- 后端:注解 或者 配置类 ...
- 前端:通过代理...
-
转载自:https://juejin.cn/post/7411548553997533211