1、什么是 SpringMVC?
SpringMVC 是基于 MVC 开发模式的框架,用来优化控制器。它是 Spring 家族的一员,它也具备 IOC 和 AOP。
MVC 是一种开发模式,它是模型视图控制器的简称,所有的 web 应用都是基于 MVC 开发。
1)M:模型层,包含实体类,业务逻辑层,数据访问层
2)V:视图层,html,javaScript,vue 等都是视图层,用来显现数据
3)C:控制器,它是用来接收客户端的请求,并返回响应到客户端的组件,Servlet就是组件
2、SpringMVC 框架的优点?
1)轻量级,基于 MVC 的框架
2)易于上手,容易理解,功能强大
3)它具备 IOC 和 AOP
4)完全基于注解开发
3、Springmvc 的执行流程:
执行流程说明:
1)向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。
2)DispatcherServlet 根据<servlet-name>中的配置对请求的URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
3)DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。
4)提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。在填充 Handler的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:
HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。
数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等。
数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
5)Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。
6)根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。
7)ViewResolver 结合Model和View,来渲染视图。
8)视图负责将渲染结果返回给客户端
4、基于注解的 SpringMVC 框架开发的步骤:
1)新建项目,选择 webapp 模板
2)修改目录,添加缺失的 test 目录,java目录,resources目录(两套),并修改目录属性
3)修改 pom.xml 文件,添加 SpringMVC 的依赖,添加 Servlet 的依赖
<!--添加springmvc的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!--添加servlet的依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency>
4)添加 springmvc.xml 配置文件,指定包扫描,添加视图解析器.
SpringMVC 框架为了避免请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver 中引入了请求的前辍与后辍。而 action 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接;
<context:component-scan>:用来进行包扫描,这里用于指定@Controller注解所在的包路径。
<!--添加包扫描--> <context:component-scan base-package="com.bjpowernode.controller"></context:component-scan> <!--添加视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--配置前缀--> <property name="prefix" value="/admin/"></property> <!--配置后缀--> <property name="suffix" value=".jsp"></property> </bean>
5)删除 web.xml 文件,新建 web.xml
6)在 web.xml 文件中注册 springMVC 框架(所有的 web 请求都是基于 servlet 的)
<!--注册SpringMVC框架--> <servlet> <servlet-name>springmvc</servlet-name> <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> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- 指定拦截什么样的请求 <a href="${pageContext.request.contextPath}/demo.action">访问服务器</a> --> <!--指定拦截以.action结尾的请求,交给核心处理器DispatcherServlet处理。--> <url-pattern>*.action</url-pattern> </servlet-mapping>
7)在 webapp 目录下新建 admin 目录,在 admin 目录下新建 main.jsp 页面,删除 index.jsp 页面,并新建
8)开发控制器(Servlet),这是一个普通的类,不用继承和实现接口。类中的每个方法就是一个具体的action控制器。
@Controller //表示当前类为处理器,交给Spring去创建对象 public class DemoAction { /** * 以前的Servlet的规范 * protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {} * action中所有的功能实现都是由方法来完成的 * action方法的规范 * 1)访问权限是public * 2)方法的返回值任意 * 3)方法名称任意 * 4)方法可以没有参数,如果有可是任意类型 * 5)要使用@RequestMapping注解来声明一个访问的路径(名称) * */ @RequestMapping("/demo") public String demo(){ System.out.println("服务器被访问到了......."); return "main"; //可以直接跳到/admin/main.jsp页面上 } }
9)添加 tomcat 进行测试功能
5、分析 web 请求:
web请求执行的流程:
index.jsp<--------------->DispatcherServlet<------------------->SpringMVC的处理器(加了@Controller的类中的方法)
one.jsp <--------------->DispatcherServlet<------------------->SpringMVC的处理器(加了@Controller的类中的方法)
6、@RequestMapping 注解详解:
1)此注解可加在方法上,是为此方法注册一个可以访问的名称(路径)。
@RequestMapping("/demo") public String demo(){ System.out.println("服务器被访问到了......."); return "main"; //可以直接跳到/admin/main.jsp页面上 } //<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
2)此注解可以加在类上,相当于是包名(虚拟路径),区分不同类中相同的 action 的名称。URI 的请求是相对于 Web 的根目录。换个角度说,要访问处理器的指定方法,必须要在方法指定 URI 之前加上处理器类前定义的模块名称。
@RequestMapping("/user") public class DemoAction1 {..} //<a href="${pageContext.request.contextPath}/user/demo.action">访问服务器</a>
3)此注解可区分 get 请求和 post 请求:
@Controller public class ReqAction { @RequestMapping(value = "/req",method = RequestMethod.GET) public String req(){ System.out.println("我是处理get请求的........"); return "main"; } @RequestMapping(value = "/req" ,method = RequestMethod.POST) public String req1(){ System.out.println("我是处理post请求的........"); return "main"; } }
7、五种数据提交方式:
1)单个提交数据
栗子:
/* <form action="${pageContext.request.contextPath}/one.action"> 姓名:<input name="myname"><br> 年龄:<input name="age"><br> <input type="submit" value="提交"> </form> */ @RequestMapping("/one") public String one(String myname,int age){ ===>自动注入,并且类型转换 System.out.println("myname="+myname+",age="+(age+100)); return "main"; }
2)对象封装提交数据:
声明一个自定义的实体类参数,框架调用实体类中相应的setter方法注入属性值,只要保证实体类中成员变量的名称与提交请求的name属性值一致即可。
实体类:
public class Users { private String name; private int age; //构造方法,setter方法等省略。。。 }
页面:
<form action="${pageContext.request.contextPath}/two.action" method="post"> 姓名:<input name="name"><br> 年龄:<input name="age"><br> <input type="submit" value="提交"> </form>
action:
@RequestMapping("/two") public String two(Users u){ System.out.println(u); return "main"; }
3)动态占位符提交
//<a href="${pageContext.request.contextPath}/three/张三/22.action">动态提交</a> @RequestMapping("/three/{uname}/{uage}") public String three(@PathVariable("uname") //用来解析路径中的请求参数 String name, @PathVariable("uage") int age){ System.out.println("name="+name+",age="+(age+100)); return "main"; }
4)映射名称不一致
/** * 姓名:<input name="name"><br> * 年龄:<input name="age"><br> */ @RequestMapping("/four") public String four(@RequestParam("name") //专门用来解决名称不一致的问题 String uname, @RequestParam("age") int uage){ System.out.println("uname="+uname+",uage="+(uage+100)); return "main"; }
5)使用HttpServletRequest对象提取;
/** * 姓名:<input name="name"><br> * 年龄:<input name="age"><br> */ @RequestMapping("/five") public String five(HttpServletRequest request){ String name = request.getParameter("name"); int age = Integer.parseInt(request.getParameter("age")); System.out.println("name="+name+",age="+(age+100)); return "main"; }
8、中文乱码解决方案:
解决方案:
<filter> <filter-name>encode</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!-- 配置参数 private String encoding; private boolean forceRequestEncoding; private boolean forceResponseEncoding; --> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encode</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
9、action方法的返回值:
1)String:
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址;
可以自动拼接前缀和后缀,可以指定返回的路径;
当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前辍与后辍了。
2)Object:
返回 json 格式的对象,自动将对象或集合转为 json,使用的 jackson 工具进行转换,必须要添加 jackson 依赖,一般用于 ajax 请求;
处理器方法也可以返回 Object 对象,这个 Object 可以是 Integer,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。返回对象,需要使用@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。Ajax请求多用于Object返回值类型。
3)void:
无返回值,一般用于ajax请求;
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
4)基本数据类型,用于 ajax 请求。
5)ModelAndView:返回数据和视图对象,现在用的很少。
10、完成 ajax 请求访问服务器,返回学生集合:
1)添加jackson依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency>
2)在 webapp 目录下新建 js目录,添加 jQuery 函数库
3)在 index.jsp 页面上导入函数库:
4)在 action 上添加注解@ResponseBody,用来处理ajax请求
@Controller public class AjaxAction { //处理ajax请求,一定要加@ResponseBody @ResponseBody @RequestMapping("/ajax") public List<Student> ajax(){ Student stu1 = new Student("张三",22); Student stu2 = new Student("李四",24); Student stu3 = new Student("王五",23); List<Student> list = new ArrayList<>(); list.add(stu1); list.add(stu2); list.add(stu3); //调用json转换工具ObjectMapper进行转换 return list; //===>springmvc负责转换成json } }
5)在 springmvc.xml 文件中添加注解驱动 <mvc:annotationdriven/>,它用来解析 @ResponseBody 注解
<mvc:annotation-driven></mvc:annotation-driven>
11、SpringMVC 的四种跳转方式:
可以使用框架提供的关键字 redirect: ,进行一个重定向操作,包括重定向页面和重定向action;
可以使用框架提供的关键字forward:,进行服务器内部转发操作,包括转发页面和转发action;
栗子:
@RequestMapping("/one") public String one(){ System.out.println("这是请求转发页面跳转........."); return "main"; //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转 } @RequestMapping("/two") public String two(){ System.out.println("这是请求转发action跳转........."); // /admin/ /other.action .jsp //forward: 这组字符串可以屏蔽前缀和后缀的拼接.实现请求转发跳转 return "forward:/other.action"; //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转 } @RequestMapping("/three") public String three(){ System.out.println("这是重定向页面......."); //redirect: 这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转 return "redirect:/admin/main.jsp"; } @RequestMapping("/four") public String four(){ System.out.println("这是重定向action......."); //redirect: 这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转 return "redirect:/other.action"; } @RequestMapping("/five") public String five(){ System.out.println("这是随便跳......."); return "forward:/fore/login.jsp"; }
12、SpringMVC 默认的参数类型:
不需要去创建,直接拿来使用即可。
1)HttpServletRequest
2)HttpServletResponse
3)HttpSession
4)Model
5)Map
6)ModelMap
栗子:
//做一个数据,传到main.jsp页面上 Users u = new Users("张三",22); //传递数据 request.setAttribute("requestUsers",u); session.setAttribute("sessionUsers",u); model.addAttribute("modelUsers",u); map.put("mapUsers",u); modelMap.addAttribute("modelMapUsers",u);
注意:Map,Model,ModelMap 和 request 一样,都使用请求作用域进行数据传递,所以服务器端的跳转必须是请求转发,页面才可以得到值。
13.日期处理:
1)日期的提交处理
A、单个日期处理
在方法的参数上使用@DateTimeFormat注解;
在类的成员setXXX()方法上使用@DateTimeFormat注解
B.类中全局日期处理
注册一个注解,用来解析本类中所有的日期类型,自动转换
@InitBinder; public void initBinder(WebDataBinder dataBinder){ dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true)); }
2)日期的显示处理:
步骤:
A)添加依赖 jstl
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
B)在页面上导入标签库 :
如果是 list 中的实体类对象的成员变量是日期类型,则必须使用 jstl 进行显示。
<%--导入jstl核心标签库--%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%--导入jstl格式化标签库--%> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
C)使用标签显示数据:
<table width="800px" border="1"> <tr> <th>姓名</th> <th>生日</th> </tr> <c:forEach items="${list}" var="stu"> <tr> <td>${stu.name}</td> <td>${stu.birthday}------ <fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate></td> </tr> </c:forEach> </table>
14、SpringMVC 的拦截器:
拦截器执行的时机:
1)preHandle():在请求被处理之前进行操作(预处理)
2)postHandle():在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果(后处理)
3)afterCompletion:所有的请求响应结束后执行善后工作,清理对象,关闭资源(最终处理)
拦截器实现的两种方式:
1)继承 HandlerInterceptorAdapter 的父类
2)实现 HandlerInterceptor 接口
拦截器实现的步骤:
1)创建登录方法,在 session 中存储用户信息,用于进行权限验证
@RequestMapping("/login") public String login(String name, String pwd, HttpServletRequest request){ if("zar".equalsIgnoreCase(name) && "123".equalsIgnoreCase(pwd)){ //在session中存储用户信息,用于进行权限验证 request.getSession().setAttribute("users",name); return "main"; }else{ request.setAttribute("msg","用户名或密码不正确!"); return "login"; } }
2)开发拦截器的功能,实现 HandlerInterceptor 接口,重写 preHandle() 方法:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //是否登录过的判断 if(request.getSession().getAttribute("users") == null){ //此时就是没有登录,打回到登录页面,并给出提示 request.setAttribute("msg","您还没有登录,请先去登录!"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response); return false; } return true;//放行请求 } }
3)在 springmvc.xml 文件中注册拦截器:
<mvc:interceptors> <mvc:interceptor> <!--映射要拦截的请求--> <mvc:mapping path="/**"/> <!--设置放行的请求--> <mvc:exclude-mapping path="/showLogin"></mvc:exclude-mapping> <mvc:exclude-mapping path="/login"></mvc:exclude-mapping> <!--配置具体的拦截器实现功能的类--> <bean class="com.bjpowernode.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
资源在WEB-INF目录下:
很多企业会将动态资源放在WEB-INF目录下,这样可以保证资源的安全性,因为在WEB-INF目录下的动态资源不可以直接访问,必须要通过请求转发的方式进行访问,这样避免了通过地址栏直接对资源的访问,重定向也无法访问动态资源。
栗子:
@Controller public class WebInfAction { @RequestMapping("/showMain") public String showMain(){//通过请求转发的方式访问webapp目录下的WEB-INF目录下的main.jsp System.out.println("访问main.jsp"); return "main"; } @RequestMapping("/showLogin") public String showLogin(){////通过请求转发的方式访问webapp目录下的WEB-INF目录下的login.jsp System.out.println("访问login.jsp"); return "login"; } }