1、底层原理
springmvc框架由DispatchServlet,handlerMapping,handlerAdapter,和视图解析器组成
- DispatchServlet为springmvc核心拦截器,拦截所有请求并解析url(例如:http://localhost:8080/demo/example/model,截取时候,去掉协议,去掉端口号,去掉工程名,即为example/model)
- handlerMapping为处理器映射(通过处理器映射,你可以将web请求映射到正确的处理器(handler)上。Spring内置了很多处理器映射策略),HandlerMapping是把一个URL指定到一个Controller上,就像应用系统的web.xml文件使用<servlet-mapping>将URL映射到servlet
- handlerAdapter(HandlerAdapter字面上的意思就是处理适配器,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。)
- 视图解析器通过返回值找到相应的页面进行转发
springmvc框架业务逻辑
- 由用户发送请求到DispatchServlet,DispatchServlet通过url找到handlerMapping(handler Mapping可以理解为一个map,储存的是键值对,此处为key为url和value为一个handler类型的对象,通过url来寻找对象,handler中储存的为类名,方法名,参数列表,有这三个即可通过反射调用)
- 如果第一步找不到即报404,能找到的话,则将handler对象返回给DispatchServlet
- DispatchServlet将获取到的handler对象交给handlerAdapter,handlerAdapter拿出类名,方法名,参数列表进行调用
- 将handlerAdapter调用的返回值给DispatchServlet
- DispatchServlet将第四步的返回值交还给视图解析器
- 视图解析器通过返回值找到相应的页面进行转发(一定是转发,不是重定向)
2、搭建springmvc框架
(1)导包
导入maven包的网址——http://mvnrepository.com/
maven中如果只搭建springmvc,只需要在pom.xml中导入两个包,spring-mvc和spring-webmvc
这里说一下,新建的maven war项目会报错,因为webapp下缺少WEB-INF目录和web.xml文件,需要手动添加
导入包
<!--pom.xml文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>springmvc0815</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>4.1.6.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-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
spring包不管用那个版本的一定要同一版本好,此处我做了版本管理,方便以后修改spring版本号
(2)配置DispatchServlet
配置Servlet有两种方法,配xml或者配webservlet注解,但是此处DispatchServlet为maven提供的jar包,如果要加注解,则需要修改源码重新编译,所以此处我们选择配置xml文件。
servlet为单实例,多线程,对象是在首次访问的时候创建出来的,也就是说第一个访问的人,速度要慢一些,为了提高性能,我们要让他在服务器启动的时候就创建对象
添加<load-on-startup>1</load-on-startup>
<!--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_3_0.xsd" id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
启动,会发现控制台报错
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/springmvc-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:344)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:452)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:663)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:629)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:677)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:548)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:489)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1188)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1132)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1021)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5085)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5397)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1410)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1400)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330)
... 28 more
Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml],找不到文件
我们回过头来看springmvc的底层原理,第一步用户发送请求给dispatchservlet,dispatchservlet拿url去handlermapping寻找,前提是handlermapping里面要有内容,那我们考虑如何往handlermapping中放东西
框架在于可供所有人使用,所以框架的搭建者不知道你会向handler mapping中放什么内容,这个时候,他都去了一个配置文件,spring-servlet.xml,使用框架的人需要往handlermapping中放什么,自己去配置文件中写,而spring-servlet.xml默认放在/WEB-INF目录下
那么文件名和路径能不能修改,我们来看dispatchservlet源码
/**
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
* given the name "test" in a context, the namespace used by the servlet will
* resolve to "test-servlet".
*/
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
/**
* Set a custom namespace for this servlet,
* to be used for building a default context config location.
*/
public void setNamespace(String namespace) {
this.namespace = namespace;
}
/**
* Return the namespace for this servlet, falling back to default scheme if
* no custom namespace was set: e.g. "test-servlet" for a servlet named "test".
*/
public String getNamespace() {
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
如果namespace为空,则配置文件名为servlet-name加后缀"-servlet"
如果namespace不为空,则配置文件名为namespace加后缀"-servlet"
所以我们想修改名字需要配namespace
<!--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_3_0.xsd" id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>namespace</param-name>
<param-value>springmvc</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>
load-on-startup一定要配在servlet节点的最后一行,不然会报错
(3)配置springmvc.xm
首先,在WEB-INF下创建springmvc.xml,要带spring的命名空间
<!--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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
">
</beans>
- 开启注解驱动<mvc:annotation-driven></mvc:annotation-driven>
- 包扫描<context:component-scan base-package="com.test.controller"></context:component-scan>,会扫描包下所有的类,在需要被扫描到的类上加@controller注解,在需要被扫描到的方法上加@RequestMapping注解。
<!--MyController.java-->
package com.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping("/index")
public String index(){
return "abcd";
}
}
- 此时,加了@controller注解的类MyController会被扫描到,扫描完后,会继续扫描类中的方法,什么样的方法能被放到handlerMapping中呢,方法要对应url,我们就把url加在方法上,他的注解叫@RequestMapping,他就相当于webservlet,例如此方法中加了@RequestMapping("/index")注解,当输入/index地址的时候,在服务器启动的时候,handlerMapping开始加载,读取配置文件,看到requestMaping注解,就会把“/index"作为key,然后把获取到当前的类名,方法名,参数列表作为value封装到handler对象中
- 配置视图解析器,也就是一个bean结点。需要配置三个属性viewClass(视图类型)——jstl类型。prefix(前缀),suffix(后缀)
<!--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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd "> <mvc:annotation-driven></mvc:annotation-driven> <context:component-scan base-package="com.test.controller"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <property name="prefix" value="/WEB-INF/pages"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
此时我们依赖jstl,需要在pox.xml中导入jstl包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
关于前缀和后缀:接着上面的流程,我们拿到返回值/abcd,handlerAdapter将返回值(/abcd)给到视图解析器后,会把前缀(/WEB-INF/pages)放到返回值前,把后缀(.jsp)放到返回值后,以webapp为根目录去找这个文件(/WEB-INF/pages/abcd.jsp),进行转发
3、框架的使用
(1)controller往作用于中加载
使用modelandview或者modelmap
@Controller
public class MyController {
//方法1
@RequestMapping("/index")
public String list(ModelMap map){
map.addAttribute("attributeName", attributeValue);
}
//方法2
@RequestMapping("/index2")
public ModelAndView list(){
ModelAndView modelAndView = new ModelAndView("abcd");//abcd为要转发的页面
modelAndView.addObject("attributeName", attributeValue);
return modelAndView;
}
}
modelmap底层仍会封装出modelandview
另:@RequestMapping("index")默认为value属性,get和post方法都会进入此方法
@RequestMapping(value="/index2",method=RequestMethod.POST)只会接受post方法
@RequestMapping(value="/index2",method=RequestMethod.GET)只会接受get方法
(2)controller中接参数
//方法1
@RequestMapping(value="/index2",method=RequestMethod.POST)
public String index(String name){
System.out.println(name);//只需要与前端那么属性值一样即可接收
return "abcd";
}
//方法2
//假设pojo中存在Student类型的对象,具有name,sex,age三个属性
@RequestMapping(value="/index2",method=RequestMethod.POST)
public String index(Student student){
System.out.println(student);//只要pojo对象的属性名,与前端页面的属性名一致,直接用对象接受即可
return "abcd";
}