zl程序教程

您现在的位置是:首页 >  其他

当前栏目

SpringMVC入门第二部分

2023-03-14 22:55:21 时间

SpringMVC入门第二部分

数据带给页面

方法1: 在方法上传入原生的request和session

userController 类:

@Controller
public class userController {
    @RequestMapping("/hello")
    public String show(HttpSession session, HttpServletRequest httpServlet)
    {
         session.setAttribute("msg1","你好session");
         httpServlet.setAttribute("msg2","你好erquest");
        return "Success";
    }
}

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
    <title>Success</title>
</head>
<body>
request域: ${requestScope.msg2}
session域: ${sessionScope.msg1}
</body>
</html>

方法2: 在方法里面传入map,model或者modelMap—给request域中带数据

Map方式:

@Controller
public class userController {
    @RequestMapping("/hello")
    public String show(Map<String,Object> map)
    {
       map.put("msg","你好");
        return "Success";
    }
}

model方式:

@Controller
public class userController {
    @RequestMapping("/hello")
    public String show(Model model)
    {
         model.addAttribute("msg","你好");
        return "Success";
    }
}

modelMap方式:

@Controller
public class userController {
    @RequestMapping("/hello")
    public String show(ModelMap model)
    {
         model.addAttribute("msg","你好");
        return "Success";
    }
}

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
    <title>Success</title>
</head>
<body>
request域: ${requestScope.msg}
</body>
</html>

map,modelmap,model继承关系图

给这些参数里面保存的所有数据最终都会放在请求域中,可以在页面获取

Map,model,modelMap最终都是BindingAwareModelMap在工作,相当于给BindingAwareModelMap中保存的数据都会被放在请求域中


方法返回值为ModelAndView

     @RequestMapping("/dhy")
    public ModelAndView test02()
     {
         //之前的返回值我们就叫视图名:视图名视图解析器会帮我们最终拼串得到页面的真实地址
         //ModelAndView mv=new ModelAndView("Success");
         ModelAndView mv=new ModelAndView();
         mv.setViewName("Success");
          mv.addObject("msg","你好");
         return mv;
     }

给session域中带数据。SpringMVC中提供了一种临时给Session域中保存数据的方式

使用一个注解@SessionAttributes(只能标注在类上)

//可以使用数组形式,key是msg,msg1,msg2的都会放入session域中
@SessionAttributes(value = {"msg","msg1","msg2"})
@Controller
public class userController {
    @RequestMapping("/hello")
    public String show(HttpSession session, HttpServletRequest httpServlet)
    {
         session.setAttribute("msg1","你好session");
         httpServlet.setAttribute("msg2","你好erquest");
        return "Success";
    }
    @RequestMapping("/dhy")
    public ModelAndView test02()
     {
         //之前的返回值我们就叫视图名:视图名视图解析器会帮我们最终拼串得到页面的真实地址
         //ModelAndView mv=new ModelAndView("Success");
         ModelAndView mv=new ModelAndView();
         mv.setViewName("Success");
          mv.addObject("msg","你好");
         return mv;
     }
}
@SessionAttributes(types = {String.class,Integer.class})
@Controller
public class userController {
    @RequestMapping("/hello")
    public String show(HttpSession session, HttpServletRequest httpServlet)
    {
         session.setAttribute("msg1","你好session");
         httpServlet.setAttribute("msg2","你好erquest");
        return "Success";
    }
    @RequestMapping("/dhy")
    public ModelAndView test02()
     {
         //之前的返回值我们就叫视图名:视图名视图解析器会帮我们最终拼串得到页面的真实地址
         //ModelAndView mv=new ModelAndView("Success");
         ModelAndView mv=new ModelAndView();
         mv.setViewName("Success");
          mv.addObject("msg","你好");
         return mv;
     }
}

后来推荐@SessionAttributes就别用了,可能会引发异常,给session中存放数据建议使用原生API


@ModelAttribute注解

问题演示

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>小朋友</title>
</head>
<body>

<form action="book" method="post">
    价格:<input type="text" name="price"/><br>
    <input type="submit" value="提交">
</form>
</body>
</html>
@Controller
public class BookController {

