springmvc总结

1、SpringMVC 和 Struts2 对比

①、SpringMVC 的入口是 Servlet,而Struts2是Filter

②、SpringMVC会稍微比Struts2快些,SpringMVC是基于方法设计的,而Struts2是基于类设计的,每次发一次请求都会实例一个Action.

③、SpringMVC使用更加简洁,开发效率比Struts2高。支持JSR303,处理ajax的请求更方便

④、Struts2的OGNL表达式使页面的开发效率相比SpringMVC更高些。

2、SpringMVC 和Spring 整合的问题

        是否还需要 整合spring?

        建议需要:通常情况下,类似于数据源,事务,整合其他框架都是放在Spring 的配置文件中

        (而不是放    在    SpringMVC        的        配        置文件中)

          实际上放入Spring配置文件对应的IOC容器中的还有Service 和 Dao.

          问题:若spring 的 IOC容器和SpringMVC 的 IOC 容器扫描的包有重合的部分,就会导致有的bean会被创建2次
                解决:
                1、使spring的IOC容器扫描的包和SpringMVC 的 IOC 容器扫描的包分开,没有重合的部分,
                可是在实际开发中,如果按模块建包,则无法满足该情况
                2、使用context:component-scan标签的context:exclude-filter、context:include-filter 
                节点来规定只能扫描的注解
                <context:component-scan base-package="com.test" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

springMVC 的 IOC 容器中的bean 可以来引用spring IOC容器中的bean,反之,则不行
即 spring IOC容器中的bean不能引用springMVC IOC容器中的bean。

因为 springMVC容器和spring容器有父子关系,spring容器为父容器,springMVC为子容器

3、SpringMVC工作流程

3、springMVC配置

在web.xml中配置DispatcherServlet

<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcherServlet-servlet.xml</param-value>
</init-param>

<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<init-param>标签:

springMVC配置文件默认在WEB-INF下,如果需要放在src下, 则需要在web.xml 配置DispatcherServlet中配置

用<init-param>标签进行配置。

注意:如果是用maven构建的项目,那么src目录是:src/main/resources(第一次用maven,在此折腾半天,才发现这个问题)

<load-on-startup>1</load-on-startup>:表示在WEB容器启动时就实例化DispatcherServlet,而不是等到该servlet被选择时。正数数字越小,优先级越高。

<url-pattern>/</url-pattern>:表示拦截所有的请求

如果用到REST风格URL,还需在web.xml中配置HidddenHttpMethodFilter

<!-- 配置HiddenHttpMethodFilter 作用可以把post请求转为delete或者put请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
然后配置springMVC配置文件

注意该配置文件的名字格式为 <servlet-name>-servlet.xml

<!-- 启用spring mvc 注解 -->
<context:annotation-config></context:annotation-config>

<!-- 需要扫描的spring控制类 -->
<context:component-scan base-package="com.test"></context:component-scan>
<context:component-scan base-package="com.cjl"></context:component-scan>


<!-- 视图解析器配置:如何把handler方法返回的值解析为实际的物理视图(该解析器用于JSP页面) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

<!-- 配置视图解析器BeanNameViewResolver:使用视图的名字解析视图 -->
<!-- 通过order属性来定义视图解析器的优先级,value越小优先级越高 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"></property>
</bean>

<!--配置直接跳转转发的页面 而无需经过Handler的方法 -->
<mvc:view-controller path="/hello" view-name="hello"/>
<!-- 直接跳转页码,不经过handler,在实际开发中,通常需要配置 mvc:annotation-driven 标签 -->
<mvc:annotation-driven></mvc:annotation-driven>

<!-- 静态资源 -->
<!--
default-servlet-handler 将在springmvc上下文中定义一个DefaultServletHttpRequestHandler
它会地进入DispatcherServlet的请求进行筛查,如果发现是没有映射的请求,就将改请求交由WEB应用服务器默认的
servlet 处理。如果不是静态资源的请求,才有DispatcherServlet继续处理

一般WEB应用服务器默认的servlet的名称都是default
若使用的WEB服务器的默认servlet名称不是default,则需要通过 default-servlet-name 属性显示指定
-->
<mvc:default-servlet-handler/>

<!--
配置多个拦截器,那么多个拦截器的执行顺序是什么?
[FirstInterceptor] preHandle
[SecondInterceptor] preHandle
listAllEmployees
[SecondInterceptor] postHandle
[FirstInterceptor] postHandle
[SecondInterceptor] afterCompletion
[FirstInterceptor] afterCompletion

preHandle 按正序执行
postHandle、afterCompletion 按倒序执行

