【SpringMVC】【狂神说】学习笔记

学习方法说明

SSM:SpringMVC + Spring + MyBatis
MVC三层架构

框架的学习要研究官方文档,锻炼自学能力

SpringMVC + Vue + SpringBoot + SpringCloud + Linux

学习SpringMVC的前提要学习Spring

回顾MVC架构

MVC是数据模型(model),视图(view),控制器(controller)的简写,是一种软件设计规范

模型:一个或多个JavaBean对象,用于存储数据(实体模型,由JavaBean类创建)和处理业务逻辑(业务模型,由一般的java类创建)
视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。
控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

MVC主要作用是降低了视图与业务逻辑间的双向解耦

转发:一次请求,地址不变
重定向:二次请求,地址变化

重定向:是将用户从当前处理请求定向到另一个视图(例如JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的request作用域
转发:转发是将用户对当前的请求转发给另一个视图或处理请求,以前的request中存放的信息不会失效
转发是服务器行为,重定向是客户端行为

转发过程:
客户浏览器发送http请求,web服务器接受请求调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里转发的路径必须是同一个web容器下的url,其不能转发到其他web路径上,中间传递的是自己容器内的request
在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器制作一次访问请求。

重定向过程:
客户浏览器发送http请求,web服务器接受后发送302状态码响应及对应新的location给客户浏览器,客户浏览器发现是302响应,则自动再发送一个新的http请求,请求URL是新的location地址,服务器根据此请求寻找资源并发送给客户。
在这里location可以重定向到任意URL,既然是浏览器重新发出的请求,那么就没什么request传递的概念了,在客户浏览器的地址栏中显示的是其重定向后的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求。
SpringMVC中的转发和重定向:

@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login() {
        //转发到一个请求方法(同一个控制器类可以省略/index/)
        return "forward:/index/isLogin";
    }
    @RequestMapping("/isLogin")
    public String isLogin() {
        //重定向到一个请求方法
        return "redirect:/index/isRegister";
    }
    @RequestMapping("/isRegister")
    public String isRegister() {
        //转发到一个视图,SpringMVC框架默认使用的是转发
        return "register";
    }
}

在 Spring MVC 框架中,不管是重定向或转发,都需要符合视图解析器的配置,如果直接转发到一个不需要 DispatcherServlet 的资源,例如:

return "forward:/html/my.html";

则需要使用 mvc:resources 配置:

<mvc:resources location="/html/" mapping="/html/**" />

pojo:后台的数据对象,按照数据库字段涉及
pojo持久化后==》PO
pojo传输过程中==》DTO
pojo用作表示层==》VO
vo:视图层对象,不一定要与后台数据库一样,可以使用什么,就定什么属性,够用即可
dto:数据传输对象,视图层与服务层之间的传输对象,大多数情况下,DTO的属性来自多个表
bo:业务对象
dao:数据访问对象
例子:
比如一张表有100个字段,那么对应的PO就有100个属性(大多数情况下,DTO内的数据来自多个表)。但view层只需显示10个字段,没有必要把整个PO对象传递到client,这时我们就可以用只有这10个属性的DTO来传输数据到client,这样也不会暴露server端表结构。到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO。

Servlet回顾

public class HelloServlet extends HttpServlrt{

	@override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOexcertion{
		//1.获取前端参数
		String method = req.getParameter("method");
		if(method.equals("add")){
			req.getSession().setAttribute("msg", "执行add方法");
		}
		if(method.equals("delete")){
			req.getSession().setAttribute("msg", "执行delete方法");
		}
		//2.调用业务层
		
		//3.视图转发或者重定向
		//转发 req.getRequestDispatcher()
		//重定向 resp.sendRedirect()
		req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
	}
	
	@override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOexcertion{
		doGet(req, resp);
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/javaee"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
		version="4.0">

	<servlet>
		<servlet-name>hello</servlet-name>
		<servlet-class>helloServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello</servlet-name>
		<url-pattern>/hello</url-pattern>
	</servlet-mapping>

	<!--配置超时时间-->
	<session-config>
		<session-timeout>15</session-timeout>
	</session-config>

	<!-配置欢迎页面--->
	<welcome-file-List>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-List>
</web-app>

MVC框架要做那些事情
1.将url映射到java类或者java类的方法
2.封装用户提交的数据
3.处理请求–调用相关的业务处理–封装响应数据
4.将响应的数据进行渲染.jsp/.html等表示层数据

MVVM :M V VM–viewmode

初识SpringMVC

基于Java实现的MVC框架,底层还是servlet
Spring MVC的特点:
1.轻量级,简单易学
2.高效,基于请求响应的MVC框架
3.约定优于配置
4.功能强大:RESTful,数据验证,格式化,主题等
5.与Spring兼容性好,无缝结合
6.简单灵活

Spring:大杂烩,我们可以将SpringMVC中所有要用到的bean,注册到Spring中

Spring的web框架围绕DispatcherServlet设计

DispatcherServlet的作用是将请求分发到不同的处理器。

SpringMVC简单,便捷,易学,天生和Spring无缝集成(使用Spring IOC和AOP),使用约定优于配置,能够进行简单junit测试,支持Restful风格,异常处理,本地化,国际化,数据验证,类型转换,拦截器 等等。。。。。都是要学会并且熟练使用的

SpringMVC以请求为驱动,围绕一个中心Servlet分派请求及提供其他功能,DispacterServlet是一个实际的Servlet(它继承自HttpServlet基类)

SpringMVC执行原理

在这里插入图片描述

1.DispatcherServlet前端控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求

我们假设请求的url为:http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器

2.HandlerMapping处理器映射。DispatcherServlet自动调用HandlerMapping,HandlerMapping根据请求url查找Handler(举例:根据/hello找到HelloController)

3.HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器是hello

4.HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射器等

5.HandlerAdapter处理器适配器,其按照特定的规则去执行Handler

6.Handler让具体的Controller执行

7.Controller让具体的执行信息返回给HandlerAdapter,如ModelAndView

8.HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet

9.DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名

10.视图解析器解析的逻辑视图名传递给DispatcherServlet

11.DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图

12.最终视图呈现给用户

springMVC执行流程说明(重要):

(1)客户端(浏览器)发送请求,直接请求到 DispatcherServlet。

(2)DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。

(3)解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。

(4)HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑。

(5)处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。

(6)ViewResolver 会根据逻辑 View 查找实际的 View。

(7)DispaterServlet 把返回的 Model 传给 View(视图渲染)。

(8)把 View 返回给请求者(浏览器)

SpringMVC 重要组件说明

1、前端控制器DispatcherServlet(不需要工程师开发),由框架提供(重要)

作用:Spring MVC 的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了 DispatcherServlet 减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。

2、处理器映射器HandlerMapping(不需要工程师开发),由框架提供

作用:根据请求的url查找Handler。HandlerMapping负责根据用户请求找到Handler即处理器(Controller),SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3、处理器适配器HandlerAdapter

作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4、处理器Handler(需要工程师开发)

注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5、视图解析器View resolver(不需要工程师开发),由框架提供

作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6、视图View(需要工程师开发)

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

注意:处理器Handler(也就是我们平常说的Controller控制器)以及视图层view都是需要我们自己手动开发的。其他的一些组件比如:前端控制器DispatcherServlet、处理器映射器HandlerMapping、处理器适配器HandlerAdapter等等都是框架提供给我们的,不需要自己手动开发。

DispatcherServlet类中的属性beans:

  • HandlerMapping:用于handlers映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解
  • HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序
  • ViewResolver:根据实际配置解析实际的View类型
  • ThemeResolver:解决Web应用程序可以使用的主题,例如提供个性化布局
  • MultipartResolver:解析多部分请求,以支持从HTML表单上传文件
  • FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,通常用于重定向

HandlerMapping接口处理请求的映射HandlerMapping接口的实现类

SimpleUrlHandlerMapping类通过配置文件把URL映射到Controller类。
DefaultAnnotationHandlerMapping类通过注解把URL映射到Controller类

HandlerAdapter接口-处理请求映射

AnnotationMethodHandlerAdapter:通过注解,把请求URL映射到Controller类的方法上

HandlerExceptionResolver接口-异常处理接口

SimpleMappingExceptionResolver通过配置文件进行异常处理。
AnnotationMethodHandlerExceptionResolver:通过注解进行异常处理。

ViewResolver接口解析View视图

UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理。

总结一下SpringMVC工作流程:

1.客户端请求提交到DispatcherServlet
2.由DispatcherServlet控制器寻找一个或多个HandlerMapping,找到处理请求的Controller
3.DispatcherServlet将请求提交到Controller
4.Controller调用业务逻辑处理后返回ModelAndView对象
5.DispatcherServlet寻找一个或多个ViewResolver视图解析器,找到ModelAndViewzhi指定的视图
6.视图负责将结果显示到客户端

动手编写一个helloworld的小demo
首先第一步配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置DispatcherServlet:这是SpringMVC的核心,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

第二步,编写Spring的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
    <!--处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
    <!--视图解析器-->
    <bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB_INF/JSP"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>


    <!--BeanNameUrlHandlerMapping:bean-->
    <bean id="/hello" class="com.springmvc.demo.HelloController"></bean>

</beans>

第三步,创建对应的Controller

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloController implements Controller{

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        // 业务代码
        modelAndView.addObject("msg","hello world");
        // 视图跳转
        modelAndView.setViewName("test");

        return modelAndView;
    }
}

使用注解开发SpringMVC

web.xml是只配置一次即可

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置DispatcherServlet:这是SpringMVC的核心,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

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

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.springmvc.demo"/>

