SpringMVC---确定目标方法POJO类型参数
SpringMVC确定目标方法POJO类型入参的过程
- 确定一个key:
- 若目标方法的POJO类型的参数没有使用@ModelAttribute作为修饰,则key为POJO类名第一个字母小写
- 若使用了@ModelAttribute来修饰,则key为@ModelAttribute注解的value属性值
- 在implicitModel中查找key对应的对象,若存在,则作为参数传入
- 若在@ModeAttribute标记的方法中的Map中保存过,则key和1确定的key一直,则会获取到.
- 若implicitModel中不存在key中对应的对象,则检查当前的Handler是否使用@SessionAttributes注解修饰,若是用了该注解,且@SessionAttributes注解的value属性值包含了key,则会从HttpSession中获取key所对应的value值,若存在则直接传到目标方法的入参中,若不存在则抛出异常,
- 若Handler没有表示@SessionAttributes注解或@SessionAttributes注解的value之中不包含key,则回通过反射来创建POJO类型的参数,传入为目标方法的参数
- SpringMVC会把key和POJO类型的对象保存到implictiModel中,进而会保存到request中
@ModelAttribute注解修饰POJO类型的入参
- @ModelAttribute注解也可以来修饰目标方法POJO类型的入参,器value属性值有如下的作用
- SpringMVC会使用value属性值在implicitModek中查找对应的对象,若存在则会直接传入到目标方法的入参中,
- SpringMVC会以value为key,POJO类型的睢县为value,存入道request中
- 示例:使用@ModelAttribute注解修饰入参
@ModelAttribute//SpringMVC在执行映射请求方法之前,会先调用带有@ModelAttribute 注解的方法
public void getUser(@RequestParam(value = "uid",required = false) Integer uid,Map<String,Object> map){
//模拟从数据库中获取用户数据
User user =new User();
user.setUid(uid);
user.setUname("xiemaoshu");
user.setPassword("12345");
map.put("xiemaoshu",user);//将user数据保存到request请求域之中
System.out.println("获取uid = "+uid+" 的用户数据"+user);
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("xiemaoshu") User user){
System.out.println("执行修改操作:"+user);
return SUCCESS;
}
@SessionAttributes注解引发的异常
-
当使用@SessionAttributes注解修饰类的时候,如果目标方法的POJO类型参数没有在implicitModel中被找到,则回到HttpSession中查找,如果没有找到,则会抛出一个异常,
-
异常产生:
@SessionAttributes(types = {User.class,String.class},value = {"xiemaoshu"})
@Controller
public class HelloWorld {
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("xiemaoshu") User user){
System.out.println("执行修改操作:"+user);
return SUCCESS;
}
}
- 此时@SessionAttributes注解中的value值中有"xiemaoshu",于目标方法中的@ModelAttribute注解中的"xiemaoshu"一致.
- 因此此时执行目标方法时会先在HttpSession中寻找key为"xiemaoshu"的属性,如果没有找到,就会抛出异常
-
解决的方法有两种:
- 将入参的参数名于@SessionAttributes注解的value值不一致
- 添加@ModelAttribute注解修饰的方法,在方法中使用Map集合添加key为"xiemaoshu"的属性
-
修改参数名称
@SessionAttributes(types = {String.class},value = {"xiemaoshu"})
@Controller
public class HelloWorld {
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("abc") User user){
System.out.println("执行修改操作:"+user);
return SUCCESS;
}
- 添加@ModelAttribute注解的方法
@SessionAttributes(types = {User.class,String.class},value = {"xiemaoshu"})
@Controller
public class HelloWorld {
@ModelAttribute//SpringMVC在执行映射请求方法之前,会先调用带有@ModelAttribute 注解的方法
public void getUser(@RequestParam(value = "uid",required = false) Integer uid,Map<String,Object> map){
//模拟从数据库中获取用户数据
User user =new User();
user.setUid(uid);
user.setUname("xiemaoshu");
user.setPassword("12345");
map.put("xiemaoshu",user);//将user数据保存到request请求域之中
System.out.println("获取uid = "+uid+" 的用户数据"+user);
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("xiemaoshu") User user){
System.out.println("执行修改操作:"+user);
return SUCCESS;
}
}
视图解析流程分析
- 目标方法返回的类型无论是String类型还是ModelAndView最终SpringMVC都会解析为一个ModelAndView类型
- 随后再通过试图解析器解析出将逻辑视图解析为一个真实的物理视图
- 示例代码
@RequestMapping("/testView")
public String testView(String uid){
System.out.println(uid);
return SUCCESS;
}
- 在返回时打上断点
- 在DispatcherServlet类中会执行448行代码,得到一个mv对象,这个mv对象就是ModelAndView对象
- 此时ModelAndView中的属性为,此时的view属性还只是一个逻辑视图,字符串=“success”
- 接着在DispatcherServlet类中的461行中打上断点,此时会执行processDispatchResult()方法
- processDispatchResult()方法处理方法的返回结果,判断目标方法是否有异常,如果有异常就会将方法的返回页面定义为异常页面.
-
如果没有异常,则会执行一个重要的发那个方法reader()
-
reader()方法
- reader()发那个发中使用一个"View"类描述视图,执行了一个resolveViewName()方法,处理视图
- reader()发那个发中使用一个"View"类描述视图,执行了一个resolveViewName()方法,处理视图
-
resolveViewName()方法
-
执行完resolveViewName()方法之后,view属性中的url已经变为"/WEB-INF/pages/success.jsp"路径
JstlView
- 要导入的jar包
-
示例:编写三个国际化资源文件
- i18n.properties
- i18n_zh_CN.properties
- i18n_en_US.properties
-
i18n.properties
i18n.username=Username
i18n.password=Password
- i18n_zh_CN.properties
i18n.username=用户名
i18n.password=密码
- i18n_en_US.properties
i18n.username=Username
i18n.password=Password
- 配置国际化资源文件
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"/>
</bean>
- 定义显示页面
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:message key="i18n.username" />
<br>
<fmt:message key="i18n.password" />
view-controller标签
-
如果希望直接访问一个路径,而不通过Handler方法转发到目标路径,就可以子SpringMVC配置文件中配置<view-controller\ > 标签来配置
-
例如:想要直接访问一个"WEB-INFO/pages/i18n_show.jsp"文件,则可以在SpringMVC配置文件中这样配置
<mvc:view-controller path="/i18nShow" view-name="i18n_show"/>
-
但此时原先在@RequestMapping注解中设置的映射路径就会失效
@RequestMapping("/testJstl")
public String testJstl(String uid){
System.out.println(uid);
return "i18n_show";
}
- 如果希望注解中的路径也能够生效,那么需要在SpringMVC中配置如下标签
<mvc:annotation-driven></mvc:annotation-driven>
自定义视图
- 当SpringMVC提供的视图无法满足我们的需求的时候,就可以自定义自己所需要的视图,要想自定义视图,需要实现SpringMVC提供的一个接口"View"
- 实现View接口中的getContentType()和render()方法即可
- 在getContentType()方法中返回视图的MIME文件类型
- 在render()方法中定义视图的渲染操作.
package mao.shu.springmvc.view;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@Component
public class HelloView implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse.getWriter().print("HelloWorld " + new java.util.Date());
}
}
- 要想使用自定义的视图,则需要在SpringMVC的配置文件中,添加一个视图解析器:“BeanNameViewResolver”
<!--配置视图解析器
这个试图解析器根据Bean名称来选择使用的解析器
order 属性设置试图解析器的优先级,默认值为Integer的最大值,数值越小 优先级越高
-->
<bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"/>
</bean>
-
BeanNameViewResolver 类中视图解析流程,主要是用通过Bean的名称在IOC容器中得到自定义的View类的实例化对象,所以自定义的View类也应该配置IOC容器自动注入
-
测试:定义一个请求映射方法
@RequestMapping("/testCusto")
public String testCusto(String uid){
System.out.println(uid);
return "helloView";
}
重定向
- 示例:
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/index.jsp";//跳转到/index.jsp页面中
}
- 在调试运行时会发现在创建视图的时候,会执行以下的代码,当字符串以"redirect"开头时,创建的是一个RedirectView类型的视图
- 当字符串以"forward"开头时创建的时InternalResourceView类型的视图