若SecondInterceptor preHandle 返回false,则执行顺如下:
[FirstInterceptor] preHandle
[SecondInterceptor] preHandle
[FirstInterceptor] afterCompletion
-->
<!--配置拦截器 -->
<mvc:interceptors>
<!--配置自定义拦截器 -->
<bean class="com.test.interceptor.FirstInterceptor"></bean>


<!-- 配置拦截器作用的路径-->
<mvc:interceptor>
<mvc:mapping path="/listAllEmployees"/>
<!--
<mvc:exclude-mapping path="/emp"/> -->
<bean class="com.test.interceptor.SecondInterceptor"></bean>
</mvc:interceptor>

</mvc:interceptors>
<!--配置拦截器 -->
InternalResourceViewResolver:

①、级别默认最大,所以如果配置了其他视图解析器,都比它先执行,其他视图解析器,则按order顺序从小的数字开始执行。

②、prefix,suffix:该视图解析器的解析出该跳转的页面是:prefix+returnViewName+suffix,

我的例子即是:/WEB-INF/views/xx.jsp

③、实际开发中 <mvc:view-controller> <mvc:annotation-driver> 要一起配置

④、<mvc:default-servlet-handler> 用于静态资源映射

4、多个拦截器执行顺序

        [FirstInterceptor] preHandle
[SecondInterceptor]  preHandle
listAllEmployees  //目标方法
[SecondInterceptor]  postHandle
[FirstInterceptor]  postHandle
[SecondInterceptor]  afterCompletion
[FirstInterceptor]  afterCompletion

preHandle 按正序执行
postHandle、afterCompletion 按倒序执行
    
    若SecondInterceptor preHandle 返回false,则执行顺如下:
    [FirstInterceptor] preHandle
[SecondInterceptor]  preHandle
[FirstInterceptor]  afterCompletion

5、Rest风格的URL

新增:/order  post 

修改:/order/1  put

删除:/order/1 delete

获取:/order/1  get

如何发送put和delete请求呢?

①、先在springMVC配置文件中配置HiddenHttpMethodFilter

②、需要发送post请求

③、在发送post请求时,携带一个name=“_method”的隐藏域,值为PUT或者DELETE

如何得到请求中的参数?

在目标方法中用@PathVariable注解获取参数

以delete为例,示例如下:

<form action="testRest/1" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="testRest Delete"/>
</form>
@RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE)
public String testRestDelete(@PathVariable Integer id)
{
System.out.println("testRest DELETE:"+id);
return "hello";
}
 

6、@ModelAttribute注解

/**
* 1、有@ModelAttribute 修饰的方法,会在每个目标方法执行之前被springMVC调用
* 2、@ModelAttribute 注解也可以来修饰目标方法POJO类型的入参,其value 属性值有如下作用:
* 1)、spirngMVC 会使用value属性值在implicitModel 中查找对应的对象,若存在则会直接换入到目标方法的入参中
* 2)、springMVC会以value为key,POJO类型的对象为value,存入到request中。
*/
@ModelAttribute
public void geteUser(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map)
{
System.out.println("testModelAttribute method");
if(id != null)
{
//模拟从数据库取出一个对象
User user = new User("1", "tom", "123456");
System.out.println("从数据库取出一个对象:"+user);
map.put("abc", user);
}
}