    <!--让SpringMVC不处理静态资源 例如:.css  .js  .html  .mp3-->
    <mvc:default-servlet-handler/>

    <!--
    支持mvc注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理
        而annotaion-driven配置帮助我们自动完成上述两个实例的注入
    -->
    <mvc:annotation-driven/>


    <!--视图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>



</beans>

创建controller

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController{


    @RequestMapping("/hello")
    public String hello(Model model){
        // 封装数据
        model.addAttribute("msg", "hello world");

        return "hello";// 返回页面的名称;会被视图解析器处理
    }
}

总结:
创建一个SpringMVC的步骤:
1.新建一个web项目
2.导入相关的jar包
3.编写web.xml,注册DispacherServlet
4.编写SpringMVC配置文件(dispatcher-servlet.xml)
5.接下来就是去创建对应的控制类,controller
6.最后完善前端视图和controller之间的对应
7.测试运行调试

SpringMVC的三大组件:
处理器映射器
处理器适配器
视图解析器
前端控制器

@Controller 返回的值被视图解析器解析,跳转到指定页面
@RestController 返回值不会被视图解析器解析,直接显示在页面,返回的是json

Controller配置总结

控制器Controller
控制器复杂负责提供访问应用程序的行为,通常通过接口定义或者注解定义两种方式实现
控制器负责解析用户的请求并将其转换为一个模型
在Spring MVC中一个控制器可以包含多个方法
在Spring MVC中,对于Controller的配置方式有很多种
可以通过实现controller接口实现,但是不推荐使用,推荐使用注解的形式

@Component
@Service
@Repository
@Controller
上述四个注解功能相同

RestFul风格

概念:
RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更容易实现缓存机制。

功能:
资源:互联网所有的事物都可以抽象成资源
资源操作:使用post delete put get 使用不同方法对资源进行操作
分别对应添加,删除,修改,查询

传统方式操作资源:通过不同的参数来实现不同的效果,方法单一,post和get

@Controller
public class HelloController{

    // 请求url:http://localhost:8080/add?a=5&b=2
    @RequestMapping("/add")
    public String test1(int a, int b, Model model){
        int res = a+b;
        model.addAttribute("msg",res);
        return "test";// 跳转到test.jsp
    }


    // RestFul:http://localhost:8080/1/2
    @RequestMapping("/add/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg",res);
        return "test";// 跳转到test.jsp
    }

	/**
     * 接收普通请求参数
     * http://localhost:8080/hello/show16?name=linuxsir
     * url参数中的name必须要和@RequestParam("name")一致
     * @return
     */
    @RequestMapping("show16")
    public ModelAndView test16(@RequestParam("name")String name){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("hello2");
        mv.addObject("msg", "接收普通的请求参数:" + name);
        return mv;
    }
}

@PathVariable:接受请求路径中占位符的值
要求url中的变量名和方法的参数名必须相同

@PathVariable 是通过URL路径部分获取用户的输入,例如/blogs/1
@RequestParam 是通过URL路径拼接参数获取用户的输入,例如/blogs?id=1

根据具体情况判断使用哪一种,建议:
1.当URL指向的是某一具体业务资源(或者资源列表),例如博客,用户时,使用@PathVariable
2.当URL需要对资源或者资源列表进行过滤,筛选时,用@RequestParam

为了防止客户端不赋值,设置一个默认值

@RequestParam(value = "id", required = false, defaultValue = "0")
@Controller
public class HelloController{

    // 两个Controller虽然url相同,但是可以通过不同方式调用不同的Controller
    // RestFul:http://localhost:8080/1/2
    @GetMapping("/add/{a}/{b}")// get方式:就是通过url直接调用Controller
    public String test1(@PathVariable int a, @PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg",res);
        return "test";// 跳转到test.jsp
    }

    @PostMapping("/add/{a}/{b}")// post方式:可以通过页面的表单调用Controller
    public String test2(@PathVariable int a, @PathVariable int b, Model model){
        int res = a+b;
        model.addAttribute("msg",res);
        return "test";// 跳转到test.jsp
    }
}

RestFul三大特点:
简洁,高效,安全

转发和重定向

@GetMapping("/hello")
    public String test3(){
        return "test";// 转发,springmvc默认使用的转发,或者return "forwrad:/test.jsp";
        return "redirect:/test.jsp";//重定向
    }

接收请求参数及数据回显

参数名相同

	// http://localhost:8080/hello3?name=xxx
    @GetMapping("/hello3")
    public String test3(String name){
        System.out.println(name);// xxx
        return "test";
    }

参数名不同
不管参数名是否相同,建议写@RequestParam(“url中的名字”)

	// http://localhost:8080/hello3?n=xxx
    @GetMapping("/hello3")
    public String test3(@RequestParam("n") String name){
        // 将n的值给name
        System.out.println(name);// xxx
        return "test";
    }
    /**
     * 1.接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
     * 2.假设参数是一个对象,则前端传入的参数名与对象属性名一一对应,则可以匹配上
     * 例子:http://localhost:8080/hello3?id=1&name="xxx"&age=1
     * 
     * 与User对象中的属性名对比,相同名字的可以传递值
     * 
     */
    @GetMapping("/hello3")
    public String test3(User user){
        System.out.println(user.toString());
        return "test";
    }

model:只有寥寥几个方法只适合用于储存数据,简化了新手对Model对象的操作和理解
ModelMap:继承了LinkedMap,除了实现了自身的一些方法,同样的继承LinkedMap的方法和特性
ModelAndView:可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转
HttpSession:
HttpServletRequest:
HttpServletResponse:
还有输入输出流、表单实体类、注解类型、与Spring框架相关的类型都可以作为参数

请求处理方法(控制器Controller)常见的返回类型:
String类型的视图名称
ModelAndView
Model
View

例子:前台传给后台用户名和密码,后台接受参数的几种方法:

    /**
     * 处理登录 使用UserForm对象(实体Bean) user接收注册页面提交的请求参数
     */
    @RequestMapping("/login")
    public String login(UserForm user, HttpSession session, Model model) {
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpass())) {
            session.setAttribute("u", user);
            logger.info("成功");
            return "main"; // 登录成功,跳转到 main.jsp
        } else {
            logger.info("失败");
            model.addAttribute("messageError", "用户名或密码错误");
            return "login";
        }
    }

	    @RequestMapping("/register")
    /**
    * 通过形参接收请求参数,形参名称与请求参数名称完全相同
    */
    public String register(String uname,String upass,Model model) {
        if ("zhangsan".equals(uname)
                && "123456".equals(upass)) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            logger.info("失败");
            // 在register.jsp页面上可以使用EL表达式取出model的uname值
            model.addAttribute("uname", uname);
            return "register"; // 返回 register.jsp
        }
    }
	
	    @RequestMapping("/register")
    /**
    * 通过HttpServletRequest接收请求参数
    */
    public String register(HttpServletRequest request,Model model) {
        String uname = request.getParameter("uname");
        String upass = request.getParameter("upass");
        if ("zhangsan".equals(uname)
                && "123456".equals(upass)) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            logger.info("失败");
            // 在register.jsp页面上可以使用EL表达式取出model的uname值
            model.addAttribute("uname", uname);
            return "register"; // 返回 register.jsp
        }
    }

	    @RequestMapping("/register")
    public String register(@ModelAttribute("user") UserForm user) {
        if ("zhangsan".equals(uname) && "123456".equals(upass)) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            logger.info("失败");
            // 使用@ModelAttribute("user")与model.addAttribute("user",user)的功能相同
            //register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值
            return "register"; // 返回 register.jsp
        }
    }

