Springmvc框架

2018-11-26  本文已影响0人  小月半会飞

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 {

    }
}

然后就能实现拦截器的功能

上一篇 下一篇

猜你喜欢

热点阅读