/**
* 运行流程:
* 1、执行 @ModelAttribute 注解修饰的方法,从数据库取出对象,把对象放入map中,键为user
* 2、springMVC 从map中取出user对象,并把表单中的请求参数赋给该user对象的对应属性
* 3、springMVC 把上述对象传入目标方法的参数
* 注意:在 @ModelAttribute修饰的方法中,放入到map时的键需要和目标参数的第一个字母小写的字符串一致
*
* springMVC确定目标方法POJO类型入参的过程
* 1、确定一个key
* 1)、若目标方法的POJO类型的参数没有使用 @ModelAttribute 作为参数,则key为POJO类名第一个字母小写
* 2)、若使用了 @ModelAttribute来修饰,则key为 @ModelAttribute 注解的value属性值
*
* 2、在implicitModel中查找key对应的对象,若存,则作为入参传入
* 1)、若在 @ModelAttribute 标记的方法中在Map中保存过,且key和1确定的key一致,则会获取到
*
* 3、若implicitModel中不存在key对应的对象,则检查当前的Handler是否使用了 @SessionAttributes 注解修饰,
* 若使用了改注解,且 @SessionAttribute 注解的value属性值也包含了key,则会从HttpSession中来获取key所对应
* 的value值,若存在则直接传入到目标方法的入参中,若不存在则抛出异常
* 4、若Handler没有表示 @SessionAttributes注解 或 @SessionAttributes 注解的value值中不包含key,则会通过反射来创建POJO类型的参数,
* 传入为目标方法的参数
* 5、springMVC会把key 和 POJO类型的对象 保存到implicitModel中,进而会保存到request中。
*
*
* 源码分析:
* 1、首先调用@ModelAttribute 注解修饰的方法,实际上把@ModelAttribute方法中的map中的数据放在了 implicitmodel中
* 2、解析请求处理器的目标参数,实际上该目标参数来自于 WebDataBinder对象的target属性
* (1)、创建WebDataBinder 对象;
* ①、确定 objectname 属性:若传入的attrName 属性值为“”,则objectName 为类名第一个字母小写
* 注意:attrName 若目标方法的pojo属性使用了@ModelAttribute来修饰,则attrName 值即为@ModelAttribute的value 属性值
*
* ②、确定target 属性:
* > 在implicitModel 中查找attrName 对应的属性值,若存在,ok
* > *若不存在则验证当前Handler是否使用了@SessionAttribute进行修饰,若使用了,则 尝试从session中获取attrName 所对应的属性值,
* 若session中没有对应的属性值,则抛出异常
* > 若Handler没有使用@SessionAttribute进行修饰,或@SessionAttributes 中没有使用value值指定的key和attrName 相匹配,
* 则通过反射创建了POJO对象
*
* (2)、springMVC把表单请求的参数赋给了WebDataBinder 的 target 对应的属性
* (3)、*springMVC会把WebDataBinder的attrName 和target 给到 implicitModel,进而传到request 域对象中
* (4)、把WebDataBinder 的 target作为参数传递给目标方法的入参
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute(value="abc") User user)
{
System.out.println("修改:"+user);
return HELLO;
}
注意:①、@ModelAttribute 修饰的方法会在每个目标方法被调用之前调用。

②、如上例,如果 testModelAttribute 方法中,没有 @ModelAttribute(value="abc"),则spirngMVC默认以该POJO类名的第一个字母小写为key;如果有@ModelAttribute(value="abc"),且@ModelAttribute 修饰的方法中需要 map.put("abc", user);

这时,就不以POJO类名第一个字母小写为key,以@ModelAttribute(value="abc") value为key。

7、使用POJO对象绑定请求参数

使用POJO对象绑定请求参数,springMVC会按请求参数名和POJO属性名进行自动匹配,自动为该对象填充属性值,支持级联属性。

8、处理模型数据

 SpringMVC提供了以下几种途径输出模型数据:

 1)、ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据。

 2)、Map 及 Model:入参为 org.spirngframework.ui.Model、org.spriingframework.ui.ModelMap 或者 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。

 3)、@SessionAttributes:将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性。

    @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。

    --@SessionAttributes(types=User.class) 会将隐含模型中所有类型为User.class 的属性添加到会话中。

    --@SessionAttributes(value={"user1","user2"})

    --@SessionAttributes(types={User.class,Dept.class})

    --@SessionAttributes(value={"user1","user2"},types={Dept.class})

 4)、@ModelAttribute:方法入参标注该注解后,入参的对象就会放到数据模型中。属性暂存到HttpRequest中。

9、 @ModelAttribute @SessionAttributes  标注的属性,前台如何取?

request user:${requestScope.user }

session user: ${sessionScope.user }
注意:JSP页面EL表达式开关必须打开,否则页面不会正常显示,如下:

<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8" isELIgnored="false"%>
isELIgnored:是否忽略EL表达式;

如果为true,则${}中的表达式会原样输出,不会进行计算;

如果是false,则会计算${}中的表达式;

番外:目标方法中map中的值,页面如何取?

time:${requestScope.time}
10、处理静态资源

 优雅的REST风格的资源URL不希望带有.html  或 .do等后缀。

 若将DispatcherServlet 请求映射配置为/,则SpringMVC将会捕获WEB容器的所有请求,包括静态资源的请求,SpringMVC会将他们当成一个普通请求处理,因此找不到对应的处理器将导致错误。

可以在SpringMVC的配置中配置<mvc:default-seervlet-handler/>的方式解决静态资源的问题

<mvc:default-servlet-handler/>会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler,它会对进入DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由WEB应用服务器默认的Servlet处理,如果不是静态资源,才由DispatcherServlet继续处理。

一般WEB应用服务器默认的Servlet的名称都是default。若所使用的WEB服务器的默认Servlet名称不是default,则需要通过

default-servlet-name属性显示指定

11、数据绑定流程

1)、SpringMVC框架将ServletRequest 对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象

2)、DataBinder 调用装配在SpringMVC上下文中的ConversionService组件进行 数据类型转换、数据格式化 工作。将Servlet中的请求信息填充到入参对象中。

3)、调用Validator 组件对已经绑定了请求信息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData对象。

4)、SpringMVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参。

12、springMVC的各种注解

1)、@Controller

    @Controller注解的类表示该类是个控制器类。

2)、@PathVariable

    @PathVariable 绑定URL占位符的入参。带占位符的URL是spring3.0新增的功能,该功能在springMVC向REST目标挺进发展过程中具有里程碑的意义。

    通过 @PathVariable 可以将URL中占位符参数绑定到控制器处理方法的入参中:URL中的{xxx}占位符可以通过@PathVariable("xxx") 绑定到操作方法的入参中。

3)、@ResquestMapping

    使用 @ResquestMapping 映射请求

@ResquestMapping 可以用到类上,也可以用到方法上,如果类上加上了该注解,那么该control下的所有方法的mapping前面都会自动加上类上的mapping。例如:类上@ResquestMapping(value="/aaa"),方法上@ResquestMapping(value="/bbb.htm"),那么请求bbb.htm,实际的URL是:/aaa/bbb.htm

   ①、Ant 风格资源地址支持3中匹配符:

    --?:匹配文件名中的一个字符

    --* :匹配文件名中的任意字符

   --**:匹配多层路径

    ②、@RequestMapping就支持Ant风格的URL:

    -- /user/*/createUser:匹配

        /user/aaa/createUser、/user/bbb/createUser 等URL

   --/user/**/cereateUser:匹配

    /user/createUser 、/user/aaa/bbb/createUser 等 URL