@ModelAttribute 注解在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中
@ModelAttribute 注解的方法将在每次调用该方法所在的控制器的其他方法前被调用。这种特性可以用来控制登陆权限,当然控制登陆权限的方法有很多,例如拦截器,过滤器等。

@Controller
@RequestMapping("/index")
public class LoginController {

    @RequestMapping("/LoginController")
    public ModelAndView LoginController(){
        System.out.println("LoginController");
        return new ModelAndView("login");
    }

    @RequestMapping("/RegisterController")
    public ModelAndView RegisterController(){
        System.out.println("RegisterController");
        return new ModelAndView("register");
    }
}

public class BaseCheck{
	@ModelAttribute
    public String test(){
        System.out.println("XX-XX");
        return "XX-XX";
    }
}
/**
控制台输出:
XX-XX
LoginController

前端调用/LoginController,控制台输出XX-XX
也就是LoginController()方法调用前,调用了test()
*/

@RestController
public class Test1 {

    // 作用相当于model.addAttribute("attributeName",abc)
    @ModelAttribute("attributeName")
    public String addAccount(@RequestParam String abc) {
        return abc;
    }

    @RequestMapping(value = "/helloWorld")
    public String helloWorld(Model model) {
        return model.getAttribute("attributeName").toString();
    }
}

Spring MVC Converter(类型转换器)详解

SpringMVC框架的Convertor<S,T>是一个可以将一种数据类型转换成另一种数据类型的接口,这里的S表示源类型,T表示目标类型。开发者在实际应用中使用框架内置的类型转换器基本上就够了,当然也可以自定义类型转换器。

内置的转换器:

StringToBooleanConverter 	String 到 boolean 类型转换
ObjectToStringConverter 	Object 到 String 转换,调用 toString 方法转换
StringToNumberConverterFactory 	String 到数字转换(例如 Integer、Long 等)
NumberToNumberConverterFactory 	数字子类型(基本类型)到数字类型(包装类型)转换
StringToCharacterConverter 	String 到 Character 转换,取字符串中的第一个字符
NumberToCharacterConverter 	数字子类型到 Character 转换
CharacterToNumberFactory 	Character 到数字子类型转换
StringToEnumConverterFactory 	String 到枚举类型转换,通过 Enum.valueOf 将字符串转换为需要的枚举类型
EnumToStringConverter 	枚举类型到 String 转换,返回枚举对象的 name 值
StringToLocaleConverter 	String 到 java.util.Locale 转换
PropertiesToStringConverter 	java.util.Properties 到 String 转换,默认通过 ISO-8859-1 解码
StringToPropertiesConverter 	String 到 java.util.Properties 转换,默认使用 ISO-8859-1 编码
ArrayToCollectionConverter 	任意数组到任意集合(List、Set)转换
CollectionToArrayConverter 	任意集合到任意数组转换
ArrayToArrayConverter 	任意数组到任意数组转换
CollectionToCollectionConverter 	集合之间的类型转换
MapToMapConverter 	Map之间的类型转换
ArrayToStringConverter 	任意数组到 String 转换
StringToArrayConverter 	字符串到数组的转换,默认通过“,”分割,且去除字符串两边的空格(trim)
ArrayToObjectConverter 	任意数组到 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回数组的第一个元素并进行类型转换
ObjectToArrayConverter 	Object 到单元素数组转换
CollectionToStringConverter 	任意集合(List、Set)到 String 转换
StringToCollectionConverter 	String 到集合(List、Set)转换,默认通过“,”分割,且去除字符串两边的空格(trim)
CollectionToObjectConverter 	任意集合到任意 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回集合的第一个元素并进行类型转换
ObjectToCollectionConverter 	Object 到单元素集合的类型转换

自定义类型转换器并注册

创建自定义类型转换器类:

package converter;

import org.springframework.core.convert.converter.Converter;
import pojo.GoodsModel;

public class GoodsConverter implements Converter<String, GoodsModel> {
    public GoodsModel convert(String source) {
        // 创建一个Goods实例
        GoodsModel goods = new GoodsModel();
        // 以“,”分隔
        String stringvalues[] = source.split(",");
        if (stringvalues != null && stringvalues.length == 3) {
            // 为Goods实例赋值
            goods.setGoodsname(stringvalues[0]);
            goods.setGoodsprice(Double.parseDouble(stringvalues[1]));
            goods.setGoodsnumber(Integer.parseInt(stringvalues[2]));
            return goods;
        } else {
            throw new IllegalArgumentException(String.format(
                    "类型转换失败, 需要格式'apple, 10.58,200 ',但格式是[% s ] ", source));
        }
    }
}

注册类型转换器到springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p" 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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 -->
    <context:component-scan base-package="controller" />
    
    <!--注册类型转换器GoodsConverter-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="converter.GoodsConverter"/>
            </list>
        </property>
    </bean>
    
    <!--视图解析器-->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

Spring MVC Formatter(数据格式化)详解

Spring MVC 框架的 Formatter 与 Converter<S,T> 一样,也是一个可以将一种数据类型转换成另一种数据类型的接口。不同的是,Formatter 的源数据类型必须是 String 类型,而 Converter<S,T> 的源数据类型是任意数据类型。

在 Web 应用中由 HTTP 发送的请求数据到控制器中都是以 String 类型获取,因此在 Web 应用中选择 Formatter 比选择 Converter<S,T> 更加合理。

内置的格式化转换器:

NumberFormatter:实现 Number 与 String 之间的解析与格式化。
CurrencyFormatter:实现 Number 与 String 之间的解析与格式化(带货币符号)。
PercentFormatter:实现 Number 与 String 之间的解析与格式化(带百分数符号)。
DateFormatter:实现 Date 与 String 之间的解析与格式化

自定义格式转换器并注册

创建自定义格式化转换器类:

package controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.springframework.format.Formatter;

public class MyFormatter implements Formatter<Date> {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
	//parse 方法的功能是利用指定的 Locale 将一个 String 类型转换成目标类型
    public String print(Date object, Locale arg1) {
        return dateFormat.format(object);
    }

	//print 方法与之相反,用于返回目标对象的字符串表示
    public Date parse(String source, Locale arg1) throws ParseException {
        return dateFormat.parse(source); // Formatter只能对字符串转换
    }
}

注册格式解析器到springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p" 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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描controller包 -->
    <context:component-scan base-package="controller" />
    <!--注册MyFormatter-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="formatters">
            <list>
                <bean class="formatter.MyFormatter"/>
            </list>
        </property>
    </bean>

	<!--
	mvc:annotation-driven的作用:
	其中最主要的两个类:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter ,分别为HandlerMapping的实现类和HandlerAdapter的实现类

	HandlerMapping的实现类的作用
	实现类RequestMappingHandlerMapping,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
 
	HandlerAdapter的实现类的作用
	实现类RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值。
	-->
    <mvc:annotation-driven conversion-service="conversionService"/>
    
	<!--视图解析器-->
	<bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

解决乱码问题

自定义乱码过滤器

创建乱码过滤器类:

public class EncodingFilter implements Filter{

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        chain.doFilter(request, response);
    }

    public void destory(){

    }
}

将乱码过滤器组测到web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置DispatcherServlet:这是SpringMVC的核心,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

	<!--注册乱码过滤器-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>com.springmvc.demo.EncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>

    
</web-app>

SpringMVC提供了过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置DispatcherServlet:这是SpringMVC的核心,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dispatcher-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置SpringMVC的乱码过滤-->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
</web-app>

Json

