使用Spring MVC进行Web管理

1、概念

MVC

MVC是一种架构模式,将程序的开发分为Mode、View、Controller三个层次,各层之间彼此分离,协同工作。View视图层关注数据的呈现,Model模型层关注支撑业务数据的构成,Controller完成业务逻辑处理和数据传递。

如下所示为一种MVC设计模式,首先用户请求到达前端控制器Front Controller,它将请求转发给能处理该请求的Controller,控制器进行业务处理并产生业务数据并返回给前端控制器。Front Controller将收到的业务数据转发给页面模板View template渲染成最终页面并返回给Front controller。最后Front controller将最终页面返回给用户。在这个模式中,Front controller负责分发调度,Controller负责处理业务数据,View template负责呈现页面。MVC核心思想是业务数据的处理和呈现相分离。

Spring MVC的执行流程

  1.  用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
  2. DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器,得到请求被处理的执行链。所谓执行链是指用一些拦截器Interceptor包裹在真正的处理器Handler外围,在请求到达Handler之前进行一些预处理,在Handler返回执行结果后再进行一些操作才将结果返回。
  3. DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器具体应该要去执行哪个Controller
  4. HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
  5. DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
  6. DispatcherServlet将模型数据填充到视图中
  7. DispatcherServlet将结果响应给用户

2、使用Spring MVC

文件配置

如下左图所示对一个customers表进行管理,完成用户信息查询显示和增加等功能,首先通过maven创建一个web项目并手动添加model、view、controller等类。项目结构如下右图所示

 

首先在pom.xml文件中配置项目用到的依赖如servlet、mysql-connector、spring-webmvc等。

接着在web.xml文件中引入两个配置文件:Spring框架的上下文配置文件application-context.xml和Spring MVC的配置文件mvc-dispatcher-servlet.xml。在配置DispatcherServlet时通过<init-param>contextConfigLocation指定了配置文件的位置,否则SpringMVC默认根据<servlet-name>自动查找/web-INF/$servlet-name$-servlet.xml文件

