文章目录
MVC模式
MVC是Model,View和Controller的缩写
模型:用于存储数据以及处理用户请求的业务逻辑
视图:向控制器提交数据,显示模型中的数据
控制器:根据视图提出的请求判断请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示
基于Servlet的MVC模式
模型:一个或多个javaBean对象
视图:一个或多个JSP页面
控制器:一个或多个Servlet对象
SpringMVC工作原理
Spring核心组件
DispatcherServlet : 前端控制器,接收所有请求(不包含jsp)
HandlerMapping: 解析请求格式的.判断希望要执行哪个具体的方法.
HandlerAdapter: 负责调用具体的方法.
ViewResovler:视图解析器.解析结果,准备跳转到具体的物理视图
执行流程
1.客户端请求提交到DispatcherServlet
2.由DispatcherServlet根据请求路径寻找一个或多个HandlerMapping,找到对应的HandlerAdapter
3.由HandlerAdapter执行对应的Controller
4.Controller调用业务逻辑,然后返回一个ModelAndView对象
5.DispatcherServlet寻找一个或多个ViewResovler视图解析器,找到ModelAndView指定的视图
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path=req.getServletPath();
System.out.println(path);
if(path.equals("/abc")){
show1(req, resp);
}
if(path.equals("/def")){
show2(req, resp);
}
if(path.equals("/ddd")){
show3(req, resp);
}
if(path.equals("/qqqq")){
show4(req, resp);
}
}
private void show1(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("show1");
req.getRequestDispatcher("/index.jsp").forward(req,resp);
}
private void show2(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("show2");
resp.sendRedirect("/login.jsp");
}
private void show3(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("show3");
}
private void show4(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("sho4");
}
}
每个show1,show2,…相当于一个Controller
原理基本类似,根据请求的路径名来调用对应的Controller
这里的if相当于HandlerMapping,只不过人家使用的是map
而调用方法不是像这样简单调用,而是通过HandlerAdapter去调用
返回请求转发或者重定向是通过ModelAndView和ViewResovler去完成
环境搭建
导入相应jar包
基于配置文件的SpringMVC
1.在web.xml中部署DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--部署DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param> <!--springmvc的配置文件 默认在web-inf下 这里重新配置位置 -->
<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>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
DispatcherServlet是基于Servlet的,所以需要在web.xml下进行配置
接下来需要配置springmvc的配置文件,如果没有在<servlet>
标签下进行配置<init-param>
,那么springmvc的配置文件就在WEB_INF下,并且配置文件名字为<servlet-name>
的内容加上 - servlet.xml
可以通过<init-param>
来配置springmvc的文件
2.创建controller
创建一个实现Controller接口的类
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginContorller implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
System.out.println("LoginContorller");
return new ModelAndView("/login.jsp");
}
}
`
返回值为ModelAndView,默认为请求转发
3.创建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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置controller-->
<bean name="login" class="cn.com.controller.LoginContorller"></bean>
<!--配置HandlerMapping接口-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 解析出来控制器逻辑名 -->
<!--key为控制器的逻辑名-->
<entry key="login" value-ref="login"></entry>
</map>
</property>
</bean>
<!--配置调用控制器的接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
</beans>
4.测试
index.jsp为测试页面
点击登陆后
登陆成功
注:
上述的配置是没有配置viewResolver的
可以配置viewResolver,其作用是在ModelAndView的内容加前缀和后缀
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置controller-->
<bean name="login" class="cn.com.controller.LoginContorller"></bean>
<!--配置HandlerMapping接口-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 解析出来控制器逻辑名 -->
<!--key为控制器的逻辑名-->
<entry key="login" value-ref="login"></entry>
</map>
</property>
</bean>
<!--配置调用控制器的接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
package cn.com.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginContorller implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
System.out.println("LoginContorller");
return new ModelAndView("login");
}
}
这时ModelAndView的内容可以只写名字,viewResolver将自动添加前缀和后缀
SpringMVC的容器与组件
spring的容器是ApplicationContext,SpringMVC的容器是ConfigurableWebApplicationContext
ApplicationContext和ConfigurableWebApplicationContext有什么关系?
查看ApplicationContext的结构
可以看出ApplicationContext有两个子类,而WebApplicatonContext是spirng整合web的容器
接下来就探究springmvc的容器和spring容器的关系
首先,基于spingmvc的原理,首先是要启动DispatcherServlet,而DispatcherServlet是基于serlvet的,所以它必定有init()方法,service()方法,destroy()方法
DispatcherServlet
首先来看DispatcherServlet
可以看出springmvc将创建自己的配置上下文,基于servlet的init-param
init-param的ContextConfigLoaction将指示一个被xmlwebapplicationContext加载的xml文件
所以可以通过init-param标签指示springmvc的配置文件的位置
1.init()
1.在DispatcherServlet的源码里并没有Init()方法,所以找寻它的父类FrameworkServlet
2.DispatcherServlet的父类FrameworkServlet也没有Init()方法,继续找寻FrameworkServlet的父类
3.在HttpServletBean有init()方法并且是final的
在init()方法中有个initServletBean的方法,查看结构可以看出实现该方法的有FramworkServlet和ResourceServlet,而FramworkServlet是HttpServletBean的子类所以是调用的FramworkServlet中的initServletBean
4.进入FramworkServlet的initServletBean方法
5.进入到initWebApplicationContext方法中
getServletContext()获取的是spring容器
由WebApplicationContext rootContext=WebApplicationContextUtils.getWebApplicationContext(getServletContext());可以看出spring容器整合为WebApplicationContext
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
由这三行代码可以看出webApplicationContext强制转换为了ConfigurableWebApplicationContext
cwac.setParent(rootContext);
由这行代码可以看出ConfigurableWebApplicationContext 的父容器就是webApplicationContext也就是ApplicationContext
所以SpringMVC容器是Spring容器的子容器,SpringMVC容器是可以获取到Spring容器的内容
2.service()
在DispatcherServlet中没有service()方法,所以到其父类FrameworkServlet中找
1.可以在FrameworkServlet中找到service()方法
2.进入到processRequest
在processRequest中调用了doService方法
而在frameworkservlet中是一个抽象方法,所以它会调用子类的方法,也就是DispatcherServlet的方法
3.进入DispatcherServlet的doService
4.进入doDispatch
查看getHandler方法
可以看出是通过HandlerMapping获取Handler
再查看getHandlerAdapter方法
返回了对应的HandlerAdapter
继续查看doDispatch
可以看出通过handler执行了handle方法,并返回了ModelAndView的一个对象
所以看出springMVC的执行是在DispatcherServlet的doservie()方法中,而不同的类它们的分工是不同的
所以具体流程就是
1.客户端请求提交到DispatcherServlet
2.由DispatcherServlet根据请求路径寻找一个或多个HandlerMapping,找到对应的HandlerAdapter
3.由HandlerAdapter执行对应的Controller
4.Controller调用业务逻辑,然后返回一个ModelAndView对象
5.DispatcherServlet寻找一个或多个ViewResovler视图解析器,找到ModelAndView指定的视图
HandlerMapping
在DispatcherServlet中,通过请求可以获取一个HandlerMapping,而handler是如何找寻到HandlerAdpter
HandlerMapping是一个接口,进入一个具体的实现类
因为在配置文件中配置的是SimpleUrlHandlerMapping,所以查看SimpleUrlHandlerMapping
在实现类中有一个map结构,所以这可以和springmvc配置文件对应起来
<!--配置HandlerMapping接口-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 解析出来控制器逻辑名 -->
<!--key为控制器的逻辑名-->
<entry key="login" value-ref="login"></entry>
</map>
</property>
</bean>
1.进入其初始化方法
2.进入registerHandlers方法
enty.getKey()就是配置文件里配置的key
enty.getValue就是配置文件里配置的value
可以看出不管有没有以"/"开头,它都会自动加上
3.进入registerHandler方法
可以看出它从spring容器中拿取了对象,对象就是配置文件里的value-ref
接下来就是找寻对应HandlerAdpater,可以看出它是有默认的HandlerAdpater的,在springMVC中,HandlerAdpater和ViewResolver如果没有配置是有默认的
HandlerAdpter
HandlerAdpter的方法很简单,DispatcherServlet调用了handle,所以来看handle()方法
1.handle()方法
HandlerAdpter是一个接口,所以去看其实现类的接口,因为在配置文件中配置的是SimpleControllerHandlerAdapter,所以查看SimpleControllerHandlerAdapter
它强制转化为了Controller,并调用了Controller的handlerRequest()方法,而自己写的控制器类是实现了Controller接口的
2.进入Controller接口handlerRequest()方法
此时查看结构可以看到是有自己写的实现类的,也就是走到这它就去调用了自己写的实现类
基于注解的SpringMVC
1.配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--部署DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param> <!--springmvc的配置文件 默认在web-inf下 这里重新配置位置 -->
<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>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.配置springMVC.xml
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描注解-->
<context:component-scan base-package="cn.com.controller"></context:component-scan>
<!--注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
3.配置Controller
package cn.com.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginContorller {
@RequestMapping("login")
public String login(){
System.out.println("登陆");
return "/login.jsp"; //如果配置了viewResolver,可以只写一个login
}
}
在控制器类上配置@Controller
在其处理方法上配置@RequestMapping
Controller的参数
把内容写到方法参数中,SpringMVC 只要有这个内容就自动注入内容.
基本数据类型
1.默认保证参数名称和请求中传递的参数名相同
<form action="login" >
姓名:<input name="name" ></br>
年龄:<input name="age"></br>
<input type="submit">
</form>
写了一个表单
@Controller
public class LoginContorller {
@RequestMapping("login")
public String login(String name,int age){
System.out.println(name+" "+age);
return "/login.jsp";
}
}
在方法中获取参数
可以看出是获取到了的
这是因为在请求参数中有name和age,springMVC会根据名称自动进行注入,如果找不到就注入null
<form action="login" >
姓名:<input name="name11" ></br>
年龄:<input name="age11"></br>
<input type="submit">
</form>
可以看出报错,报错的原因不是找不到,而是因为age是个Int类型,而springmvc无法找到参数内容的话会自动注入null,而nul无法赋值给Int
2.果请求参数名和方法参数名不对应使用@RequestParam()赋 值
<form action="login" >
姓名:<input name="name11" ></br>
年龄:<input name="age11"></br>
<input type="submit">
</form>
@Controller
public class Contorller {
@RequestMapping("login")
public String login(@RequestParam("name11") String name, @RequestParam("age11") int age){
System.out.println(name+" "+age);
return "/login.jsp";
}
}
获取成功
3.@RequestParam 设置默认值.
如果方法参数是基本数据类型(不是封装类)注入Null时会报错,此时可以通过@RequestParam 设置默认值.
<form action="login" >
姓名:<input name="name11" ></br>
年龄:<input name="age11"></br>
<input type="submit">
</form>
@Controller
public class Contorller {
@RequestMapping("login")
public String login(@RequestParam(defaultValue = "zhangsan") String name, @RequestParam(defaultValue ="20") int age){
System.out.println(name+" "+age);
return "/login.jsp";
}
}
可以看出参数名没有对应,但是没报错,输出默认值
4.强制设置参数
@Controller
public class Contorller {
@RequestMapping("login")
public String login(@RequestParam(required = true) String name, @RequestParam(defaultValue ="20") int age){
System.out.println(name+" "+age);
return "/login.jsp";
}
}
required = true表示必须有参数
可以看出直接没有执行到对应的方法
直接在浏览器返回错误,而服务器没有500
参数是对象
1.请求参数名和对象属性名相同
<form action="login" >
姓名:<input name="name" ></br>
年龄:<input name="age"></br>
<input type="submit">
</form>
建立实体类
package cn.com.pojo;
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
属性和请求参数对应
@Controller
public class Contorller {
@RequestMapping("login")
public String login(Student student){
System.out.println(student);
return "/login.jsp";
}
}
可以看出自动注入值了
如果对象的属性和请求参数对应,springMVC自动注入值,调用的是getset方法
2.请求参数包含多个同名参数
<form action="login" >
姓名:<input name="name" ></br>
年龄:<input name="age"></br>
打球<input type="checkbox" name="hobby" value="1">
绘画 <input type="checkbox" name="hobby" value=2">
电影<input type="checkbox" name="hobby" value="3"></br>
<input type="submit">
</form>
hobby是同名参数
@Controller
public class Contorller {
@RequestMapping("login")
public String login(Student student,@RequestParam("hobby") List<String> hobby){
System.out.println(student+""+hobby);
return "/login.jsp";
}
}
使用List获取同名参数,并在前面加上@RequestParam
获取成功
3.类中包含对象
package cn.com.pojo;
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package cn.com.pojo;
public class People {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "People{" +
"student=" + student +
'}';
}
}
@Controller
public class Contorller {
@RequestMapping("login")
public String login(People people){
System.out.println(people);
return "/login.jsp";
}
}
<form action="login" >
姓名:<input name="student.name" ></br>
年龄:<input name="student.age"></br>
<input type="submit">
</form>
使用对象名.属性注入值
4.restful传值
request获取参数一般都是键值对的形式
如 /login?name=xxx&age=xxxx
使用restful方法传值就不是键值对
请求格式使用特殊格式
<a href="login/kryie/11">跳转</a>
@Controller
public class Contorller {
@RequestMapping("login/{name}/{age}")
public String login(@PathVariable String name,@PathVariable int age){
System.out.println(name+" "+age);
return "/login.jsp";
}
}
@RequestMapping 中一定要和请求格式对应
{} 中名称自定义名称
@PathVariable 获取@RequestMapping 中内容,默认按照 方法参数名称去寻找
@PathVariable(“指定名称”)可以指定去寻找名称
跳转方式
SpringMVC默认跳转方式是请求转发.
可以通过设置返回字符串的内容设置跳转方式
@Controller
public class Contorller {
@RequestMapping("login")
public String login(String name,int age){
System.out.println(name+" "+age);
return "redirect:/login.jsp";
}
}
重定向
@Controller
public class Contorller {
@RequestMapping("login")
public String login(String name,int age){
System.out.println(name+" "+age);
return "forward:/login.jsp";
}
}
请求转发
@ResponseBody
在方法上只有@RequestMapping 时,无论方法返回值是什么认为需要跳转
1.普通字符串
.在方法上添加@ResponseBody时,恒不跳转,直接返回对应的值
@Controller
public class Contorller {
@RequestMapping("login")
@ResponseBody
public String login(String name,int age){
System.out.println(name+" "+age);
return name+" "+age;
}
}
2.返回对象
返回值为对象时,@ResponseBody将对象转化为json字符串
在使用@ResponseBody时需要导入jackson的jar包
@Controller
public class Contorller {
@RequestMapping("login")
@ResponseBody
public Student login(){
Student student=new Student();
student.setName("Zhangsan");
student.setAge(11);
return student;
}
}
如果返回值包含中文,出现中文乱码 ,则在RequestMapping中添加属性
@RequestMapping(value="login",produces="application/json; charset=utf-8")
produces 表示响应头中 Content-Type 取值.