前后段分离阶段:
后端部署后端,提供接口:
前端独立部署,负责渲染后端的数据:
此时前后端交互用json

@Controller
@RequestMapping("/AboutJson")// 建议开发者采用类级别注解,将相关处理归类放在一起,便于维护
public class UserController{

	@RequestMapping("/json1")
	@ResponseBody//作用是让返回值不走视图解析器,直接返回到页面一个json字符串
	public String json1(){
		User user = new User();
		return user.toString();
	}
}

json格式

[{key1:value1, key2:value2},{key1:value1, key2:value2}]

在dispatcher-servlet.xml中配置json对乱码问题处理的配置

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

    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.springmvc.demo"/>

    <!--JSON乱码问题配置-->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"></constructor-arg>
            </bean>
            <bean class="org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"></property>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>



    <!--让SpringMVC不处理静态资源 例如:.css  .js  .html  .mp3-->
    <mvc:default-servlet-handler/>

    <!--
    支持mvc注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理
        而annotaion-driven配置帮助我们自动完成上述两个实例的注入
    -->
    <mvc:annotation-driven/>


    <!--视图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

@RestController 作用在类上,那么这个类的所有的方法就都返回json字符串

@Controller 作用在类上,返回的值会通过视图解析器构成一个页面文件的路径

@ResponseBody 作用在方法上,使方法返回json串

ObjectMapping 对字符串和json间的转换及操作

    // ObjectMapper 工具类,用于处理json,可以设置字符串格式等
    public String getJson(Object object, String dateFormat){
        ObjectMapper mapper = new ObjectMapper();
        // 不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 自定义日期的格式
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        mapper.setDataFormat(sdf);

        try {
            return mapper.writeValueAsString(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Jackson应该是目前比较好的json解析工具
Jackson的坐标

<dependency>
	<groupId>com.fasterxml.jsckson.core</groupId>
	<artifactId>jackson.databind</artifactId>
	<versionId>2.9.8</versionId>
</dependency>

两个重要的JSON格式转换注解,分别是@RequestBody 和 @ResponseBody
@RequestBody 用于将请求体中的数据绑定到方法的形参中,该注解应用在方法的形参上
@ResponseBody 用于直接返回return对象,该注解应用在方法上

@Controller
public class TestController {
    /**
     * 接收页面请求的JSON参数,并返回JSON格式的结果
     */
    @RequestMapping("testJson")
    @ResponseBody
    public Person testJson(@RequestBody Person user) {
        // 打印接收的JSON格式数据
        System.out.println("pname=" + user.getPname() + ",password="
                + user.getPassword() + ",page" + user.getPage());
        ;
        // 返回JSON格式的响应
        return user;
    }
}

在上述控制器类中编写了接收和响应 JSON 格式数据的 testJson 方法,方法中的 @RequestBody 注解用于将前端请求体中的 JSON 格式数据绑定到形参 user 上,@ResponseBody 注解用于直接返回 Person 对象(当返回 POJO 对象时默认转换为 JSON 格式数据进行响应)

SpringMVC拦截器(Interceptor)的配置及作用

Spring MVC的拦截器(Interceptor)与Java Servlet的过滤器(Filter)类似,它主要用于拦截客户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志,判断用户是否登陆等功能上

定义一个拦截器可以通过两种方式:

  • 一种是通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类来定义
  • 一种是通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义

代码示例:

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");

    }

    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
    }

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
        return false;
    }
}

方法含义:

  • preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作
  • postHandle 方法:该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改
  • afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作

让自定义的拦截器生效需要在 Spring MVC 的配置文件中进行配置,配置示例代码如下:

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 配置一个全局拦截器,拦截所有请求 -->
    <bean class="interceptor.TestInterceptor" /> 
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/**" />
        <!-- 配置不需要拦截作用的路径 -->
        <mvc:exclude-mapping path="" />
        <!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="interceptor.Interceptor1" />
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/gotoTest" />
        <!-- 定义在<mvc: interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="interceptor.Interceptor2" />
    </mvc:interceptor>
</mvc:interceptors>

在上述示例代码中,<mvc:interceptors> 元素用于配置一组拦截器,其子元素 定义的是全局拦截器,即拦截所有的请求。

<mvc:interceptor> 元素中定义的是指定路径的拦截器,其子元素 <mvc:mapping> 用于配置拦截器作用的路径,该路径在其属性 path 中定义。

如上述示例代码中,path 的属性值“/**”表示拦截所有路径,“/gotoTest”表示拦截所有以“/gotoTest”结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置。

需要注意的是,<mvc:interceptor> 元素的子元素必须按照 <mvc:mapping…/>、<mvc:exclude-mapping…/>、<bean…/> 的顺序配置。

拦截器的执行流程

单个拦截器的执行流程如下

创建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" 
        xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
        <!--配置DispatcherServlet-->
        <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>

创建控制器类

    package Controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    @Controller
    public class InterceptorController {
        @RequestMapping("/gotoTest")
        public String gotoTest() {
            System.out.println("正在测试拦截器,执行控制器的处理请求方法中");
            return "test";
        }
    }

创建拦截器类

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");

    }

    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
    }

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
        return false;
    }
}

创建配置文件springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
        xmlns:p="http://www.springframework.org/schema/p" 
        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
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!-- 使用扫描机制扫描控制器类 -->
        <context:component-scan base-package="controller" />
        <!-- 配置视图解析器 -->
        <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
        </bean>
        <!-- 配置拦截器 -->
        <mvc:interceptors>
            <!-- 配置一个全局拦截器,拦截所有请求 -->
            <bean class="interceptor.TestInterceptor" />
        </mvc:interceptors>
    </beans>

创建视图(当前示例使用jsp)

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!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=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        视图
        <%System.out.println("视图渲染结束。"); %>
    </body>
    </html>

测试:执行http://localhost:8080/springMVCDemo06/gotoTest
输出如下:

preHandle方法在控制器的处理请求方法调用之后,解析视图之前执行
正在测试拦截器,执行控制器的处理请求方法中
postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行
视图渲染结束
afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行

总结一下上述代码的执行顺序:preHandle–>controller的方法–>postHandle–>视图渲染–>afterCompletion

多个拦截器的执行流程

在上述代码的基础上修改
创建多个拦截器

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
// 1号拦截器
public class Interceptor1 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor preHandle 方法执行");
        /** 返回true表示继续向下执行,返回false表示中断后续的操作 */
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor1 postHandle 方法执行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor1 afterCompletion 方法执行");
    }
}
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
// 2号拦截器
public class Interceptor2 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor2 preHandle 方法执行");
        /** 返回true表示继续向下执行,返回false表示中断后续的操作 */
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor2 postHandle 方法执行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor2 afterCompletion 方法执行");
    }
}

配置拦截器

    <mvc:interceptors>
        <!-- 配置一个全局拦截器,拦截所有请求 -->
        <!--<bean class="interceptor.TestInterceptor" />-->
        <mvc:interceptor>
            <!-- 配置拦截器作用的路径 -->
            <mvc:mapping path="/**"/>
            <!--定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截-->
            <bean class="interceptor.Interceptor1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- 配置拦截器作用的路径 -->
            <mvc:mapping path="/gotoTest"/>
            <!--定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截-->
            <bean class="interceptor.Interceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>

测试多个拦截器的输出如下

Interceptor1 preHandle 方法执行
Interceptor2 preHandle 方法执行
正在测试拦截器,执行控制器的处理请求方法中
Interceptor1 postHandle 方法执行
Interceptor2 postHandle 方法执行
视图渲染结束
Interceptor1 afterCompletion 方法执行
Interceptor2 afterCompletion 方法执行

SpringMVC拦截器实现用户登陆权限验证

本节将通过拦截器来完成一个用户登录权限验证的 Web 应用,具体要求如下:只有成功登录的用户才能访问系统的主页面 main.jsp,如果没有成功登录而直接访问主页面,则拦截器将请求拦截,并转发到登录页面 login.jsp。当成功登录的用户在系统主页面中单击“退出”链接时回到登录页面。

创建pojo

    public class User {
        private String uname;
        private String upwd;
        //省略setter和getter方法
    }

创建控制器类

