C 前端控制器 ——> DispatcherServlethtml
M 数据对象前端
V 视图处理器ViewResvorjava
发起请求到前端控制器 DispatcherServlet
web
而后这个控制器会调用 HandlerMapping
查找对应的 Controller
或者说 Handler
spring
找到了对应的 Controller
就让 HandlerAdaptor
去执行 handler
数据库
执行了 handler
之后返回的就是 ModelAndView
对象。数组
对象返回给前端控制器,而后前端控制器会丢给 ViewResovr
去解析spring-mvc
而后继续返回给前端控制器并返回给用户。markdown
在 web.xml 中咱们须要配一个 Servlet 和一个 Listener ,这个 Servlet 其实就是咱们的路由调度器,而后 Listener 则是上下文监听器。还有一个初始化参数就是指定 spring 的配置文件的位置。session
其实这些配置基本都是固定的,在使用 idea 创建 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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置 spring 的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!--1. / 全部的都进行解析,可是无论 jsp
2. *.from
3. /* 不能用 全部的都拦截
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>HttpHiddenMethods</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpHiddenMethods</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
复制代码
接着就是配置 spring 配置文件,能够看到在上面的 web.xml 中咱们在初始化参数中指定了 spring 的配置文件就是 applicationContext.xml 放在了 WEB-INF 路径下面。
而后须要配置一些核心的 bean 让 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"
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 http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--<!–处理器映射器–>-->
<!--<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>-->
<!--<!–处理器适配器–>-->
<!--<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>-->
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB_INF/templates/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置须要扫描的包-->
<context:component-scan base-package="com.mvc"/>
<!--必需要的,配置了 springMVC 的注解生效,否则的话咱们的 url 映射仍是经过配置文件的方式生效的
否则一直处于 404 状态,这也就是下面的 dispatcher-servlet.xml 的做用配置 url 映射
-->
<mvc:annotation-driven />
</beans>
复制代码
@Controller
public class Test {
@RequestMapping("/helloTest")
public String hello(){
return "hello";
}
}
复制代码
这样咱们的程序就可以跑起来了。
通常的咱们没法直接使用 RESTful 风格的请求,可是在 SpringMVC 中有一个过滤器能够帮咱们把一个 post 请求转化成为 PUT
或者 DELETE
请求。具体的作法以下:
<filter>
<filter-name>HttpHiddenMethods</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpHiddenMethods</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
复制代码
<form method="post">
<input type="hidden" name="_method" value="PUT">
</form>
<form method="post">
<input type="hidden" name="_method" value="DELETE">
</form>
复制代码
这个注解主要就是用来作方法和类的 url 映射的,也就是至关于一个路由映射文件。这个注解可使用在类上面也可使用在方法上面,在类上面的话就是咱们访问每个方法的时候都须要加上类的 url 前缀。
他有几个比较重要的属性,用来管理 url 的:
value: 这个是默认的,是 url 地址
method:这个是用来指定请求方式的 RequestMethod.GET/POST/DELETE/PUT ….
params:这个是用来规定咱们的 url 中携带的参数的他是一个数组,能够放多个值
params = {"username","age!=10"} params 是包含 username age!=10 headers 也是如此只是规定了请求头而已
这个注解的做用是用来传递参数的,咱们不只仅可以使用 ?
来传递参数,还可使用更优雅的 /paramName/value
的方式来传递参数,而且可以直接在方法中绑定这些参数。
@RequestMapping(value = "/helloworld/{id}")
public String hello(@PathVariable("id") String id){
System.out.println("hello controller");
return "hello";
}
复制代码
这里的 id 必须和 url 中的保持一致,可是无需和方法的参数保持一致。
可是有一个问题,当咱们传递过来的参数是经过 ?
的形式传递过来的,那么咱们又该怎么去获取他呢?是的这里咱们可使用 @RequestParam
方法来获取这些值,当咱们有些参数不是必须传递的咱们就可使用 require=false
来规定不用必传,这个时候若是咱们没有传值,这个参数恰好是一个引用类型的就会是 null
可是若是是一个基本数据类型,就会报错。咱们必须手动的设置一个默认值。
@RequestParam(value = "age",required = false,defaultValue = "0") Integer age
复制代码
上面的参数就能够获取到 http://localhost/hello?age=10
这种的 url 的参数了、
用法同上!
@RequestHeader("Content-Type") String content
复制代码
这个使用同上,获取 Cookie 的值。
咱们的表单提交过来的数据会自动的被封装到 POJO 对象中,咱们只须要配置好表单的 name 值和 Bean 的属性值一致便可,若是说里面有级联的属性,咱们就使用 proA.proB
来封装。例如:
<form method="post">
<input type="text" name="name"/>
<input type="text" name="age"/>
<input type="text" name="address.code">
<input type="text" name="address.name">
</form>
复制代码
这个表单就会被封装成一个 POJO 对象,这个对象里面有另一个类的引用就致使了级联属性的出现,咱们是就是使用了点的方式完成的封装。
它支持比较多的原生的 Servlet 的 API ,实际上是在他内部调用了 request 对象的一些方法获取到的。
HttpServletRequest
HTTPServletResponse
HttpSession
Locale
InputStream
OutputStream
Reader
Writer
Principal
通常咱们须要在控制器里面绑定一些数据到视图中,而后咱们能够在视图里面采用标签来获取 Controller 里面的数据从而展现这些数据,在 SpringMVC 中有几种方法能够达到这个目的。
这个东西其实就像他的名字同样,他是 Model 数据和模型的结合体,咱们能够往里面添加数据(Model),也能够把要转发的页面放在里面让视图解析器去渲染。因此说这个对象里面有一个 Model 属性,这个属性就是用来存放数据的,其实就是一个 Map 。Map 里面的这些数据都会被遍历而后放到 request 域对象之中,咱们只须要在请求域中获取就好。
/**
* ModelAndView 来用做 Controller 与 View 之间的数据交互的介质
* 也就是 Model 的载体
* @return
*/
public ModelAndView modelAndViewTest(){
ModelAndView modelAndView = new ModelAndView("hello");
modelAndView.addObject("time", new Date());
return modelAndView;
}
复制代码
其实三个东西类型都是 Map 类型的,而后SpringMVC 在真正的传入的对象显然就是他们的实现类,这里咱们不过度纠结,基层确定是一个 Map 。Map 里面的这些数据都会被遍历而后放到 request 域对象之中,咱们只须要在请求域中获取就好。
这个用起来也比较简单,就是在方法的入参里面传入这个一个东西就好了,而不是采用的返回值。Map 的具体的泛型就是 string 和 object。看下面的例子。
public String modelMap(Map<String,Object> map){
map.put("time", new Date());
return "hello";
}
复制代码
这个注解只能放在类上面,而后咱们使用 value 属性或者 types 属性来规定哪些属性须要被放在 Session 域中,这个两个属性其实都是一个数组,因此咱们能够方多个值。
value 这个属性,就是当咱们在放入 map 中的一个键名的时候咱们就能够把它放到 session 域中。而 types 属性则是当咱们放一个 class 的时候他会自动抓取处于 map 中的同类型的数据。
@SessionAttributes(value = {"time","username"},types = {String.class,Integer.class})
复制代码
这个注解是标识在方法上的,这个注解标识的方法会在全部的方法调用前被调用。在这个被标识的方法里面咱们须要从数据库中获取对应的对象,而后把这个对象放到 map 里面,可是注意 map 中的键必需要是咱们的 Model 类对应的小写的一个字符串才能起做用。当咱们使用其余的方法来进行某个 model 的修改动做的时候咱们某个字段不传的话这时候在 map 中的那个对象的对应字段挥起一个补充做用,把对应字段填上。
执行流程:
首先是执行了被这个注解标识的方法,将数据库中获取到的值放到了 map 里面,而后这个 map 是被放到了一个implicitModel 里面
而后在咱们提交一个表单的时候,咱们对应的方法的参数会到 implicitModel 里面查找对应的对象,查找的 key 就是先看看咱们的这个方法的参数是否被 ``@ModelAttribute(value="...")` 修饰。若是是的话咱们采用的是直接使用它的 value 属性做为 key 去查找。
若是没有这个注解修饰参数,则采用这个 POJO 的类名第一个字母小写做为 key 查找。
若是没有则看看是否这个类被 @SessionAttributes 注解 注释,若是是则是去 session 中查找,若是没有找到抛异常。
若是上面的状况都没找到,则是使用 POJO 反射建立一个新的对象把表单数据封装进去,而若是上面有找到的话咱们就使用那个 Model 而后设置对应的属性值。
接着把这个修改后的 model 放到 implicitModel 进而放到 request 域中。
视图的解析步骤:
首先咱们是访问了咱们的控制器。
而后咱们的控制器会返回 string 或者 VIewAndModel 对象。
他们都会被视图解析器(咱们在 spring 中配置的 bean)包装成一个 ModelAndView 对象。
最后渲染视图,并转发到对应的视图。
能够手动配置路由,不通过 controller 就能够访问到对应的视图。在 spring 配置文件里写上以下内容:
<mvc:annotation-driven />
<!--手动配置路由 直接路由到视图无需通过 controller-->
<mvc:view-controller path="/success" view-name="hello"/>
复制代码
那么咱们访问 http://localhost:8080/success
就被转发到 WEB_INF/templates/hello.jsp 具体的目录取决于咱们配置的视图解析器的前缀和后缀。
咱们通常采用的就是 InternalResourceViewResolver
这个视图解析器,咱们也能够自定义视图。自定义视图则须要一个特殊的视图解析器完成解析视图的工做,就是 BeanNameViewResolver
就是经过视图的 bean 的 name 来获取视图的。因为咱们配置了多个视图解析器则须要定义一个优先级,哪一个视图解析器先工做,使用 order 属性。
<!--自定义的 Bean视图解析器 直接经过 bean 的 name 获取视图-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"/>
</bean>
复制代码
下面是咱们使用 bean 定义的一个视图。
@Component
public class MyViewRevsor implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().write("hello");
}
}
复制代码
咱们通常在 controller 中写的东西默认都会转发的,咱们须要重定向的话咱们只须要在人绘制前面加上 redirect 就能够。
@RequestMapping("/beanView")
public String beanView(){
return "redirect:/myViewRevsor";
}
复制代码
对于静态资源咱们须要直接获取而不须要进行映射,因此说咱们在获取静态资源的时候回出现 404 ,咱们就配置一个
<mvc:default-servlet-handler/>
这个就会自动的处理没有映射的 url 。
表单数据在提交之后其实是依赖于 SpringMVC 里面一个 WebDataBinder 类进行的数据绑定,这个 WebDataBinder 里面有不少其余的对象的引用其中就有数据格式化、数据校验、数据转换的对象,也就是说在这个数据转换的过程咱们是能够添加一些对象来手动的控制数据的绑定的。
converter 这个是用来数据转换的,具体的参照文档,就好比咱们把前端的一个字符串转成方法入参的一个 bean 对象,就通过这个 converter 来完成。
@initBinder 被这个注解表示的方法,会在数据绑定以前进行运行,其功能就是对 binder 进行一些设置好比忽略一些字段。修改字段,拒绝字段等等。
@InitBinder public void initBinder(WebDataBinder binder) { // 拒绝 name 字段 binder.setDisallowedFields("name"); }
数据的格式化,采用注解注解在对应的 bean 的字段上。经常使用的有时间还有数子。
JSR303 校验规范,这只是 JavaEE 的规范,真正的实现类是 Hibernate ValidData ,而后咱们进行数据校验也是使用注解的方式,具体的注解在规范里面都有,都比较简单。而且咱们须要在方法的入参的 bean 上加上 @Valid 注解。
错误消息的回显,显然若是咱们的校验生效而且有错误的话咱们须要回显到表单。咱们就须要在方法的参数里面加上一个 BindResult 对象,而后错误的数据都会被放到这个东西里面,同时 BindResult 是一个 Error 类型的对象,因此咱们亦能够放这个对象。最后放到 map 里面在前端回显便可。
只须要在方法上加上 @ResponseBody 就能够,返回值是一个 List 或数组。
<!--配置拦截器-->
<mvc:interceptors>
<!--自定义的拦截器组件-->
<bean class="com.mvc.MyInterceptor"/>
<!--更详细的配置能够针对 url-->
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<mvc:exclude-mapping path="/hah"/>
<bean class="com.mvc.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
复制代码
统一异常处理就采用对应的 handler 来处理异常,使用 @ExceptionHandler 注解,而后标注要处理的异常类型,可是若是说咱们须要把异常带到错误页面咱们不能使用 map 而只能使用 ModelAndView 否则那就会报错。
@ExceptionHandler({ArithmeticException.class})
public ModelAndView error(Exception ex){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("ex", ex);
return modelAndView;
}
复制代码
若是他在当前的 controller 中找不到对应的 ExceptionHandler 就去查找对应的 @ControllerAdvise 注解表示的类,中的ExceptionHandler 注解方法。也就是默认的处理器。