• 初学 Spring MVC(基于 Spring in Action)


    Spring MVC(Model-View-Controller)

    当你看到本博文时,我猜你可能正面临着我已探索过的问题。

    同其他博主一样,我先按照书上详细的介绍一下 Spring MVC,也是为了自己能理解的更深刻一些。

    一、跟踪 Spring MVC 的请求

    每当我们碰到各种问题时,我们总是会向浏览器求助。

    正如你在百度里查找资料一样,搜索一下问题,点击一个链接,再查看里面的内容。

    1、在请求离开浏览器时,总会带有你发出请求的信息,比如你可能刚刚在搜 Spring MVC,这就是信息。

    2、请求的第一站是前端控制器,在 Spring MVC 中,它是 DispatcherServlet。

         它的作用是把请求发送到合适的 Spring MVC 控制器。

          因为有各种各样的请求需要控制器去处理,所以前端控制器是把请求发送到一个合适的控制器中。

    3、用户发送了一个请求,当然想看的是结果了。所以控制器处理完请求后将会返回一些信息以便能让用户看见。

          这些希望被用户看见的信息称为模型(Model)。

          当然用户肯定是想看的舒服一点,所以这些信息需要发送到视图(View)时显示,通常是 JSP。

    4、控制器的事情还没完,它的实际返回值是一个视图名,将视图名和模型数据传递给前端控制器(DispatcherServlet)。

    5、既然知道由哪个视图显示用户希望看到的结果,那么请求的最后一站也就是视图的实现了。

    二、搭建 Spring MVC

    既然原理已经讲清楚了,那么接下来看个例子才能更好理解!

    我们第一步也就是来配置上面所说到的东西,按照传统方式,通常是使用 web.xml 来配置的。

    但是作者说的使用 JavaConfig 更好一些。那我们可以对比一下这两种方式。

    1、配置 DispatcherServlet

    package spittr.config;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
    {
    
        @Override
        protected Class<?>[] getRootConfigClasses() 
        {
            return new Class<?>[] { RootConfig.class };
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() 
        {
            return new Class<?>[] { WebConfig.class };
        }
    
        @Override
        protected String[] getServletMappings() 
        {
            return new String[] { "/" };
        }
    
    }

    正如书本提到的,扩展 AbstractAnnotationConfigDispatcherServletInitializer 类的任意类---

    都会自动配置 DispatcherServlet 和 Spring 应用上下文。

    至于怎么配置可以看到下面重写的三个函数。

    函数 getServletMappings 会处理所有的请求,也就是用户发出的请求都交由 SpringMVC 来处理。

    函数 getRootConfigClasses 会根据 RootConfig 类来配置 ContextLoaderListener 创建的应用上下文的 bean。

    函数 getServletConfigClasses 会根据 WebConfig 类来配置 DispatcherServlet 应用上下文的 bean。

    另外配置类都要带有 @Configuration 注解,为了能让组件扫描知道这是配置类。

    2、接下来看下这两个类的内容

    package spittr.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan("spittr.web") public class WebConfig { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; } }

    该类的 Configuration 需要导入 org.springframework.context.annotation.Configuration;

    @EnableWebMvc 说明需要启用 SpringMVC,

    需要导入 org.springframework.web.servlet.config.annotation.EnableWebMvc;

    @ComponentScan 注解说明启用组件扫描,扫描的包为 spittr.web,

    需要导入 org.springframework.context.annotation.ComponentScan;

    书中说的处理静态资源可能不太好,需要继承 WebMvcConfigureAdapter 并重写 configureDefaultServletHanding 方法。

    目前我还不太清楚哪里不好,所以这里把它删掉。

    在这里可以看到有个视图解析器,它的作用是给控制器返回的视图名加个前缀和后缀,以便知道视图文件在哪里。

    在该函数前面加了个@bean 注解,是为了将起加入到 spring 容器中去,不然该视图解析器不会生效。

    3、接下来看下 RootConfig 的内容

    package spittr.config;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    
    @Configuration
    @ComponentScan(basePackages = { "spittr" }, excludeFilters = {
            @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) })
    public class RootConfig 
    {
    
    }

    在这里我详细的说一下@ComponentScan的作用:
    在配置类上配置 @ComponentScan 注明扫描的包名,该包及其子包下的类中,

    只要代码中的类标注了 @Controller、@Service、@Repository、@Component 都会将其加载到 spring容器中。

    当然 @bean 是显示的声明,可以直接加载到 spring容器中。

    basePackages 表面组件扫描的包是那个,从Packages中可以看出,该属性的值可以是一个数组。

    excludeFilters 指定扫描的时候需要排除的组件,includeFilters 指定扫描的时候只包含的组件,

    useDefaultFilters 是否开启默认扫描规则。

    例如:@ComponentScan(basePackages = "spittr",includeFilters = {},excludeFilters = {},useDefaultFilters = true)

    excludeFilters 和 includeFilters 都是 Filter 数组 ,Filter中我们需要指定类型,分别为:

    1、ANNOTATION 注解类型

    2、ASSIGNABLE_TYPE 指定类型

    3、ASPECTJ ASPECTJ 表达式

    4、REGEX 正则表达式

    5、CUSTOM 自定义

    书上的例子是过滤 spittr 包中的注解类型的组件,后面跟的值我猜可能是过滤那个类里面的注解,使其不被加载的 Spring 容器中。

    三、编写基本控制器

     1、HomeController

    package spittr.web;
    import static org.springframework.web.bind.annotation.RequestMethod.*;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/home")
    public class HomeController {
    
      @RequestMapping(method = GET)
      public String home(Model model) {
        return "home";
      }
    
    }

    该控制器会处理 "/home" 的 GET 请求,返回的视图名为 home。

    我们来看下 Spittr 的首页 index.jsp 以及 home.jsp:

    <%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>Spittr</title>
        <link rel="stylesheet" type="text/css" href="/resources/styles.css">
      </head>
      <body>
        <h1>Welcome to Spittr</h1>
        <a href="<c:url value="/home"/>">Home</a> |
        <a href="<c:url value="/spittles"/>">Spittles</a> |
        <a href="<c:url value="/spitter/register"/>">Register</a>
        
      </body>
    </html>

    <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
        pageEncoding="ISO-8859-1"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Insert title here</title>
    </head>
    <body>
    Welcome to you!
    </body>
    </html>

    当我们运行 index.jsp 后点击 Home 就会显示 Home 页面。

    四、传递模型数据到视图中

    在我们的Home 控制器中并没有把数据返回到视图中,仅仅只返回了一个视图名,可以说是一个超级简单的控制器了。

    现在让我们给视图加点数据进去。

    我们先定义一个 Spittle 类:

    package spittr;
    import java.util.Date;
    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.apache.commons.lang3.builder.HashCodeBuilder;
    
    public class Spittle 
    {
        private final Long id;
        private final String message;
        private final Date time;
        private Double latitude;
        private Double longitude;
    
        public Spittle(String message, Date time)
        {
            this(message, time, null, null);
        }
    
        public Spittle(String message, Date time, Double latitude, Double longitude) 
        {
            this.id = null;
            this.message = message;
            this.time = time;
            this.latitude = latitude;
            this.longitude = longitude;
        }
    
        public long getId() {
            return id;
        }
    
        public String getMessage() 
        {
            return message;
        }
    
        public Date getTime()
        {
            return time;
        }
    
        public Double getLongitude() 
        {
            return longitude;
        }
    
        public Double getLatitude() 
        {
            return latitude;
        }
    
        @Override
        public boolean equals(Object that) 
        {
            return EqualsBuilder.reflectionEquals(this, that, "id", "time");
        }
    
        @Override
        public int hashCode()
        {
            return HashCodeBuilder.reflectionHashCode(this, "id", "time");
        }
    }

    然后假设我们的数据都在一个仓库里:

    定义一个接口 SpittleRepository:

    package spittr.data;
    import java.util.List;
    import org.springframework.stereotype.Component;
    import spittr.Spittle;
    
    @Component
    public interface SpittleRepository 
    {
        List<Spittle> findSpittles(long max, int count);
    
    }

    再实现它:

    package spittr.data;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import org.springframework.stereotype.Repository;
    import spittr.Spittle;
    @Repository
    public class JdbcSpittleRepository implements SpittleRepository 
    {
    
        private List<Spittle> createSpittleList(int count) 
        {
            List<Spittle> spittles = new ArrayList<Spittle>();
            for (int i = 0; i < count; i++) 
            {
                spittles.add(new Spittle("Spittle" + i, new Date()));
            }
            return spittles;
        }
    
        @Override
        public List<Spittle> findSpittles(long max, int count) 
        {
            // TODO Auto-generated method stub
    
            return createSpittleList(count);
        }
    
    }

    现在我们的仓库实现了,可以生成 Spittle 数据。然后我们编写一个 Spittle 控制器:来把数据放到视图中。

    package spittr.web;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import spittr.data.SpittleRepository;
    
    @Controller
    public class SpittleController 
    {
        private SpittleRepository spittleRepository;
    
        @Autowired
        public SpittleController(SpittleRepository spittleRepository)
        { // 注入SpittleRepository
            this.setSpittleRepository(spittleRepository);
        }
    
        @RequestMapping(value="/spittles",method = RequestMethod.GET)
        public String spittles(Model model) 
        {
            // 将spittle添加到模型中
            model.addAttribute(spittleRepository.findSpittles(Long.MAX_VALUE, 20));
            return "spittles"; // 返回视图名
        }
    
        public SpittleRepository getSpittleRepository()
        {
            return spittleRepository;
        }
    
        public void setSpittleRepository(SpittleRepository spittleRepository) 
        {
            this.spittleRepository = spittleRepository;
        }
    
    }

    该控制器中的 @Autowired 注解会在 Spring容器中寻找有 @Repository 注解的bean,然后将该 bean 注入进去。

    也就是在 Spring 容器中 寻找适当的 bean 注入到参数中去。

    在控制器处理方法上,也就是带有 @RequestMapping 注解的方法的参数有个 Model 也就是模型,

    控制器可以通过该参数将数据传递到视图上去。

    现在我们看下 spittles.jsp 视图:

    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
    <%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>Spitter</title>
        <link rel="stylesheet" type="text/css" href="<c:url value="../resources/style.css" />" >
      </head>
        <div class="listTitle">
          <h1>Recent Spittles</h1>
          <ul class="spittleList">
            <c:forEach items="${spittleList}" var="spittle" >
              <li id="spittle_<c:out value="spittle.id"/>">
                <div class="spittleMessage"><c:out value="${spittle.message}" /></div>
                <div>
                  <span class="spittleTime"><c:out value="${spittle.time}" /></span>
                </div>
              </li>
            </c:forEach>
          </ul>
    
        </div>
      </body>
    </html>

    最后让我们来看下效果:

         


       


                                     

    如果是用 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">
      <display-name>Java_Web</display-name>
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      
      <servlet>
          <servlet-name>SpringDispatcherServlet</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/SpringMVC.xml</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>SpringDispatcherServlet</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
      <context-param>   
          <param-name>contextConfigLocation</param-name>   
          <param-value>/WEB-INF/AppContext.xml</param-value>   
      </context-param>
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener> </web-app>

    另外说明一下,如果是 SpringMVC.xml 文件放在src里的话:<param-value>classpath:SpringMVC.xml<param-value>。

    同理:AppContext.xml也是一样。

    接下来我们看下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:aop="http://www.springframework.org/schema/aop"
        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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
    
     <context:component-scan base-package="spittr"></context:component-scan>
     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
     </bean>
    
    </beans>

     该文件配置的是视图解析器,讲解了 javaConfig,这里就不多说了。

    AppContext.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:aop="http://www.springframework.org/schema/aop"
        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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
    
    
    </beans>

    上面的包并没有单元测试和 mock 模拟测试包。

    如果需要项目及相关包可以点此下载:Download

  • 相关阅读:
    pic32 + Vs1003 正弦测试通过
    c强转问题
    Ucos ii 移植到LPC2148
    Pic32时钟
    linux下的文件扫描程序(转载)
    pic32 spi驱动pmodcls lcd液晶屏
    最*不太*
    vhdl元件例化语句
    被忽略了的gcc 浮点选项
    Linux C/S文件传输和云端文件下载服务模拟
  • 原文地址:https://www.cnblogs.com/M-Anonymous/p/11269197.html
Copyright © 2020-2023  润新知