import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.User;
@Controller
public class UserController {
    /**
     * 登录页面初始化
     */
    @RequestMapping("/toLogin")
    public String initLogin() {
        return "login";
    }
    /**
     * 处理登录功能
     */
    @RequestMapping("/login")
    public String login(User user, Model model, HttpSession session) {
        System.out.println(user.getUname());
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpwd())) {
            // 登录成功,将用户信息保存到session对象中
            session.setAttribute("user", user);
            // 重定向到主页面的跳转方法
            return "redirect:main";
        }
        model.addAttribute("msg", "用户名或密码错误,请重新登录! ");
        return "login";
    }
    /**
     * 跳转到主页面
     */
    @RequestMapping("/main")
    public String toMain() {
        return "main";
    }
    /**
     * 退出登录
     */
    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        // 清除 session
        session.invalidate();
        return "login";
    }
}

创建拦截器

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        // 获取请求的URL
        String url = request.getRequestURI();
        // login.jsp或登录请求放行,不拦截
        if (url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) {
            return true;
        }
        // 获取 session
        HttpSession session = request.getSession();
        Object obj = session.getAttribute("user");
        if (obj != null)
            return true;
        // 没有登录且不是登录页面,转发到登录页面,并给出提示错误信息
        request.setAttribute("msg", "还没登录,请先登录!");
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
                response);
        return false;
    }
    @Override
    public void afterCompletion(HttpServletRequest arg0,
            HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        // TODO Auto-generated method stub
    }
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
            Object arg2, ModelAndView arg3) throws Exception {
        // TODO Auto-generated method stub
    }
}

配置拦截器

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类 -->
    <context:component-scan base-package="controller" />
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 配置拦截器作用的路径 -->
            <mvc:mapping path="/**" />
            <bean class="interceptor.LoginInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

创建JSP页面(login.jsp)

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!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=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        ${msg }
        <form action="${pageContext.request.contextPath }/login" method="post">
            用户名:<input type="text" name="uname" /><br>
            密码:<input type="password" name="upwd" /><br>
            <input type="submit" value="登录" />
        </form>
    </body>
    </html>

创建JSP页面(main.jsp)

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!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=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        当前用户:${user.uname }<br />
        <a href="${pageContext.request.contextPath }/logout">退出</a>
    </body>
    </html>

Spring MVC数据验证

用户的输入一般是随意的,为了保证数据的合法性,数据验证是所有 Web 应用必须处理的问题

Spring MVC 的 Converter 和 Formatter 在进行类型转换时是将输入数据转换成领域对象的属性值(一种 Java 类型),一旦成功,服务器端验证器就会介入。也就是说,在 Spring MVC 框架中先进行数据类型转换,再进行服务器端验证。

服务器端验证对于系统的安全性、完整性、健壮性起到了至关重要的作用。在 Spring MVC 框架中可以利用 Spring 自带的验证框架验证数据,也可以利用 JSR 303 实现数据验证。

创建自定义 Spring 验证器时需要实现的 Validator 接口和工具类 ValidationUtils

示例:
要求检查商品名和商品详情不能为空,商品价格在 0 到 100,创建日期不能在系统日期之后

创建数据输入页面 addGoods.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!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=UTF-8">
<title>添加商品</title>
</head>
<body>
    <form:form modelAttribute="goods"
        action="${pageContext.request. contextPath }/goods/save" method="post">
        <fieldset>
            <legend> 添加一件商品 </legend>
            <P>
                <label>商品名:</label>
                <form:input path="gname" />
            </p>
            <p>
                <label>商品详情:</label>
                <form:input path="gdescription" />
            </p>
            <P>
                <label>商品价格:</label>
                <form:input path="gprice" />
            </p>
            <P>
                <label>创建日期:</label>
                <form:input path="gdate" />
                (yyyy-MM-dd)
            </p>
            <p id="buttons">
                <input id="reset" type="reset">
                <input id="submit" type="submit" value="添加">
            </p>
        </fieldset>
        <!-- 取出所有验证错误 -->
        <form:errors path="*" />
    </form:form>
</body>
</html>

编写模型类 Goods

package pojo;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

public class Goods {
    private String gname;
    private String gdescription;
    private double gprice;
    // 日期格式化(需要在配置文件中配置FormattingConversionServiceFactoryBean)
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date gdate;

    public String getGname() {
        return gname;
    }

    public void setGname(String gname) {
        this.gname = gname;
    }

    public String getGdescription() {
        return gdescription;
    }

    public void setGdescription(String gdescription) {
        this.gdescription = gdescription;
    }

    public double getGprice() {
        return gprice;
    }

    public void setGprice(double gprice) {
        this.gprice = gprice;
    }

    public Date getGdate() {
        return gdate;
    }

    public void setGdate(Date gdate) {
        this.gdate = gdate;
    }
}

编写验证器类GoodsVaildator并使用 @Component 注解将 GoodsValidator 类声明为验证组件

package validator;

import java.util.Date;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import pojo.Goods;

@Component
public class GoodsValidator implements Validator {
    @Override
    public boolean supports(Class<?> klass) {
        // 要验证的model,返回值为false则不验证
        return Goods.class.isAssignableFrom(klass);
    }

    @Override
    public void validate(Object object, Errors errors) {
        Goods goods = (Goods) object; // object要验证的对象
        // goods.gname.required是错误消息属性文件中的编码(国际化后对应的是国际化的信息)
        ValidationUtils.rejectIfEmpty(errors, "gname", "goods. gname.required");
        ValidationUtils.rejectIfEmpty(errors, "gdescription",
                "goods.gdescription.required");
        if (goods.getGprice() > 100 || goods.getGprice() < 0) {
            errors.rejectValue("gprice", "gprice.invalid");
        }
        Date goodsDate = goods.getGdate();
        // 在系统时间之后
        if (goodsDate != null && goodsDate.after(new Date())) {
            errors.rejectValue("gdate", "gdate.invalid");
        }
    }
}

编写错误消息属性文件 errorMessages.properties

goods.gname.required=请输入商品名称
goods.gdescription.required=请输入商品详情
gprice.invalid=价格为0~100
gdate.invalid=创建日期不能在系统日期之后

在配置文件中声明一个 messageSource bean让SpringMVC从该文件中获取错误信息

    <!-- 配置消息属性文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/resource/errorMessages" />
    </bean>

编写service层接口

    package service;
    import java.util.ArrayList;
    import pojo.Goods;
    public interface GoodsService {
        public boolean save(Goods g);
        public ArrayList<Goods> getGoods();
    }

编写service层实现类

package service;

import java.util.ArrayList;
import org.springframework.stereotype.Service;
import pojo.Goods;

@Service
public class GoodsServiceImpl implements GoodsService {
    // 使用静态集合变量goods模拟数据库
    private static ArrayList<Goods> goods = new ArrayList<Goods>();

    @Override
    public boolean save(Goods g) {
        goods.add(g);
        return true;
    }

    @Override
    public ArrayList<Goods> getGoods() {
        return goods;
    }
}

编写控制器类 GoodsController,在该类中使用 @Resource 注解注入自定义验证器。另外,在控制器类中包含两个处理请求的方法

package controller;

import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.Goods;
import service.GoodsService;

@Controller
@RequestMapping("/goods")
public class GoodsController {
    // 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(GoodsController.class);

    @Autowired
    private GoodsService goodsService;
    // 注解验证器相当于"GoodsValidator validator=new GoodsValidator () ; "
    @Resource
    private Validator validator;

    @RequestMapping("/input")
    public String input(Model model) {
        // 如果model中没有goods属性,addGoods.jsp会抛出异常
        // 因为表单标签无法找到modelAttribute属性指定的form backing object
        model.addAttribute("goods", new Goods());
        return "addGoods";
    }

    @RequestMapping("/save")
    public String save(@ModelAttribute Goods goods, BindingResult result,
            Model model) {
        this.validator.validate(goods, result); // 添加验证
        if (result.hasErrors()) {
            return "addGoods";
        }

        goodsService.save(goods);
        logger.info("添加成功");
        model.addAttribute("goodsList", goodsService.getGoods());
        return "goodsList";
    }
}