     @RequestMapping("/book")
    public String ShowBook(Book book)
    {
        System.out.println(book);
        return "Success";
    }
}

这里实际上是springMVC创建了一个Book对象,然后将对应的属性通过set方法赋值给该对象

如果我们要完成的操作是对数据库中保存的一条图书记录进行全更新,那么使用这种方法时,就会有问题

例如:我们在页面对图书进行修改时,无法修改图书的名字,因此new出来的Book对象的name值一定为null,如果把该对象放入数据库中,那么book的名字就会变成null,覆盖掉了原本图书的名字


解决方法----@ModelAttribute的使用方式之一

如果是使用从数据库中拿出来的准备好的对象来封装请求参数,那么如果请求参数中不包括对象的全部变量值,那么剩余变量的值就依旧采用数据库中拿出来的对象自身的值,这样就不会造成为null的现象

@Controller
public class BookController {

     @RequestMapping("/book")
     //告诉SpringMVC不要new这个book了,而是使用我刚才保存的book
     //即用我刚才从数据库中查找出来的book
    public String ShowBook(@ModelAttribute("book") Book book)
    {
        System.out.println(book);
        return "Success";
    }


@ModelAttribute
    void BookFromDao(Map<String,Object> map)
    {
    //模拟从数据库查出来的book
         Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        map.put("book",book);
    }
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>小朋友</title>
</head>
<body>

<form action="book" method="post">
    价格:<input type="text" name="price"/><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

如果由springmvc来New一个新的book对象,那么这里的name就为null


@ModelAttribute的原理


@ModelAttribute标注在方法上面的另外一个作用


SpringMVC确定POJO自定义对象值的三步

自定义类型对象的赋值流程描述,以及@SessionAttributes注解可能引发的异常

给自定义类型赋值,主要在于attrName的值,首先会把attrName作为key值去隐含模型中寻找key==attrName的键,然后将key对应的值进行赋值


举例1:在给Book book对象赋值前,不加@ModelAttribute注解,那么会将参数类型首字母小写,即Book—>book,去隐含模型中寻找对应的key值

@Controller
public class BookController {

     @RequestMapping("/book")
    public String ShowBook(Book book,Map<String,Object> map)
    {
        System.out.println(book);
        return "Success";
    }
@ModelAttribute
    void BookFromDao(Map<String,Object> map)
    {
        Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        map.put("book",book);//放入隐含模型中,key==book
    }
}

举例2:通过@ModelAttribute(“haha”)来指定attrName的值

@Controller
public class BookController {

     @RequestMapping("/book")
     //告诉SpringMVC不要new这个book了,而是使用我刚才保存的book
     //即用我刚才从数据库中查找出来的book
    public String ShowBook(@ModelAttribute("haha") Book book,Map<String,Object> map)
    {
        System.out.println(book);
        return "Success";
    }


@ModelAttribute
    void BookFromDao(Map<String,Object> map)
    {
        Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        map.put("haha",book);//放入隐含模型中,key==haha
    }
}

举例3:attrName=haha,隐含模型中只有key==book的值,那么会去session域中寻找,@SessionAttributes(value=“haha”),但是session中没有键为haha的值,这样会报异常

@SessionAttributes("haha")
@Controller
public class BookController {

