版权声明:文章为作者自己原创文章,转载请注明出处。 https://blog.csdn.net/qq_37128049/article/details/83420355
SpringMVC 笔记一 [共四篇,史上最全重点] @记得关注哦,持续更新自己的最全最精华笔记哦~
今日内容:
1.注意:
* 三层架构是java特有的。 表现层|持久层|业务层;
* MVC设计模式是很多语言所采用的;[只要是web开发,都会了解到MVC的设计模式]
SpringMVC的开始:
1. 三层架构:平时是C/S或B/S架构,我们平时做web开发是用B/S架构的开发;服务端就经常分为三层架构;
1. 表现层:框架[SpringMVC]:浏览器与服务器端进行数据的交互;服务器接收浏览器的参数,然后调用业务层的方法,拿到结果哦数据,然后响应给浏览器结果;
2. 业务层:框架[Spring]:业务层与持久层进行交互,它响应表现层进行方法执行,并联系持久层的该方法,拿到结果返回给表现层;
3. 持久层:框架[Mybatis]:通过业务层的指令执行对数据库的操作,拿到数据后返回给业务层;
2. MVC设计模型:
1. M:model 模型 JavaBean [mybatis,Spring] -->执行者
2. V:view 视图 JSP [jsp,html等页面] -->显示
3. C:Controller 控制器 Servlet [SpringMvc] -->接收请求完成响应
3. 概述:
* 基于java实现的MVC设计模型的请求驱动类型的轻量级WEB框架,属于SpringFrameWork的后续产品,已经融合在Spring web Flow里面;
4. 优势:
1. 清晰的角色划分 [模块开发]
* 前端控制器
* 请求到处理器映射 [请求路径]
* 处理器适配器
* 视图解析器 [跳转到xxx.jsp页面]
* 处理器或页面控制器
* 验证器
* 命令对象
* 表单对象
->>当请求进入SpringMVC中的时候,会经过这些一系列的模块,每个组件都会对这个请求体做一些事情,当走完的时候就直接返回结果;
->>
* 清晰明了,扩展修改容易,每个各司其职;
* 可以使用命令对象直接作为业务对象
* ...
5. SpringMVC和Struts2的优劣分析:
1. 共同点:
* 都是表现层框架,基于MVC模型编写的
* 底层都离不开原始ServletAPI
* 处理请求的机制都是一个核心控制器
2. 区别:
* Spring MVC入口是Servlet,而Struts2是Filter [Struts的核心就是过滤器]
* SpringMVC 是基于方法设计的,Struts是基于类;struts每次执行都会创建一个类,所以SpringMVC会稍微比Struts2快些; [SpringMVC是单例的,所以省去创建对象这个过程]
* SpringMVC使用更加简洁,同时还支持JSR303,处理ajax的请求更方便
* Struts2的OGNL的表单式使页面的开发效率相比Spring MVC更高些,但是执行效率并没有JSNL表达式高;
SpringMVC的入门程序:
1. 需求分析: 编写index.jsp超链接标签,发送请求给SpringMVC表现层的后台,编写一个类并编写方法并返回结果,让程序转发到成功的jsp页面上;
2. 入门实现案例:
1. 开始:
1. 需要搭建开发的环境
* 构建Maven项目 jdk.1.8|骨架构建|后缀为:webapp、
* cn.itcast|springmvc_day01_01_start
* 下一步后添加一组键值对:[解决maven创建过慢] archetypeCatalog | internal
2. 环境搭建:
1. 在main下创建java包,resources包[Mark Diretory as 将java和resources的类型转换]
2. pom.xml引入:
* 拷入依赖:
* <properties> <spring.version>5.0.2.RELEASE</spring.version>
* </properties>
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency>
<dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> </dependencies>
3. 配置前端控制器: [servlet]
1. 在web.xml 中添加:
* <web-app>
* <display-name>Archetype Created Web Application</display-name>
* <servlet>
* <servelt-name>dispatcherServlet</servlet-name>
* <servelt-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>
* <servelt-mapping>
* <servlet-name>dispatcherServlet</servlet-name>
* <url-prrten>/</url-pattern>
* </serlvet-mapping>
* </wen-app>
2. 配置核心配置文件:
* 创建springmvc.xml [resources下] 导入其约束
3. 开始入门程序
1. 在index.jsp中编写客户端;
2. 在java.cn.itcast.controller.HelloController中编写控制器类:
* public String sayHello(){
* System.out.println("Hello StringMVC")
* return "sucess"; //返回susess,就得提供这个名字的jsp名字
* }
3. 将该类变成对象,即在springmvc.xml中开启注解扫描:在文档中找
* <context compontent...
4. 在HeeloController上面添加@Controller注解[servelt标识]和@RequestMapping(path="/user")【一级目录】,并在方法上添加注解@RequestMapping(path="/hello") [请求映射,这个注解路径hello就是以后该类该方法的请求路径]
5. 让mvc这个配置文件加载,在前端控制器中配置加载,web.xml,提供初始化参数:
* <init-param>
* <param-name>contextConfigLocation</param-name>
* <param-value>classpath:springmvc.xml</param-value>
* </init-param>
6. 创建一个名为sucess.jsp的成功界面
* webapp.WEB-INF.pages.success.jsp
* 在springmvc.xml配置视图解析器:
* <bean id="internalResourceViewResolver" class="org.spring...internalResourceViewResolver..."></bean>
* <property name="prefix" value="/WEB-INF/pages"> //prefix 前缀
* <property name="suffix" value=".jsp"> //suffix 后缀
* </bean>
* //第一个hellocontruller中的sayhello方法中返回sueccess,会通过视图解析器添加前后缀和路径,直接找到了动态包中的seccess.jsp页面;可以直接访问静态页面,但是WEB-INF里面的内容只能请求转发进去;
* 很多公司就将一些登录后的页面直接放到web-inf里面,这样就免去了很多的验证操作;
* 开启springMVC框架注解的支持
* <mvc:annotation-driven/>
7. 补全两个jsp网页
* <a href="hello" >入门程序</a>
* 没有虚拟路径的时候,/hello或者hello都可以
* 有虚拟路径的时候
* "/hello" ->[绝对路径] http://locathost:8080/hello
* "hello" ->[相对路径] http://locathost:8080/springmvc-day01/hello
->> 相对路径是相对于当前文件的路径,会添加上虚拟路径跳到相对路径上;绝对路径就会调到http://locathost:8080/hello,不会添加虚拟路径地址了;
* 但是在工作中我们会直接使用绝对路径,使用相对路径多次的跳转容易出现问题
3. 解析:
1. 启动服务器,加载一些配置文件
1. DispatcherServlet对象呗创建
2. springmvc.xml被加载了
3. HelloController创建成对象
2. 发送请求,后台处理请求
3. 流程分析
1. 连接指向项目名称/hello->DispatcherServlet[控制作用,指挥中心]->先找@RequestMapping(path="hello")sayHello方法执行,返回success,然后控制器找视图解析器对象:InternalResourceViewResolver然后拼接前后缀和位置,找到succes.jsp然后返回结果;
2. DispatcherServlet:它就是一个控制器,实际上没有处理问题的能力,但是它可以调度其他的模块组件,完成任务;
3. 组件功能:
1. 处理器映射器:[HandlerMapping]--> 负责解析浏览器发来的地址,让Controller类中方法去执行/hello 返回具体的哪个类和哪个方法
2. 处理器适配器:[HandlerAdapter]-->适配器模式,让过来的任何类和任何方法都转为可识别的方法和类,然后进行执行操作; //将千奇百怪的东西适配成格式统一的类,方法然后给处理器
3. 处理器:[Handler]:处理器平常也叫作Controller :拿到执行代码进行执行,然后给视图解析器结果;
4. 视图解析器:如果是返回"success"等,就会调用视图解析器进行解析,并跳转到xxx.jsp页面;
5. View 视图:接收到数据,将数据进行渲染,变成好看的页面Jsp,Freemarker,Excel,Pdf等...
6. 处理器映射器,处理器适配器,视图解析器称为SpringMVC的三大组件;
请求参数的绑定
1. @RequestMapping的参数内容:
1. @AliasFor("path"): 这个注解的意思是别名 因为@RequestMapping里的path和value值一样,所以平时写指定路径的时候,可以不写path,因为path=value,而value又可以省略;
2. @RequestMapping()里面的值:
1. value:用于指定请求的URL。它和path属性的作用是一样的。 [指定发送请求的路径]
2. method:用于指定允许的请求方式,不是此请求的方式将不被允许; [指定发送请求的方式]
3. params={"username"}:表示浏览器发送的请求必须有key值为username [指定发送请求的条件]
4. params={"username=heihei"}:表示浏览器发送的请求必须含有key值为username,对应的value值为heihei [指定发送请求的条件]
5. headers={"Accept"}:表示浏览器发送的请求头必须包含Accept [指定限制请求消息头的条件]
2. 请求参数绑定入门 [把表单提交的请求参数,作为控制器中方法的参数进行绑定]
1. 绑定机制:
1. 表单提交的数据都是k=v格式的。username=haha&password=123
2. 把表单提交的请求参数,作为控制器中方法的参数进行绑定 [方法的参数名和表单中的key值是一样的,那么mvc就会自动的将表单的value值绑定到方法的参数上;]
3. 要求:提交表单的name和方法参数的名字一样;
2. 请求参数的绑定: [示例]
1. 浏览器:
* <a href="param/testParam?username=heihei&password=123"></a>
* //这里不加/如果加了/则会是绝对路径,如果加/必须提交上下文路径,就是添加上虚拟路径;
2. 服务器:ParamController类加注解:@Controller和@RequestMapping("param")
3. 示例:
* @Controller //该注解交给容器管理,表名它是一个控制器
* @RequestMapping()
* public class ParamController{
* //请求参数绑定入门
* @RequestMapping("/testParam")
* public String testParam(String username,String passowrd){
* System.out.println("执行了");
* System.out.println("username");
* System.out.println("password");
* return "succuess";
* }};
4. 传入对象:
1. 对象的封装:
1. 注意:如果方法内传入的是对象,而请求传入的是单一的数据,如果对象内的成员变量与request传入的数据是一样的,那么数据会自动与传入的对象绑定;
2. 示例:
1. 实体类
* public Account implements Serializable{
* private String username;
* pricate String password;
* private Date birthday;
* get,set方法,to string方法...
* }
2. * 浏览器:
* <form action="param/saveAccount" method="post">
* 用户名:<input type=""text" name="username"></br/>
* 密码:<input type=""text" name="password"></br/>
* 金额:<input type=""text" name="birthday"></br/>
* <input type=""submit" value="提交"></br/>
3. 控制器:
* @Controller //该注解交给容器管理,表名它是一个控制器
* @RequestMapping()
* public class ParamController{
* //请求参数绑定把数据封装到javaBean中;
* @RequestMapping("/saveAccount")
* public String saveAccount(Account account){
* System.out.println("执行了");
* System.out.println("account");
* return "success";
* }};
2. 引用类型的封装:如果在实体类中还有一个引用对象:
1. 实体类:Account
* public Account implements Serializable{
* private String username;
* pricate String password;
* private Date birthday;
* private User user;
* user 的get,set等方法...
* get,set方法,to string方法...
* }
2. 实体类:User
* private User implements Serializable{
* private String uname;
* private String age;
* }
3. 浏览器:
* <form action="param/saveAccount" method="post">
* 用户名:<input type=""text" name="username"></br/>
* 密码:<input type=""text" name="password"></br/>
* 生日:<input type=""text" name="birthday"></br/>
* user的名字:<input type=""text" name="user.uname"></br/>
* user的年龄:<input type=""text" name="user.age"></br/>
* <input type=""submit" value="提交"></br/>
3. 解决中文乱码问题:
1. 导入过滤器即可;
2. 如果web-app标签报错:
* 将过滤器放在servlet配置文件的前面
* 导入约束,提高版本;
3. 配置文件中的问题:
1. classpath的意思:类路径,就是编译过后的类路径
2. <load-on-startup>1</load-on-startup>
* 服务器启动的时候servlet不会被创建,它是在需要的时候第一次被创建,以后就不会再创建了; [单例]
* 只要加上此代码,则服务器一启动就会创建Servlet对象;在工作中,都会使用此,让加载时间花在启动中,也不要让用户停留等待;
3. 对象中嵌套集合,集合中储存对象,对象内含有成员数据;
1. 浏览器:
* <form action="account/updateAccount" method="post">
* 用户名称:<input type="text" name="username" ><br/>
* 用户密码:<input type="password" name="password" ><br/>
* 用户年龄:<input type="text" name="age" ><br/>
* 账户 1 名称:<input type="text" name="accounts[0].name" ><br/>
* 账户 1 金额:<input type="text" name="accounts[0].money" ><br/>
* 账户 2 名称:<input type="text" name="accounts[1].name" ><br/>
* 账户 2 金额:<input type="text" name="accounts[1].money" ><br/>
* 账户 3 名称:<input type="text" name="accountMap['one'].name" ><br/>
* 账户 3 金额:<input type="text" name="accountMap['one'].money" ><br/>
* 账户 4 名称:<input type="text" name="accountMap['two'].name" ><br/>
* 账户 4 金额:<input type="text" name="accountMap['two'].money" ><br/>
* <input type="submit" value="保存">
* </form>
2. 客户端:
* @RequestMapping("/updateAccount")
* public String updateAccount(User user) {
* System.out.println("更新了账户。。。。"+user);
* return "success";
* }
4. 自定义类型 【在进行表单数据与控制器方法参数的绑定的时候,大多数的数据都可以进行自动转换】
1. String-->Integer [常用类型可以自动转换]
2. 2000/11/11 -->Date [可以转换]
3. 2000-11-11 -->转换失败,报错~ [spring只认识/的日期,不认识/的日期]
* 案例解决:
1. 浏览器:
* <form action="param/saveUser" method="post">
* 用户生日<input type="text" name="age" ><br/>
* <input type="submit" value="保存">
2. 控制器方法:
* @RequestMapping("saveUser")
* public String saveUser(User user){
* System.out.println("user.date")
* return "success";
* }
3. 创建工具类:utils.StringToDateConverter
* public class StringToDateConverter implements Converter<String,Date>{
* @Overide
* public Date convert(String source){
* //传入进来的字符串
* public Date convert(String source){
* if(source=null){
* throw new RuntimeException("请您传入数据");
* }
* DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
* //把字符串转换成日期
* try{
* return df.parse(source);}
* catch(Exception e){
* throw new RuntimeException("数据类型转换出现错误");
* }} };
4. 在springmvc.xml中配置自定义类型转换器:
* <bean id="conversionService" class="rg.springframework.context.support.ConversionServiceFactoryBean">
* <property name="converters">
* <set>
* <bean class="cn.itcast.utils.StringToDateConverter">
* </set>
* </bean>
5. 在springmvc.xml中配置支持:
* <mvc:annotation-driven conversion-service="conversionService"/>
* //使用此配置,将开启对自定义类型转换器的支持;
4. 使用此方法,只能解决使用-的问题可以识别日期,但是自动的/的办法就不能使用了;
1. 问题:如何使输入日期2012/6/5 和2012-6-5都可以被识别;
答:在工具中修改
* public class StringToDateConverter implements Converter<String,Date>{
* Date date=null
* @Overide
* public Date convert(String source){
* //传入进来的字符串
* public Date convert(String source){
* if(source=null){
* throw new RuntimeException("请您传入数据");
* }
* DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
* //把字符串转换成日期
* try{
* return df.parse(source);}
* catch(Exception e){
* throw new RuntimeException("数据类型转换出现错误");
* }} };
2. 中文乱码解决:使用前面的代码只能解决post的中文乱码问题,表单提交也基本使用psot,如果需要使get请求的乱码问题也解决,可以使用tomcat 8以上或者网上搜索解决方案;
常用注解
1. 原生API:
* @RequestMapping
* public String testServlet(HttpServletRequest request,HttpServletResponse response){
* //拿到request
* System.out.prinlnt(request);
* //拿到session
* HttpSession session=request.getSession();
* //拿到ServletContext
* ServletContext servletContext=session.getServletContext();
* }
2. 常用注解:
1. @RequsetParam:对方法的参数里进行定义:必须传入此属性,如果不传则报错,浏览器发送请求带的数据必须与这个一样;
* public String testRequestParam(@RequestParam(name="name")String username){
* System.out.println(username);
* return "success";
* };
2. @RequestBody:用于获取请求体的内容,直接使用得到的是key=value&key=value...结构的数据,它拿到的是整个请求体的内容;
1. 示例:
* @RequestMapping("/testRequstBody")
* public String testRequestBody(@RequestBody String body){
* System.out.println("执行了...");
* System.out.println("body");
* return "success";
* }
2. 作用范围:JSON的数据在请求体里,它可以与json配合使用;使用该注解可以直接拿到json的全部内容;
3. @PathVaribale注解
1. REST风格URL:
1. 不是规范,不是强制要求使用,觉得好就用;
2. 优点:
2. restful编程风格
1. 原来方式:
* UserController类
* @RequestMapping(path="/user/save")
* save
*
* @RequestMapping("/user/save")
* update
*
* @RequestMapping("/user/save")
* findAll
2. 现在的方式:
* UserController类
* path="/user" post
* save
*
* save="/user" put
* update
*
* path="/user" get
* findAll
* localhost:8080/user get
*
* path="/user" get
* fmdByID(id)
* localhost:8080/user/{id} get
3. 演示:
1. 演示控制类方法
* @RequestMapping("/testPathVariable/{id}")
* public String testPathVariable(@PathVariable(name="sid")String id){
* System.out.println(id);
*
* }
2. 浏览器方法:
* <a href="/testPathVariable/10">点我</a>
4. 使用过滤器方式将请求进行转换:
1. WebClient类可以直接使用静态方法发送请求,并且可以模拟各种请求方式进行测试;
2. 不是很重要,需要的话可以查看笔记;
5. 注意:
* 使用相对路径,又转发到本页面,会出现重复相对路径,导致地址出错,无法访问;因为相对路径的查找其他路径是从它本来相对路径的基础上进行查找,就会重叠,如果查找页面的时候使用绝对路径就不会出现这个问题了;
* isELIgnored="false" 不忽略el表达式;
* 示例:
* <%@ page contextType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>