编写配置文件 springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描包 -->
    <context:component-scan base-package="controller" />
    <context:component-scan base-package="service" />
    <context:component-scan base-package="validator" />
    <!-- 注册格式化转换器,因为用到日期转换 -->
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    </bean>
    <!-- 配置视图解析器 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 配置消息属性文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/resource/errorMessages" />
    </bean>
</beans>

创建数据显示页面 goodsList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <table>
        <tr>
            <td>商品名</td>
            <td>商品详情</td>
            <td>商品价格</td>
            <td>创建日期</td>
        </tr>
        <c:forEach items="${goodsList }" var="goods">
            <tr>
                <td>${goods.gname }</td>
                <td>${goods.gdescription }</td>
                <td>${goods.gprice }</td>
                <td>${goods.gdate }</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

创建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" 
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
    <!--配置DispatcherServlet-->
    <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>
    <!--避免中文乱码-->
    <filter>
        <filter-name>encodingFilter</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>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Spring MVC JSR-303验证框架之Hibernate-Validator

对于 JSR 303 验证,目前有两个实现,一个是 Hibernate Validator,一个是 Apache BVal。本教程采用的是 Hibernate Validator,注意它和 Hibernate 无关,只是使用它进行数据验证。

下载与安装 Hibernate Validator

用户可以通过地址“https://sourceforge.net/projects/hibernate/files/hibernate-validator/”下载 Hibernate Validator,本教程选择的是 hibernate-validator-4.3.2.Final-dist.zip。

首先将下载的压缩包解压,然后将 \hibernate-validator-4.3.2.Final\dist 目录下的 hibernate-validator-4.3.2.Final.jar 和 \hibernate-validator-4.3.2.Final\dist\lib\required 目录下的 jboss-logging-3.1.0.Final.jar、validation-api-1.0.0. GA.jar 复制到应用的 \WEB-INF\lib 目录下。

配置属性文件与验证器

如果将验证错误消息放在属性文件中,那么需要在配置文件中配置属性文件,并将属性文件与 Hibernate Validator 关联,具体配置代码如下:

<!-- 配置消息属性文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <!-- 资源文件名 -->
    <property name="basenames">
        <list>
            <value>/WEB-INF/resource/errorMessages</value>
        </list>
    </property>
    <!-- 资源文件编码格式 -->
    <property name="fileEncodings" value="utf-8" />
    <!-- 对资源文件内容缓存的时间,单位为秒 -->
    <property name="cacheSeconds" value="120" />
</bean>
<!-- 注册校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <!-- hibernate 校验器 -->
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
    <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用 classpath下的 ValidationMessages.properties -->
    <property name="validationMessageSource" ref="messageSource" />
</bean>
<!--开启 Spring的 Valid 功能 -->
<mvc:annotation-driven conversion-service="conversionService" validator="validator" />

标注类型

JSR 303 不需要编写验证器,但需要利用它的标注类型在领域模型的属性上嵌入约束。
1)空检查

@Null:验证对象是否为 null。
@NotNull:验证对象是否不为 null,无法检查长度为 0 的字符串。
@NotBlank:检查约束字符串是不是 null,以及被 trim 后的长度是否大于 0,只针对字符串,且会去掉前后空格。
@NotEmpty:检查约束元素是否为 null 或者是 empty。

示例如下:

@NotBlank(message="{goods.gname.required}") //goods.gname.required为属性文件的错误代码
private String gname;
2)boolean 检查

@AssertTrue:验证 boolean 属性是否为 true。
@AssertFalse:验证 boolean 属性是否为 false。

示例如下:

@AssertTrue
private boolean isLogin;
3)长度检查

@Size(min=,max=):验证对象(Array、Collection、Map、String)长度是否在给定的范围之内。
@Length(min=,max=):验证字符串长度是否在给定的范围之内。

示例如下:

@Length(min=1,max=100)
private String gdescription;
4)日期检查

@Past:验证 Date 和 Callendar 对象是否在当前时间之前。
@Future:验证 Date 和 Calendar 对象是否在当前时间之后。
@Pattern:验证 String 对象是否符合正则表达式的规则。

示例如下:

@Past(message="{gdate.invalid}")
private Date gdate;
5)数值检查
名称 说明
@Min 验证 Number 和 String 对象是否大于指定的值
@Max 验证 Number 和 String 对象是否小于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值,这个约束的参数是一个通过 BigDecimal 定义的最大值的字符串表示,小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值,这个约束的参数是一个通过 BigDecimal 定义的最小值的字符串表示,小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否符合指定格式的数字,integer 指定整数精度,fraction 指定小数精度
@Range(min=,max=) 检查数字是否介于 min 和 max 之间
@Valid 对关联对象进行校验,如果关联对象是个集合或者数组,那么对其中的元素进行校验,如果是一个 map,则对其中的值部分进行校验
@CreditCardNumber 信用卡验证
@Email 验证是否为邮件地址,如果为 null,不进行验证,通过验证
示例如下:

@Range(min=10,max=100,message="{gprice.invalid}")
private double gprice;

示例代码:
模型类Goods中使用JSP303对属性进行验证:

    public class Goods {
        //goods.gname.required错误消息 key(国际化后对应的就是国际化的信息)
        @NotBlank(message="{goods.gname.required}")
        private String gname;
        @NotBlank(message="{goods.gdesciption.required}")
        private String gdescription;
        @Range(min=0,max=100,message="{gprice.invalid}")
        private double gprice;
        // 日期格式化(需要在配置文件中配置FormattingConversionServiceFactoryBean)
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        @Past(message="{gdate.invalid}")
        private Date gdate;
        //省略setter和getter方法

在控制器类 GoodsController 中使用 @Valid 对模型对象进行验证

package controller;

import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.Goods;
import service.GoodsService;

@Controller
@RequestMapping("/goods")
public class GoodsController {
    // 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(GoodsController.class);
    @Autowired
    private GoodsService goodsService;
    @RequestMapping("/input")
    public String input(Model model) {
        // 如果model中没有goods属性,addGoods.jsp会抛出异常
        // 因为表单标签无法找到modelAttribute属性指定的form backing object
        model.addAttribute("goods", new Goods());
        return "addGoods";
    }

    @RequestMapping("/save")
    public String save(@ModelAttribute Goods goods, BindingResult result,Model model) {
        if (result.hasErrors()) {
            return "addGoods";
        }
        goodsService.save(goods);
        logger.info("添加成功");
        model.addAttribute("goodsList", goodsService.getGoods());
        return "goodsList";
    }
}

配置文件 springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描包 -->
    <context:component-scan base-package="controller" />
    <context:component-scan base-package="service" />
    <!-- 配置消息属性文件 -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 资源文件名 -->
        <property name="basenames">
            <list>
                <value>/WEB-INF/resource/errorMessages</value>
            </list>
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 对资源文件内容缓存的时间,单位为秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>
    <!-- 注册校验器 -->
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- hibernate 校验器 -->
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
        <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用 classpath下的 VaiidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>
    <!--开启 SpringValid 功能 -->
    <mvc:annotation-driven conversion-service="conversionService"
        validator="validator" />
    <!-- 注册格式化转换器,因为用到日期转换 -->
    <bean id="conversionService" 
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    </bean>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

Java国际化概念和使用介绍

资源文件的命名可以有如下 3 种形式:

baseName.properties : 程序默认显示的值
baseName_zh_CN.properties : 某语言
baseName_en_US.properties : 某语言

Spring MVC的国际化

语言区域的选择

在 Spring MVC 中可以使用语言区域解析器 bean 选择语言区域,该 bean 有 3 个常见实现,即 AcceptHeaderLocaleResolver、SessionLocaleResolver 和 CookieLocaleResolver。

AcceptHeaderLocaleResolver

根据浏览器 Http Header 中的 accept-language 域设定(accept-language 域中一般包含了当前操作系统的语言设定,可通过 HttpServletRequest.getLocale 方法获得此域的内容)。

改变 Locale 是不支持的,即不能调用 LocaleResolver 接口的 setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale)方法设置 Locale。

SessionLocaleResolver

根据用户本次会话过程中的语言设定决定语言区域(例如用户进入首页时选择语言种类,则此次会话周期内统一使用该语言设定)。

CookieLocaleResolver

根据 Cookie 判定用户的语言设定(Cookie 中保存着用户前一次的语言设定参数)。