这里需要注意的是<url-pattern>匹配路径“/”只能匹配到类似于/customer/view的url,“/*”不仅可以匹配到/customer/view,还能匹配到/view.jsp、/view.html等带后缀的url。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  
  <display-name>Archetype Created Web Application</display-name>

<!--配置Spring的上下文context-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/config/application-context.xml</param-value>
  </context-param>

<!--配置DispatcherServlet-->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/config/mvc-dispatcher-servlet.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

如下所示为application-context.xml文件,在其中开启基于注解的Bean管理并自动扫描com.mvc下的类,通过exclude-filter排除@Controller注解的Bean,因为之后将@Controller类交给DispatcherServlet来管理

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 启动基于注解的Bean管理 -->
    <context:annotation-config></context:annotation-config>

    <!-- 扫描com.mvc下的Bean -->
    <context:component-scan base-package="com.mvc">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

如下所示为mvc-dispatcher-servlet.xml文件,在其中激活注解的Bean管理并且搜索@Controller类,并且启动HandlerMapping。DispatcherServlet除了对Controller进行管理外,还对View进行管理。配置ViewResolver为JSTL模板页面,并且为页面添加前后缀,例如Controller返回字符串success,添加前后缀就成为/WEB-INF/jsps/success.jsp,从而可以找到对应的页面。

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 激活注解 -->
    <context:annotation-config></context:annotation-config>

    <!-- 让DispatchServlet只搜索@Controller注解的类 -->
    <context:component-scan base-package="com.mvc">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 让DispatchServlet启用基于注解的HandlerMapping -->
    <mvc:annotation-driven/>

    <!-- 映射静态资源目录 -->
    <mvc:resources mapping="/resources/**" location="/resources"/>

    <!-- 配置ViewResolver为JstlView,并为其添加前后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

编写Controller

如下实现通过访问url为/customer/view?customerId=3的页面显示用户的姓名。通过@RequestMapping的默认value属性为CustomerController类和viewCustomer()方法指定映射的url路径,还可以通过method = RequestMethod.POST属性来指定响应url的类型。

在viewCustomer()方法首先通过@RequestParam注解获取url中的参数customerId,然后通过调用CustomerService完成根据id查询数据库的操作并返回一个customer对象,controller再将该对象绑定到Model对象中。Spring MVC通过Model对象携带数据信息,前端页面可以直接从Model对象中获取到customer。

最后返回页面字符串"customer_view",Dispatcher Servlet会根据字符串拼接成正确的页面位置。不仅可以直接书写页面字符串,还可以为“redirect/forward:”+页面来实现重定向的功能。

@Controller
@RequestMapping("/customer")
public class CustomerController {
    private CustomerService customerService;

    @Autowired
    public void setCustomerService(CustomerService customerService) {
        this.customerService = customerService;
    }

    @RequestMapping("/view")
    public String viewCustomer(@RequestParam("customerId") int id, Model model) {
        Customer customer = customerService.queryById(id);
        model.addAttribute(customer);           //将customer对象绑定到model中
        return "customer_view";
    }
}

上面是通过@RequestParam获取参数并通过Model对象返回对象,还可以使用传统的HttpRequest对象来进行参数的获取和数据的绑定

    @RequestMapping("/view")
    public String viewCustomer(HttpServletRequest request){
        int id=Integer.parseInt(request.getParameter("customerId"));    //获取参数
        Customer customer=customerService.queryName(id);
        request.setAttribute("customer",customer);                      //绑定返回对象
        return "customer_view";
    }

如果访问的url为RESTful的形式/customer/view/3,可以通过@PathVariable来获取参数。这里使用Map来携带customer对象信息给前端。

    @RequestMapping("/view/{customerId}")
    public String viewCustomer(@PathVariable("customerId")int id, Map<String,Object> model){
        Customer customer=customerService.queryName(id);
        model.put("customer",customer);
        return "customer_view";
    }

最后在customer_view.jsp页面通过EL表达式显示customer对象的信息如下

<h2>编号:${customer.id},名字:${customer.name}</h2>

表单数据绑定

SpringMVC会自动完成表单数据绑定到对象,需要注意的是表单属性的name要和对象属性名相同。如下所示为提交一个customer对象的表单,其name为id、name分别对应Customer对象的id、name属性。

    <form action="<%=request.getContextPath()%>/customer/save" method="post">
        id:<input type="text" name="id">
        用户名:<input type="text" name="name">
        <input type="submit" value="提交">
    </form>

在Controller中以参数的形式接收customer对象并通过customerService将customer保存到数据库。之后将页面重定向到/customer/view页面并传入customerId为刚保存的customer对象的id。

    @RequestMapping(value = "/save",method = RequestMethod.POST)
    public String saveCustomer(Customer customer){
        System.out.println(customer.getId()+customer.getName());
        customerService.addCustomer(customer);
        return "redirect:view?customerId="+customer.getId();
    }

值得注意的是表单提交时中文会出现乱码,这是需要在web.xml文件中设置编码方式为UTF-8

  <filter>
    <filter-name>characterEncoding</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>
  </filter>
  <filter-mapping>
    <filter-name>characterEncoding</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

文件上传

在Spring MVC中使用文件上传,首先在pom.xml文件中引入maven依赖commons-fileupload,接着在mvc-dispatcher-servlet.xml中配置bean CommonMultipartResolver

    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10485760"/>   <!--文件最大10M=10485760字节-->
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="resolveLazily" value="true"/>       <!--开启文件延迟解析-->
    </bean>

提交文件的表单如下:在其中指定enctype为multipart/form,文件的name为imgFile

    <form method="post" action="<%=request.getContextPath()%>/customer/uploadImg" enctype="multipart/form-data">
        <input type="file" name="imgFile">
        <input type="submit" value="提交">
    </form>

在Controller中接收图片并保存,通过MultipartFile对象实现文件操作,其isEmpty()用于判定文件是否为空,getInputStream()获取文件流,getOriginalFilename()获取文件名。通过FileUtils的copyInputStreamToFile()方法将文件拷贝到服务器新建的文件对象中

    @RequestMapping(value = "/uploadImg",method = RequestMethod.POST)
    public String uploadImg(@RequestParam("imgFile") MultipartFile multipartFile) throws IOException {
        if (!multipartFile.isEmpty()){
            FileUtils.copyInputStreamToFile(multipartFile.getInputStream(),
                    new File("D:\\Temp\\Pictures",multipartFile.getOriginalFilename()));
        }
        return "redirect:upload";
    }

返回JSON数据

有时我们需要服务器不是返回一个页面,而是JSON格式的数据以便前端完成异步加载等功能。在SpringMVC中使用Json首先要通过maven引入依赖jackson-databind。之后在mvc-dispatcher-servlet.xml中配置ContentNegotiatingViewResolver默认返回JSON格式的数据。

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
            </list>
        </property>
    </bean>

之后在Controller中设置方法直接返回Customer类型的对象,并且添加@ResponseBody注解

    @RequestMapping("/json_data")
    public @ResponseBody Customer getJsonData(@RequestParam("customerId") int id){
        return customerService.queryById(id);
    }

通过浏览器访问该url返回JSON格式的数据如下所示

3、拦截器

Spring MVC中的拦截器可以通过继承HandlerInterceptor接口来实现,该接口有三个方法,其中preHandler()为请求到达真正Controller之前进行拦截的方法,它具有返回值,如果为true则放行请求,false代表拦截请求。例如在该方法中对用户是否登录进行验证,验证通过才放行,否则通过request将其转发到登陆页面。postHandler()是从Controller返回结果后再次经过拦截器所执行的方法,该方法的参数除了request、response、handler之外还有ModelAndView对象,该对象代表Controller返回的数据和视图信息,我们可以通过该对象修改返回的数据与视图页面。afterCompletion()方法是拦截器执行结束后的方法,类似于析构函数,一般在其中进行对象销毁、连接关闭等操作。

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("请求到达拦截器...");
        if (request.getSession().getAttribute("username") == null){         //验证是否已登录
            request.getRequestDispatcher("/WEB-INF/jsps/login.jsp").forward(request,response);
            return false;
        }else
            return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("返回结果到达拦截器...");
        modelAndView.addObject("msg","这是拦截器返回时添加的信息");        //修改返回的数据信息
        modelAndView.setViewName("/customer/add_customer");              //修改返回的视图页面
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器执行结束");
    }
}

之后要在mvc-dispatcher-servlet.xml文件中注册拦截器,其中<mvc:mapping>规定拦截器要进行拦截的请求路径。

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/customer_view"/>
            <bean class="com.mvc.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>


发布了124 篇原创文章 · 获赞 65 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/theVicTory/article/details/105567213
今日推荐