     @RequestMapping("/book")
     //告诉SpringMVC不要new这个book了,而是使用我刚才保存的book
     //即用我刚才从数据库中查找出来的book
    public String ShowBook(@ModelAttribute("haha") Book book,Map<String,Object> map)
    {
        System.out.println(book);
        return "Success";
    }

@ModelAttribute
    void BookFromDao(Map<String,Object> map)
    {
        Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        map.put("book",book);//放入隐含模型中,key==haha
    }
}

举例5:@ModelAttribute会把方法运行后的返回值也放入隐含模型中,key就是返回值类型小写

@Controller
public class BookController {
     @RequestMapping("/book")
    public String ShowBook(Book book)
    {
        System.out.println(book);
        return "Success";
    }
@ModelAttribute
    Book BookFromDao()
    {
        Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        return book;
    }
}

举例6:把返回值放入隐含模型,但是不使用默认的返回值小写作为key,而是自己指定

@Controller
public class BookController {
     @RequestMapping("/book")
    public String ShowBook(@ModelAttribute("haha") Book book)
    {
        System.out.println(book);
        return "Success";
    }
@ModelAttribute("haha")
    Book BookFromDao()
    {
        Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        return book;
    }
}


举例7: 隐含模型中找不到对应的key,session中也找不到,那么会自动创建一个新对象

@Controller
public class BookController {
     @RequestMapping("/book")
    public String ShowBook(@ModelAttribute("haha") Book book)
    {
        System.out.println(book);
        return "Success";
    }
@ModelAttribute
    Book BookFromDao()
    {
        Book book=new Book("大忽悠自传",19);
        System.out.println("数据库中查到的图书信息:"+book);
        return book;
    }
}

@ModelAttribute的总结

@SessionAttributes的总结

第二点是如果隐含模型中没有,session中就必须有,不然抛出异常


视图和视图解析器再分析

有前缀的返回值独立解析

注意:forward转发到一个页面, /Fail.jsp转发到当前项目下的Fail.jsp. 一定要加上/,如果不加上/就是相对路径,容易出问题

forward前缀的转发,不会由我们配置的视图解析器进行拼串

@SessionAttributes(types = {String.class,Integer.class})
@Controller
public class userController {
    @RequestMapping("/hello")
    public String show()
    {
        return "forward:/Fail.jsp";
    }
}

Fail.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>失败</title>
</head>
<body>
<h1><font color="red">Lose!!!</font></h1>
</body>
</html>

多次派发

@Controller
public class userController {
    @RequestMapping("/hello01")
    public String show()
    {
        return "forward:/Fail.jsp";
    }
@RequestMapping("/hello02")
    public String show1()
    {
        return "forward:/hello01";
    }
}

请求转发地址使用相对路径


Redirect指定重定向到页面

多次重定向

@Controller
public class userController {
    @RequestMapping("/hello01")
    public String show()
    {
        return "redirect:/Fail.jsp";
    }
@RequestMapping("/hello02")
    public String show1()
    {
        return "redirect:/hello01";
    }
}

注意:有前缀的转发和重定向操作,配置的视图解析器就不会进行拼串


视图解析器流程简单一句话总结原理:


jstlView支持便捷的国际化功能—页面中英文切换

1.导入jstl的依赖和standard的依赖

    <!--jstl的依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!--standard的依赖-->
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

2.导包如果导入了jstl,会自动创建一个jstlView,可以快速方便的支持国际化功能

也可以通过在springMVC.xml文件中配置,指定其创建jstlView

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
        <!--指定创建jstlView-->
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    </bean>

回顾javaweb国际化步骤

国际化资源文件的命名规范如下:


步骤1: 让spring管理国际化资源

步骤2:直接去页面使用fmt:message

新建两个资源文件:i18n_cn_CN.properties 和 i18n_un_US.properties

title=First War Of The World
zhangHao=LoginNumber
password=Password
loginBtn=Submit
title=战地1
zhangHao=账号
password=密码
loginBtn=提交

在springmvc配置文件中加入bean

springMVC.xml:

    <mvc:annotation-driven />
    <!--context的组件扫描,需要引入context命名空间-->
    <!--只需要扫描cotroller包下的内容-->
    <context:component-scan base-package="com.SpringMvc"/>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
        <!--指定创建jstlView-->
      <!--  <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>-->
    </bean>

    <!--让SpringMVC管理国际化资源文件-->
<bean id="messageSource"  class="org.springframework.context.support.ResourceBundleMessageSource">
    <!--basename:指定国际化资源文件的基础名-->
    <property name="basename" value="i18n"></property>
    <!-- 支持UTF-8的中文 -->
    <property name="defaultEncoding" value="UTF-8"/>

在jsp中加上标签以及替换相关需要使用的字符

login.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
   <fmt:message key="title"/>
</head>
<body>
<form action="">
    <fmt:message key="zhangHao"/>: <input type="text"><br/>
    <fmt:message key="password"/> :<input type="password"><br/>
    <input type="submit" value='<fmt:message key="loginBtn"/>'><br/>
</form>
</body>
</html>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>跳转登录页面</title>
</head>
<body>
<a href="toLoginPage" >跳转到登录页面</a>
</body>
</html>

国际化具体实现可以看下面这篇文章,链接在下面

SpringMVC国际化实现(乱码解决)idea


.jsp页面一定要过SpringMVC的视图解析流程,才会创建一个jstlView来帮你快速国际化

不能写forward等前缀,因为代理前缀,创建的view不带国际化功能

只有默认创建的view才有支持国际化功能


发送一个请求,来到一个页面

SpringMVC.xml :