由上述分析可知,SessionLocaleResolver 实现比较方便用户选择喜欢的语言种类,教程中使用该方法进行国际化实现。

下面是使用 SessionLocaleResolver 实现的 bean 定义:

<bean id="localeResolver" class="org.springframework.web.servlet.il8n.SessionLocaleResolver">
    <property name="defaultLocale" value="zh_CN"></property>
</bean>

如果采用基于 SessionLocaleResolver 和 CookieLocaleResolver 的国际化实现,必须配置 LocaleChangeInterceptor 拦截器,示例代码如下:

<mvc:interceptors>
    <bean class="org.springframework.web.servlet.il8n.LocaleChangeInterceptor"/>
</mvc:interceptors>

Spring MVC使用SessionLocaleResolver实现用户自定义切换语言实例

在控制器类 GoodsController 中使用 @Valid 对模型对象进行验证,具体代码如下:

package controller;

import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.Goods;
import service.GoodsService;

@Controller
@RequestMapping("/goods")
public class GoodsController {
    // 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(GoodsController.class);
    @Autowired
    private GoodsService goodsService;
    @RequestMapping("/input")
    public String input(Model model) {
        // 如果model中没有goods属性,addGoods.jsp会抛出异常
        // 因为表单标签无法找到modelAttribute属性指定的form backing object
        model.addAttribute("goods", new Goods());
        return "addGoods";
    }

    @RequestMapping("/save")
    public String save(@ModelAttribute Goods goods, BindingResult result,Model model) {
        if (result.hasErrors()) {
            return "addGoods";
        }
        goodsService.save(goods);
        logger.info("添加成功");
        model.addAttribute("goodsList", goodsService.getGoods());
        return "goodsList";
    }
}

配置文件 springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描包 -->
    <context:component-scan base-package="controller" />
    <context:component-scan base-package="service" />
    <!-- 配置消息属性文件 -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 资源文件名 -->
        <property name="basenames">
            <list>
                <value>/WEB-INF/resource/errorMessages</value>
            </list>
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 对资源文件内容缓存的时间,单位为秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>
    <!-- 注册校验器 -->
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- hibernate 校验器 -->
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
        <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用 classpath下的 VaiidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>
    <!--开启 Spring的 Valid 功能 -->
    <mvc:annotation-driven conversion-service="conversionService"
        validator="validator" />
    <!-- 注册格式化转换器,因为用到日期转换 -->
    <bean id="conversionService" 
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    </bean>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

小型项目(框架整合)

SSM整合,MyBatis层

创建一个maven普通项目,添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<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.kuang</groupId>
    <artifactId>ssmbuild</artifactId>
    <version>1.0-SNAPSHOT</version>


    <!--依赖-->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

    </dependencies>


    <!--静态资源导出问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

</project>

创建database.properties配置文件,用于存储数据库相关配置

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/table?userSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root

创建MaBatis的核心配置文件,编写mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--配置数据源,交给Spring去做-->

    <!--别名-->
    <typeAliases>
        <package name="com.kuang.pojo"></package>
    </typeAliases>

    <!--映射mapper的配置文件-->
    <mappers>
        <mapper class="com.kuang.dao.BookMapper"></mapper>
    </mappers>

</configuration>

编写实体类(pojo)

package com.kuang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 数据库表对应的类
 * 字段对应属性,类型也要对应
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books{
    private int bookID;
    private String bookName;
    private int bookCounts;
    private String details;
}

对应实体类pojo编写接口

package com.kuang.dao;

import com.kuang.pojo.Books;

import java.util.List;

public interface BookMapper{
    //增加一本书
    public int addBook(Books books);
    //删除一本书
    public int deleteBookById(int id);
    //更新一本书
    public int updateBook(Books books);
    //查询一本书
    public Books queryBookById(int id);
    //查询所有的书
    public List<Books> queryAllBook();
}

