1、原理
1. 客户端请求提交到DispatcherServlet
2. 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
3. DispatcherServlet将请求提交到Controller
4. Controller调用业务逻辑处理后,返回ModelAndView
5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
6. 视图负责将结果显示到客户端
2、环境搭建
1)、添加jar包依赖:
在项目的WEB-INF下新建一个lib文件夹,将以下jar包添加进去,并且add as libraries:
spring-aop-4.0.4.RELEASE.jar
spring-beans-4.0.4.RELEASE.jar
spring-context-4.0.4.RELEASE.jar
spring-core-4.0.4.RELEASE.jar
spring-expression-4.0.4.RELEASE.jar
spring-web-4.0.4.RELEASE.jar
spring-webmvc-4.0.4.RELEASE.jar
commons-logging-1.1.1.jar(用来打印log)
2)、添加资源目录
在项目下新建一个文件夹config,转成资源目录,创建文件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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.neusoft.controller"></context:component-scan>
<!-- 配置视图解析器 如何把handler 方法返回值解析为实际的物理视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<-- 给所有return “”里面的内容添加前缀和后缀 -->
<property name = "prefix" value="/WEB-INF/views/"></property>
<property name = "suffix" value = ".jsp"></property>
</bean>
</beans>
3)、配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- 配置DispatchcerServlet -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置Spring mvc下的配置文件的位置和名称 -->
<init-param>
<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>springDispatcherServlet</servlet-name>
<!-- 这里的servlet-mapping表示拦截的模式,这里是“*.do”,表示对于.do结尾的请求进行拦截 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
理解:
根据网页上发送过来的请求,去实例化springDispatcherServlet类,然后根据init-param里面的param-value的值去找到自动化配置的包,然后在找到XX.do方法,完成相应的操作
4)通过上述方法进入hello.jsp
1、先创建一个com.neusoft.controller包
2、在包下创建一个HelloController类
3、在类的前面添加“@Controller”注解,表示是spring的控制器
4、在方法前面添加对应的@RequestMapping("hello.do"),用于匹配路径;
代码实现:
@Controller
public class HelloController {
@RequestMapping("hello.do")
public String hello(){
System.out.println("hello");
return "hello";
}
}
使用:启动tomcat服务器,在网页上请求"localhost:8080/hello.do",通过mvc框架匹配到hello方法,然后通过return返回值里面的内容,进入WEB-INF/jsp/hello.jsp
3、Controller方法的返回值
1)、ModelAndView
ModelAndView中的Model代表模型,View代表视图。
业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。
框架通过调用配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据显示在指定的页面上。
代码实现:
@Controller
public class HelloController {
@RequestMapping("hello.do")
public ModelAndView hello(){
System.out.println("hello");
ModelAndView modelAndView=new ModelAndView();
// 在View属性中设置要跳转的view
modelAndView.setViewName("hello");
// 存储值到Model出行中
modelAndView.addObject("username","zhangsan");
return modelAndView;
}
}
2)、String
String可以有三种类型:
1、普通字符串:表示视图的名,由于前面配置了同意的前缀后缀,所以此处的字符串真实值是:“前缀”+视图名+“后缀”;
@Controller
public class HelloController {
@RequestMapping("world.do")
public String world(Model model) {
System.out.println("world");
// 往model属性中传值
model.addAttribute("username","lisi");
// 返回到WEB-INF/jsp/world.jsp中,带着model属性值
return "world";
// 错误跳转,404
// return "world2.do";
}
@RequestMapping("world2.do")
public String world2(){
// 返回到WEB-INF/jsp/world.jsp中
return "world";
}
}
2、redirect重定向:redirect的特点和servlet一样,对比response.serndRedirect(""),使用redirect进行重定向那么地址栏中的URL会发生变化,不传request值,走doGet请求,相对较慢,因为实际上是两次跳转,也叫外部跳转
@Controller
public class HelloController {
@RequestMapping("world.do")
public String world(HttpServletResponse response) {
System.out.println("world");
// 通过response自带的方法重定向,使用此方法最好将String累心该方法改成void;
// response.sendRedirect("world2.do");
// 重定向,只能往XX.do方法跳,因为写在WEB-INF下的jsp不能通过网页访问直接进去,如果要传值也不能再用request了,要使用session
return "redirect:world2.do";
}
@RequestMapping("world2.do")
public String world2(){
return "world";
}
}
3、forward转发:对比servlet中的request.sendDisparchar("路径").forward(request,response),地址栏中的URL不会发生改变,能传request值,不能跳站外,相对要快,因为是一次跳转,也称内部跳转
@Controller
public class HelloController {
@RequestMapping("world.do")
public String world(Model model) {
System.out.println("world");
// 往request作用域存值
request.setAttribute("username","wangwu");
// 内部跳转:内部跳转时不再自动加前缀后缀,需要自动补全
// return "forward:WEB-INF/jsp/world.jsp";
// 内部跳转:往其他方法里面跳
return "forward:world2.do";
// 以上两种跳转均带request作用域值
}
@RequestMapping("world2.do")
public String world2(){
return "world";
}
}
2)、void
可以使用Controller方法的HTTPServletRequest和HTTPServletResponse对象进行请求的接收和响应
1)使用request转发页面:
request.getRequestDispatcher("转发路径").forward(request,response);
2)使用response进行页面重定向
response.sendRedirect("重定向路径");
3)也可以使用response指定响应结果:
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().println("json串");
代码实现:
@Controller
public class LoginController {
@RequestMapping("login.do")
public void login(HttpServletResponse response) throws IOException {
System.out.println("login");
response.setContentType("text/html;charset=utf-8");
// 往页面写内容
response.getWriter().println("Login!");
response.getWriter().println("登录!");
}
}
4、SpringMVC的各种参数绑定方式
1)、基本数据类型
form表单提交
<form method="post" action="${pageContext.request.contextPath}/hello.do">
姓名:<input type="text" name="username">
籍贯:<input type="text" name="city">
年龄:<input type="text" name="age">
电话:<input type="text" name="telephone">
<input type="submit" value="提交">
</form>
controller部分代码:
@RequestMapping("count.do")
public void count(String username, String city , Integer age ,String telephone) {
System.out.println(username);
System.out.println(city);
System.out.println(age);
System.out.println(telephone);
}
imput部分的值对应的name与count方法传递的参数变量名保持一致,注意如果表单提交的值是空,如果使用int基本类型去接,就会出现数据转换异常,所以最好使用Integer包装类
2)、自定义对象类型
创建一个com.neusoft.dto包,定义一个User类,属性名称与input里面的name对应
package com.neusoft.dto;
public class User {
private String username;
private String city;
private Integer age;
private String telephone;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
}
表单和前面的一样
controller部分代码:
@RequestMapping("hello.do")
public ModelAndView hello(User user){
System.out.println("hello");
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("hello");
System.out.println(user.getUsername());
modelAndView.addObject("user",user);
return modelAndView;
}
hello.jsp部分代码:
<h3>大家好,我叫${user.username} ,来自${user.city} ,今年${user.age}岁,电话号码${user.telephone} </h3>
5、解决乱码问题
上面的扁担提交有可能出现乱码问题,因为传递中文了,下面我们就来解决乱码,在web.xml中添加自带的过滤器,代码如下
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6、RESTful架构
主要实现从url传递参数
1)、修改web.xml,添加DispatcherServlet的Restful配置
<servlet>
<servlet-name>springmvc-servlet-rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc-servlet-rest</servlet-name>
<-- 注意这里发生了变化,由之前的*.do变成了/ -->
<-- <url-pattern>*.do</url-pattern>-->
<url-pattern>/</url-pattern>
</servlet-mapping>
注意这里url-pattern发生了变化,由之前的*.do变成了/,意味着会对所有的请求的路径进行拦截,所以如果再使用/表示路径,比如进入locahost:8080/abc.jsp也会进行拦截,会把abc.jsp当做是一个指令,然后去springmvc中context配置的自动扫描包下面去寻找,没有找到就会直接报404错误,但是这个也有办法解决,这就涉及到后面会写到的静态资源访问。
2)、jsp部分代码:
<a href="${pageContext.request.contextPath}/count/3/张三">url参数传递</a>
3)controller部分:
/*@RequestMapping("count/{id}")
count后面{}里面内容与方法里面的参数名字不一样时写法
public String count(@PathVariable("id") Integer num){}*/
@RequestMapping("count/{num}/{name}")
public String count(@PathVariable Integer num,@PathVariable String name){
System.out.println(num);
System.out.println(name);
return "world";
}
7、静态资源访问<mvc:resources>
前面我们已经将DispatcherServlet中的url-pattern设置为 /,所以如果访问css,js等目录下的内容也会被DispatcherServlet拦截,而我们可以通过在springmvc.xml中配置<mvc:resources>,来使得这些文件跳过拦截
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/css/" mapping="/css/**"/>
由于以后静态文件可能会有很多个,为了不一一配置,所以我建了一个static文件夹,将这些静态文件豆放在一个文件夹下面,这个文件夹最好直接建在web下面,而不是WEB-INF下面,因为WEB-INF是受保护的文件夹,里面的资源不能够被网页直接访问,只能通过内部访问
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:resources mapping="/static/**" location="/static/"/>
我们也可以将我们的index欢迎页面保护起来,使得不能通过浏览器直接访问,首先将index放入WEB-INF/jsp目录下,然后在web.xml中添加配置,代码如下:
<!--修改起始页面位置-->
<welcome-file-list>
<welcome-file>/WEB-INF/jsp/index.jsp</welcome-file>
</welcome-file-list>
8、文件上传
首先在springmvc.xml中添加配置,设置文件的mapper
<!-- 文件上传mapper -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="80000"></property>
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
然后在jsp中写文件上传的代码,文件提交也是用表单提交,但是要添加一个enctype="multipart/form-data"属性,代码如下:
<%--文件上传表单--%>
<form method="post" action="${pageContext.request.contextPath}/unload" enctype="multipart/form-data">
username:<input name="username" type="text">
<%--文件上传--%>
<input type="file" name="pic">
<input type="submit" value="submit">
</form>
controller部分代码:
@Controller
public class UnloadController {
@RequestMapping("unload")
public void unload(String username, @RequestParam MultipartFile pic, HttpServletRequest request) throws IOException {
System.out.println(username);
String fileName=pic.getOriginalFilename();
ModelAndView modelAndView=new ModelAndView();
if(pic.getSize()>0){
String realPath=request.getServletContext().getRealPath("/static/img")+File.separator;
File files=new File(realPath);
if(!files.exists()){
files.mkdirs();
}
System.out.println(realPath);
File file=new File(realPath+fileName);
pic.transferTo(file);
System.out.println(fileName);
//这种形式下,默认会返回unload.jsp
}
}
}
我们传上来的图片也可以实时的显示在我们的网页上,在某一网页上添加一行代码,这里用index.jsp做示范:
<%--从图片的存储位置,根据图片名字取出图片--%>
<img src="${pageContext.request.contextPath}/static/img/${picname}" name="picc">
controller部分的修改:
@Controller
public class UnloadController {
@RequestMapping("unload")
//返回类型做了修改,指定返回某一页面
public ModelAndView unload(String username, @RequestParam MultipartFile pic, HttpServletRequest request) throws IOException {
System.out.println(username);
String fileName=pic.getOriginalFilename();
ModelAndView modelAndView=new ModelAndView();
if(pic.getSize()>0){
String realPath=request.getServletContext().getRealPath("/static/img")+File.separator;
File files=new File(realPath);
if(!files.exists()){
files.mkdirs();
}
System.out.println(realPath);
File file=new File(realPath+fileName);
pic.transferTo(file);
System.out.println(fileName);
// 指定返回WEB-INF/jsp/index.jsp页面
modelAndView.setViewName("index");
// 传递picname的值,使得当前图片能在跳转后的页面显示出来
modelAndView.addObject("picname",fileName);
}
return modelAndView;
}
}
9、拦截器
可以对指定的方法进行拦截,首先在springmvc.xml中添加配置,代码如下:
<mvc:interceptors>
<!--如果配置了多个拦截器,则按顺序执行 -->
<!-- 登陆认证拦截器 -->
<mvc:interceptor>
<!-- /**表示所有url包括子url路径 -->
<!-- 拦截的方法,对所有带/admin/的指令进行拦截,并在对应的class做出处理 -->
<mvc:mapping path="/admin/**"/>
<!-- 对应的拦截类,做出处理 -->
<bean class="com.neusoft.interceptor.FirstInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
拦截器可以有多个,写在前面的先执行,前面的执行完就忘后面的拦截器里面跳
新建一个com.neusoft.interceptor包,新建一个FirstInterceptor类,实现一个HandlerInterceptor接口
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
// return false;
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
对应的controller也需要修改,代码如下:
@Controller
@RequestMapping("admin")
public class HelloController {
/*@RequestMapping("count/{id}")
public String count(@PathVariable("id") Integer num){}*/
@RequestMapping("count/{num}/{name}")
public String count(@PathVariable Integer num,@PathVariable String name){
System.out.println(num);
System.out.println(name);
return "world";
}
}
对应jsp中使用的方法:
<a href="${pageContext.request.contextPath}/admin/count/3/长大">计数</a>
理解:当服务器发出计数请求,服务器就会识别是否属于拦截器的拦截对象,是就会对这个方法进行拦截,然后在拦截器中的preHandle方法当进行操作,然后再运行controller类当中的方法,之后再运行postHandle,afterCompletion方法,然后结束接着拦截器,返回jsp
现在补充一个人放到链接:
首先配置springmvc.xml
<mvc:interceptors>
<!--如果配置了多个拦截器,则按顺序执行 -->
<!-- 登陆认证拦截器 -->
<mvc:interceptor>
<!-- /**表示所有url包括子url路径 -->
<!-- 拦截的方法 -->
<mvc:mapping path="/**"/>
<!-- 对应的拦截类,做出处理 -->
<mvc:exclude-mapping path="/gologin/**"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/login/**"></mvc:exclude-mapping>
<bean class="com.neusoft.interceptor.FirstInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
jsp部分代码:
现在index.jsp做一个登录入口:
<a href="${pageContext.request.contextPath}/gologin">登录</a>
<h5>你好${username}</h5>
然后写一个登录页面:
<body>
<form method="post" action="${pageContext.request.contextPath}/login" >
username:<input type="text" name="username">
password:<input type="password" name="password">
<input type="submit">
</form>
然后写对应的controller方法:
@Controller
public class LoginController {
@RequestMapping("gologin")
public String gologin(HttpServletResponse response) throws IOException {
System.out.println("gologin");
return "login";
}
@RequestMapping("login")
public String login(String username, String password, HttpServletRequest request){
System.out.println("login");
HttpSession session=request.getSession();
if(username.equals("admin")&&password.equals("123456")){
session.setAttribute("username",username);
return "index";
}
return "login";
}
}
interceptor:
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
HttpSession session=httpServletRequest.getSession();
if(session.getAttribute("username")!=null){
return true;
}else {
httpServletRequest.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(httpServletRequest,httpServletResponse);
}
return false;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
然后就能实现拦截器的功能
网友评论