java web 45 : SpringMVC
1、模式一(Model One):JSP(只使用JSP进行开发)
Servlet:本质是一段Java程序,适合处理业务逻辑,但是Servlet不适合输出一个html网页(因为在Servlet中输出网页,得通过response获取流,通过out.write一行一行将html标签等内容输出到浏览器中)
Html:是用于开发网页的语言,适合作为网页输出,但是html是一个静态Web资源,无法展示动态的数据
JSP:也是开发网页的语言,也可以输出一个网页(html),并且JSP中可以书写Java代码(或者JSP标签,其实底层也是Java代码)展示动态数据。
JSP的出现既解决了Servlet不适合输出网页的问题,同时也解决了HTML无法展示动态数据的问题!
在一些中小型项目中只用JSP进行开发(持续了一段时间),JSP负责的工作:
(1)获取请求中携带的数据,对请求进行处理
(2)如果在处理的过程中需要访问数据库,JSP中可以书写Java代码访问数据库
(3)请求处理的结果仍然是由JSP负责响应
JSP中要处理很多的内容,必然要写很多Java代码,这样会导致JSP内部的HTML代码和Java代码混杂在一起,造成JSP页面结构的混乱,可读性变差,后期难以维护,代码难以复用!
2、模式二(Model Two):Servlet+JavaBean+JSP:
(1)Servlet:接收请求中的数据(请求参数)、调用某一个JavaBean对请求进行处理、调用某一个JSP展示请求处理的结果
将请求数据封装到JavaBean内部
调用JavaBean方法处理请求
(2)JavaBean:处理请求(封装数据、处理业务逻辑、访问数据库)
业务Bean: 负责处理业务逻辑
实体Bean: 负责封装数据
User类(username/password/nickname/email..)
(3)JSP:只负责展示/响应对请求处理的结果
-----------------------------------------
** MVC设计模式:
-----------------------------------------
MVC设计模式是一种通用的软件编程思想,不仅仅适用于Java语言。
在MVC设计模式中认为, 任何软件都可以分为三部分组成:
(1)控制程序流转的控制器(Controller)
(2)封装数据处理数据的模型(Model)
(3)负责展示数据的视图(view)
并且在MVC设计思想中要求一个符合MVC设计思想的软件应该保证上面这三部分相互独立,互不干扰,每一个部分只负责自己擅长的部分。
如果某一个模块发生变化,应该尽量做到不影响其他两个模块。这样做的好处是,软件的结构会变得更加的清晰,可读性强。有利于后期的扩展和维护,并且代码可以实现复用。
******************************************************************************************************
(1)
.用户发送请求 至 前端控制器(DispatcherServlet);
提示:DispatcherServlet的作用:接收请求,调用其它组件处理请求,响应结果,相当于转发器、中央处理器,是整个流程控制的中心
(2)
.前端控制器(DispatcherServlet)收到请求后调用处理器映射器(HandlerMapping)
处理器映射器(HandlerMapping)找到具体的Controller(可以根据xml配置、注解进行查找),并将Controller返回给DispatcherServlet;
(3)
.前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)。处理器适配器经过适配调用具体的Controller;(Controller--> service --> Dao --> 数据库)
Controller执行完成后返回ModelAndView,
提示:Model(模型数据,即Controller处理的结果,Map) View(逻辑视图名,即负责展示结果的JSP页面的名字)
处理器适配器(HandlerAdapter)将controller执行的结果(ModelAndView)返回给前端控制器(DispatcherServlet);
(4)
.前端控制器(DispatcherServlet)将执行的结果(ModelAndView)传给视图解析器(ViewReslover)
视图解析器(ViewReslover)根据View(逻辑视图名)解析后返回具体JSP页面
(5)
.前端控制器(DispatcherServlet)根据Model对View进行渲染(即将模型数据填充至视图中);
前端控制器(DispatcherServlet)将填充了数据的网页响应给用户。
其中整个过程中需要开发人员编写的部分有Controller、Service、Dao、View;
*************************************************************************************************
在web.xml中配置前端控制器
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>test_springMVC</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <!-- 配置springmvc的前端控制器(DispatcherServlet),将请求交给springMVC处理--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 为servlet配置初始参数 --> <param-name>contextConfigLocation</param-name><!-- 固定写法 --> <param-value>classpath:springmvc-config.xml</param-value><!-- 配置springmvc核心文件的位置 --> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> <!-- /* : 拦截静态资源(html/css/js/图片/视频..), / : 拦截静态资源,但是不包括jsp --> </servlet-mapping> <!-- 配置过滤器,解决POST提交的中文参数乱码问题 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> <!-- 表示对所有请求进行过滤 --> </filter-mapping> </web-app>
配置springMVC核心配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) --> <mvc:default-servlet-handler/> <!-- 2.配置注解驱动,用于识别注解(比如@Controller) --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 3.配置需要扫描的包:spring自动去扫描 base-package 下的类, 如果扫描到的类上有 @Controller、@Service、@Component等注解, 将会自动将类注册为bean --> <context:component-scan base-package="com.tedu.controller"> </context:component-scan> <!-- 4.配置内部资源视图解析器 prefix:配置路径前缀 suffix:配置文件后缀 /WEB-INF/pages/home.jsp --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
实现controller
package com.tedu.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /* * 让spring框架创建spring实例,如果当前类所在的包配置了spring容器包扫描,具有 * 该注解的类,就会作为bean注册到spring容器中,由spring容器创建实例 */ @Controller /* * @RequestMapping("/hc") 加这行注解后,访问路径要加一层/hc * 要求controller内方法的访问路径不能冲突 */ public class HelloController { //为当前方法配置访问路径访问括号里的路径,就会执行这个方法 @RequestMapping("/hello") public String testHello() { System.out.println("hello springmvc..."); // 跳转到/WEB-INF/pages/home.jsp return "home"; } }
pojo类
package com.tedu.pojo; /* * 用于封装用户信息 */ public class User { private String name; private Integer age; private String addr; public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("setName..."+name); } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; System.out.println("setAge..."+age); } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; System.out.println("setAddr..."+addr); } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", addr=" + addr + "]"; } }
springMVC的参数绑定、跳转处理、响应数据
package com.tedu.controller; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import com.tedu.pojo.User; @Controller public class ParamController { /** * springmvc的参数绑定 */ /** * 1.简单类型的参数绑定 * http://localhost:8080/test_springMVC/testParam01?name=张飞&age=35&addr=河北 * 在方法上声明和请求参数同名的形参 */ @RequestMapping("/testParam01") public String testParam01(String name, Integer age, String addr) { System.out.println("ParamController.testParam01()"); System.out.println("name="+name+" age="+age+" addr="+addr); return "home"; } @RequestMapping("/testParam02") public String testParam02(String user, String psw, String[] like) { System.out.println("ParamController.testParam01()"); System.out.println("user="+user+" psw="+psw+" like="+Arrays.toString(like)); return "home"; } /** * 2.包装类型的参数绑定(将请求中的参数直接封装到对象中) * http://localhost:8080/test_springMVC/testParam03?name=赵云&age=28&addr=北京昌平 * 可以直接将请求中的参数封装到pojo对象中,寻找对应的setName setAge setAddr方法 */ @RequestMapping("/testParam03") public String testParam03(User user) { System.out.println(user); return "home"; } /** * 3.日期类型的参数绑定(可能出现400错误,表示参数类型不匹配) * http://localhost:8080/test_springMVC/testParam04?date=2020/08/24 9:39:50 * 传递日期参数时,springmvc默认用/分隔 * 在springmvc中,提供了@InitBinder注解,用于指定自定义的日期转换格式, * 在接受日期类型的参数时,会自动按照自定义的日期格式进行转换。 */ @RequestMapping("/testParam04") public String testParam04(Date date) { System.out.println(date); return "home"; } // 自定义日期格式转换器,将springmvc默认以斜杠(/)分隔日期改为以横杠分隔(-) @InitBinder public void InitBinder (ServletRequestDataBinder binder){ binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true) ); } /** * 4.乱码处理 * 接收请求中的user和like两个参数的值 * 使用get提交表单中的user和like,没有乱码 * 使用post提交出现乱码 * springMVC针对post提交中文参数有乱码提供了解决方案 * 只需要启用springMVC提供的乱码处理过滤器即可解决 */ @RequestMapping("/testParam05") public String testParam05(String user,String[] like) { System.out.println("user="+user); System.out.println("like="+Arrays.toString(like)); return "home"; } /** * 5.实现请求转发 * 转发是一次请求一次响应 * 转发前后地址栏不会发生变化 * 转发只能是同一个应用内部的资源跳转 * 转发可以和域对象配合使用,带数据到目的地 */ //1)从Controller中的方法直接转发到test.jsp @RequestMapping("/testForward") public String testForward(Model model) { System.out.println("testForward()..."); model.addAttribute("name","刘德华");//往request域中存数据(model底层就是request域) return "test";//返回值是jsp的名字,默认就表示转发到jsp } //2)从Controller中的方法转发到另外一个方法 @RequestMapping("/test02") public String test02(Model model) { System.out.println("test02()..."); model.addAttribute("age","30"); return "forward:testForward"; // forward: 是转发到方法的固定前缀 //从test02转发到testForward,再转发到test.jsp } /** * 6.实现重定向 * 重定向是两次请求,两次响应 * 重定向前后地址栏会发生变化 * 重定向没有限制,可以是同一个web应用或不用web应用之间的资源 * 重定向不能配合request域携带数据 */ //1)从Controller方法重定向到其他的Controller方法 @RequestMapping("/testRedirect") public String testRedirect() { System.out.println("testRedirect()..."); return "redirect:/testRedirect01"; // redirect: 重定向的固定前缀 // 重定向到 /testRedirect02 对应的方法 } @RequestMapping("/testRedirect01") public String testRedirect01() { System.out.println("testRedirect01()..."); return "test"; //转发到test.jsp } //2)从当前应用重定向到其他web应用 @RequestMapping("/testRedirect02") public String testRedirect02() { System.out.println("testRedirect02()..."); return "redirect:http://localhost:8080/test02/html01.html"; } //3)重定向到其他服务器 @RequestMapping("/testRedirect03") public String testRedirect03() { System.out.println("testRedirect03()..."); return "redirect:http://www.baidu.com"; } /** * 7.通过model响应数据(Model+转发带数据到jsp) */ @RequestMapping("/testModel") public String testModel(Model model) { //可以将数据存入Model中(底层是request域) User u1 = new User(); u1.setName("韩信"); u1.setAge(28); u1.setAddr("南京"); model.addAttribute("user", u1); return "test"; } }