编写BookMapper对应的xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springmybatis.demo.BookMapper">

    <insert id="addBook" parameterType="Books">
        insert into books(bookName,bookCounts,detail)
        value (#{bookName},#{bookCounts},#{detail})
    </insert>

    <delete id="deteleBookById" parameterType="int">
        delete from books where bookID=#{bookId}
    </delete>

    <update id="updateBook" parameterType="Books">
        update books set bookName=#{bookName},bookCounts=#{bookCounts},detail=#{detail}\
        where bookID=#{bookId}
    </update>

    <select id="queryBookById" resultSetType="Books">
        select * from books
        where bookID=#{bookId}
    </select>

    <select id="queryAllBook" resultSetType="Books">
        select * from books;
    </select>

</mapper>

编写业务层接口和实现类

public interface BookService {
    //增加一本书
    public int addBook(Books books);
    //删除一本书
    public int deleteBookById(int id);
    //更新一本书
    public int updateBook(Books books);
    //查询一本书
    public Books queryBookById(int id);
    //查询所有的书
    public List<Books> queryAllBook();
}
public class BookServiceImpl implements BookService{

    // Service调用dao层:组合dao
    private BookMapper bookMapper;
    public void setBookMapper(BookMapper bookMapper){
        this.bookMapper=bookMapper;
    }

	// 业务层就是调用dao层
    @Override
    public int addBook(Books books) {
        return bookMapper.addBook(books);
    }

    @Override
    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    @Override
    public int updateBook(Books books) {
        return bookMapper.updateBook(books);
    }

    @Override
    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    @Override
    public List<Books> queryAllBook() {
        return bookMapper.queryAllBook();
    }
}

SSM整合:spring层

更具mvc的思想将整合也解成
整合dao层,创建spring-dao.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"
       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">

    <!--1.关联数据库配置文件-->
    <context:property-placeholder location="classpath:database.properties"></context:property-placeholder>

    <!--2.连接池:c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--3.sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"></property>
        <!--绑定Mybatis的配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    </bean>

    <!--配置dao接口扫描包,生成对应的dao层对象,动态的实现了Dao接口可以注入到Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--注入qlSessionFactory-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" ></property>
        <!--要扫描的dao包-->
        <property name="basePackage" value="com.kuang.dao"></property>
    </bean>

</beans>

spring整合service层,创建spring-service.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"
       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">

    <!--1.扫描service下的包-->
    <context:component-scan base-package="com.kuang.service">
    </context:component-scan>

    <!--2.将我们的所有业务类,注入到spring,可以通过配置,或者注解实现-->
    <bean id="BookServiceImpl" class="com.kuang.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper"></property>
    </bean>

    <!--3.声明式事务配置-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--aop-->


</beans>

SSM整合:springmvc层

增加web依赖,配置web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--DispatchServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</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>

    <!--乱码过滤-->
    <filter>
        <filter-name>encodingFilter</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>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--Session-->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

整合springmvc,编写spring-mvc.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
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--1.注解驱动-->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!--2.静态资源过滤-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    <!--3.扫描包:controller-->
    <context:component-scan base-package="com.kuang.controller"></context:component-scan>
    <!--4.视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

配置全局的spring配置文件applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <import resource="classpath:spring-dao.xml"></import>
    <import resource="classpath:spring-mvc.xml"></import>
    <import resource="classpath:spring-service.xml"></import>
</beans>

编写index.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
    <style>
      a{
        text-decoration: none;
        color: black;
        font-size: 18px;
      }
      h3{
        width:180px;
        height:38px;
        margin: 100px auto;
        text-align: center;
        line-height: 38px;
        background: deepskyblue;
        border-radius: 5px;
      }

    </style>

  </head>
  <body>

    <h3>
      <a herf="${pageContext.request.contextPath}/book/allbook">进入书籍页面</a>

    </h3>


  </body>
</html>

编写allbook.jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>书籍展示页面</title>

    <%--BookStrop美化界面--%>
    <link herf="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" ref="stylesheet">


  </head>
  <body>

  <div class="container">

    <div class="row clearfix">
      <div class="col-md-12 column">
        <div class="pape-header">
          <h1>
            <small>书籍列表---显示所有书籍</small>
          </h1>
        </div>
      </div>
    </div>

    <div class="row clearfix">
      <div class="col-md-12 column">
        <table class="table table-hover table-striped">
          <thead>
            <th>书籍编号</th>
            <th>书籍名称</th>
            <th>书籍数量</th>
            <th>书籍详情</th>
          </thead>
          <tbody>
            <c:forEach var="book" items="${list}">
              <tr>${book.bookID}</tr>
              <tr>${book.bookName}</tr>
              <tr>${book.bookCounts}</tr>
              <tr>${book.details}</tr>
            </c:forEach>
          </tbody>
        </table>
      </div>
    </div>



  </div>

  </body>
</html>

Spring MVC统一异常处理的3种方式(附带实例)

Spring MVC 框架支持这样的实现。Spring MVC 统一异常处理有以下 3 种方式:

  • 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
  • 实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器。
  • 使用 @ExceptionHandler 注解实现异常处理

基于HandlerExceptionResolver接口实现自定义异常类

Spring 会搜索所有注册在其环境中的实现了 HandlerExceptionResolver 接口的 Bean,逐个执行,直到返回了一个 ModelAndView 对象

public class CustomExceptionHandler implements HandlerExceptionResolver {  
  
    @Override  
    public ModelAndView resolveException(HttpServletRequest request,  
            HttpServletResponse response, Object object, Exception exception) {  
        if(exception instanceof IOException){  
            return new ModelAndView("ioexp");  
        }else if(exception instanceof SQLException){  
            return new ModelAndView("sqlexp");  
        }  
        return null;  
    }  
} 

这个类必须声明到 Spring 配置文件中,或者使用 @Component 标签,让 Spring 管理它。同时 Spring 也提供默认的实现类 SimpleMappingExceptionResolver,需要使用时只需要使用注入到 Spring 配置文件进行声明即可。自定义实现类与默认的实现类,可同时使用

<!-- 自定义的实现类 -->
<bean id="exceptionHandler" class="com.enh.test.CustomExceptionHandler"/>
<!-- 默认的实现类注入 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
    <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->  
    <property name="defaultErrorView" value="error"></property>  
    <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->  
    <property name="exceptionAttribute" value="ex"></property>  
    <!-- 
    定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
    将不同的异常映射到不同的页面上。
   -->  
    <property name="exceptionMappings">  
        <props>  
            <prop key="IOException">error/ioexp</prop>  
            <prop key="java.sql.SQLException">error/sqlexp</prop>  
        </props>  
    </property>  
</bean> 

Controller内部单独实现

该方法需要定义在 Controller 内部,然后创建一个方法并用 @ExceptionHandler 注解来修饰用来处理异常,这个方法基本和 @RequestMapping 修饰的方法差不多,只是可以多一个类型为 Exception 的参数,@ExceptionHandler 中可以添加一个或多个异常的类型,如果为空的话则认为可以触发所有的异常类型错误

@Controller  
public class ExceptionHandlerController {  
    
	@RequestMapping("/demo1")
    @ResponseBody
    public Object demo1(){
        int i = 1 / 0;
        return new Date();
    }
  
    @ExceptionHandler(value={IOException.class,SQLException.class})  
    public String exp(Exception ex,HttpServletRequest request) {  
        request.setAttribute("ex", ex);  
        return "/error.jsp";  
    }  
  
}  

@ControllerAdvice+@ExceptionHandler

@ControllerAdvice
public class GlobalController{
	@ExceptionHandler(RuntimeException.class)
	public ModelAndView fix1(Exception e){
		System.out.println("全局的异常处理器");
		ModelMap map = new ModelMap();
		map.addAttribute("ex",e);
		return new ModelAndView("error",map);
	}
}

@Controller+@ExceptionHandler、HandlerExceptionResolver接口形式、@ControllerAdvice+@ExceptionHandler优缺点说明:

在Spring4.3.0版本下,
1.优先级来说,@Controller+@ExceptionHandler优先级最高,其次是@ControllerAdvice+@ExceptionHandler,最后才是HandlerExceptionResolver,说明假设三种方式并存的情况 优先级越高的越先选择,而且被一个捕获处理了就不去执行其他的.

2.三种方式都支持多种返回类型,@Controller+@ExceptionHandler、@ControllerAdvice+@ExceptionHandler可以使用Spring支持的@ResponseBody、ResponseEntity,而HandlerExceptionResolver方法声明返回值类型只能是 ModelAndView,如果需要返回JSON、xml等需要自己实现.

3.缓存利用,@Controller+@ExceptionHandler的缓存信息在ExceptionHandlerExceptionResolver的exceptionHandlerCache,@ControllerAdvice+@ExceptionHandler的缓存信息在ExceptionHandlerExceptionResolver的exceptionHandlerAdviceCache中, 而HandlerExceptionResolver接口是不做缓存的,在前面两种方式都fail的情况下才会走自己的HandlerExceptionResolver实现类,多少有点性能损耗.

Spring MVC文件上传

MultipartFile接口
依赖

<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.3.2</version>
</dependency>

前端页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8"%>
  <!DOCTYPE html>
  <html>
  <head>
  <meta charset="ISO-8859-1">
  <title>Insert title here</title>
  </head>
  <body>
      <form action="/ssm/file/imgUpload" enctype="multipart/form-data" method="post">
  	    <input type="file" name="file"><br><br>
  	    <input type="submit" value="上传">
      </form>
  </body>
  </html>

使用MultipartFile对象作为参数,接收前端发送过来的文件,将文件写入本地文件中,就完成了上传操作

@RequestMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest req)
		throws IllegalStateException, IOException {

	// 判断文件是否为空,空则返回失败页面
	if (file.isEmpty()) {
		return "failed";
	}
	// 获取文件存储路径(绝对路径)
	String path = req.getServletContext().getRealPath("/WEB-INF/file");
	// 获取原文件名
	String fileName = file.getOriginalFilename();
	// 创建文件实例
	File filePath = new File(path, fileName);
	// 如果文件目录不存在,创建目录
	if (!filePath.getParentFile().exists()) {
		filePath.getParentFile().mkdirs();
		System.out.println("创建目录" + filePath);
	}
	// 写入文件
	file.transferTo(filePath);
	return "success";
}  

springmvc.xml配置CommonsMultipartResolver

<bean id="multipartResolver"
	class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!--上传文件的最大大小,单位为字节 --> 
	<property name="maxUploadSize" value="17367648787"></property>
	 
	<!-- 上传文件的编码 -->
	<property name="defaultEncoding" value="UTF-8"></property>
</bean>

文件下载

	@RequestMapping("/download")
	public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception {
		// 1.得到要下载文件的真实路径
		ServletContext servletContext = request.getServletContext();
		String realPath = servletContext.getRealPath("/jQuery/jQuery-3.4.1.js");
		// 2.得到要下载的文件的流
		FileInputStream is = new FileInputStream(realPath);
		byte[] temp = new byte[is.available()];// 使用available获取的大小和文件大小相同
		is.read(temp);
		is.close();
		// 3.将要下载的文件流返回
		HttpHeaders httpHeaders = new HttpHeaders();// 自定义响应头
		httpHeaders.set("Content-Disposition", "attachment;filename=jQuery-3.4.1.js");
		return new ResponseEntity<byte[]>(temp, httpHeaders, HttpStatus.OK);
	}

多文件上传,就是把MultipartFile换成MultipartFile[]

	@RequestMapping("/upload")
	public String upload(@RequestParam(value = "username", required = false) String username,
			@RequestParam("headerimg") MultipartFile[] file, Model model) {
		System.out.println("上传的文件的信息:");
		for (MultipartFile multipartFile : file) {
			if (!multipartFile.isEmpty()) {
				try {
					model.addAttribute("msg", "文件上传成功");
					multipartFile.transferTo(new File("D:\\AAA\\" + multipartFile.getOriginalFilename()));
				} catch (IllegalStateException | IOException e) {
					model.addAttribute("msg", "文件上传失败" + e.getMessage());
				}
			}
		}
		return "forward:/index.jsp";
	}

狂神说 SpringMVC 上课笔记

狂神说SpringMVC01:什么是SpringMVC

狂神说SpringMVC02:第一个MVC程序

狂神说SpringMVC03:RestFul和控制器

狂神说SpringMVC04:数据处理及跳转

狂神说SpringMVC05:整合SSM框架

狂神说SpringMVC06:Json交互处理

狂神说SpringMVC07:Ajax研究

狂神说SpringMVC08:拦截器+文件上传下载

持续更新

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值