4)、@SessionAttributes

若希望在多个请求之间公用某个模型属性数据,则可以在控制器类上 标注一个@SessionAttributes,SpringMVC将在模型中对应的属性暂存到HttpSession中。

5)、@ModelAttribute

    --在方法定义上使用@ModelAttribute注解:SpringMVC在调用目标处理方法前,会先逐个调用在方法上标注了@ModelAttribute的方法

    --在方法 的入参前使用 @ModelAttribute注解:

    ①、可以从隐含对象中获取隐含的模型数据中获取对象,在将请求参数绑定到对象中,再传入入参。

    ②、将方法入参对象添加到模型中。

6)、@AutoWired

    它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过@AutoWired的使用来消除 set,get 方法。

7)、@ReponseBody

    用来修饰Controller的目标方法,通过适当的HttpMessageConverter转换器把返回值由String转换为指定的格式,放在Response的body区。如转换为JSON

8)、@RequestHeader

    使用@RequestHeader 绑定请求报头的属性值。

    请求头包含了若干个属性,服务器可据此获知客户端的信息,通过@RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中。

9)、@CookieValue

    @CookieValue 可让处理方法入参绑定某个Cookie值

10)、@RequestParam

    在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法。

   --value:参数名

  --required:是否必须。默认为true,表示请求参数中必须包含对应的参数,若不存在,将抛出异常。

11)、@InitBinder

   由 @InitBinder 标识的方式,可以对WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成有表单字段到JavaBean属性的绑定。

  @InitBinder 方法不能有返回值,它必须声明为void

  @InitBinder 方法的参数通常是WebDataBinder 

12)、@Repository

    用来标注数据访问组件,即Dao组件

13)、@Component

    用于把普通POJO类融入Spring容器,相当于配置文件中的<bean id="" class="" />

    泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。

14)、@Service

    用于标注服务层,主要用来进行业务逻辑的处理

15)、@ExceptionHandler

    用来修饰方法,出现异常时,执行该方法

16)、@ControllerAdvice+@ExceptionHandler

    使一个Contoller成为全局的异常处理类

13、
Neither BindingResult nor plain target object for bean name 'command' available as request attribute

出错场景:经过一个action跳转到新增用户页面

@RequestMapping(value="/input",method=RequestMethod.GET)
public String input()
{
return "input";
}
 

原因是:用<form:form>标签,springMVC解析JSP之前,先去attribute找该form对应的POJO对象,如果没有找到,则用默认的对象command,然后也没找到,所以报错。

解决办法:

①、在JSP页面增加 <jsp:useBean id="command" class="com.cjl.beans.User" scope="request"></jsp:useBean> 引入该bean

②、在响应的controller中加入该代码 map.put("user", new User());,

然后在JSP页面,<form:form> 该标签中加入modelAttribute="user" 即可。

原文地址:https://www.cnblogs.com/zhx2654188344/p/14637267.html