 <!--
   发送一个请求("toLoginPage"),直接来到web-inf下的login页面,需要引入mvc命名空间

   path=""; 指定哪个请求
   view-name; 指定映射给哪个视图
   走了SpringMVC的整个流程:视图解析....
   其他的请求就不好使了,需要开启注解驱动模式
 -->
<mvc:view-controller path="/toLoginPage" view-name="login"/>
    <!--开启mvc的注解驱动模式-->
    <mvc:annotation-driven/>

上面这段代码,可以替代上面的那个类完成的功能,但是还需要加上注解驱动模式,不然其他依赖注解完成请求映射调整的就不好使了

开启mvc注解驱动模式,需要导入mvc的命名空间


自定义视图和自定义视图解析器

1.编写自定义的视图解析器和视图实现类

自定义视图解析器需要实现视图解析器接口和Order接口,Order接口是为了让自定义视图解析器先运行,否则会由默认视图解析器拦截,进行拼串

视图解析器类:

//自定义视图解析器
public class DhyViewResolver implements ViewResolver, Ordered {
    private int order=0;
    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
            //根据视图名返回视图对象
if(viewName.startsWith("dhy:"))
{
    //返回我们的一个视图解析器对象
    return new MyView();
}
else
{
    //该视图解析器解析不了,就返回null,让下一个视图解析器尝试去解析
    return null;
}
    }
    public int getOrder() {
        return order;
    }
//改变视图解析器的优先级
    public void setOrder(int order) {
        this.order = order;
    }
}

视图实现类:

//自定义视图对象
public class MyView implements View {
    //返回的内容数据类型
    @Override
    public String getContentType() {
        return "text/html";
    }

    @Override
    public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        System.out.println("之前保存的数据:"+map);
         //设置响应的内容类型
        httpServletResponse.setContentType("text/html");
        List<String> video = (List<String>) map.get("video");
        for(String s:video)
        {
            httpServletResponse.getWriter().write(s+"<br/>");
        }
        List<String> img = (List<String>) map.get("img");
        for(String s:img)
        {
            httpServletResponse.getWriter().write(s+"<br/>");
        }
        httpServletResponse.getWriter().write("<h1>精彩内容马上上映.....<h1>");
    }
}

2.视图解析器必须放置IOC容器中

<!--自定义的视图解析器-->
    <bean class="com.view.DhyViewResolver" >
        <!-- 视图解析器实现了Orderd接口,自定义视图解析器的执行优先级,数字越小优先级越高
                         默认的优先级是最低的。
                 InternalResourceViewResolver的优先级是也是最低的
          -->
<property name="order" value="1"></property>
    </bean>

3.测试类

MyViewResovlerController类:

/*自定义视图解析器和视图对象*/
@Controller
public class MyViewResovlerController
{
    @RequestMapping("/handle")
         public String handle(Model model)
         {
             List<String> VName = new ArrayList<>();
             List<String> imgName = new ArrayList<>();
             VName.add("大忽悠");
             VName.add("小朋友");
             imgName.add("大忽悠和小朋友");
             model.addAttribute("video",VName);
             model.addAttribute("img",imgName);
             //以dhy:前缀访问的才能跳转到picture页面
             return "dhy:/WEB-INF/pages/picture.jsp";
         }
}

3.目录结构

注意

在进行视图解析时,我们发现会遍历视图解析器列表,前面的视图解析器先被用来解析视图,如果视图不为空,那么后面的解析器就不在被使用,所以配制视图解析器时有个order参数,order越小,越先被遍历。其中InternalResourceViewResolver的order默认为最大值,所以它是最后一个被使用的解析器