学习笔记【SSM-第四节:SpringMVC框架】

SpringMVC

服务器端分为三层架构:表现层、业务层、持久层。

SpringMVC是表现层的框架。

MVC设计模型:

  • model模型
  • view视图
  • controller控制器

SpringMVC和Struts2的优劣区别:

共同点:

  • 他们是表现层框架,都是基于MVC模型写的
  • 底层都离不开原始ServletAPI
  • 处理请求的机制都是一个核心控制器

区别:

  • Spring MVC的入口是Servlet,而Struts2是Filter
  • SpringMVC是基于方法设计的,而Struts2是基于类,Struts2每次执行都会创建一个动作类。所以SpringMVC会比Struts2稍微快一些。
  • SpringMVC使用更加简洁,同时支持JSR303,处理ajax的请求更方便

快速入门

  • 创建前端控制器DispatcherServlet
  • 加载springmvc.xml配置文件
  • xml中创建了视图解析器对象

其中涉及注释:

  • @Controller
  • @RequestMapping用于建立请求URL和处理请求方法之间的对应关系
    • 属性path:响应请求,也可以用value,当只有这一个时可不写
    • 属性method:可选择请求方式,底层是一个枚举
    • 属性params:可以指定必须传入值的属性(可加值)
    • 属性headers:用于指定限制请求消息头的条件

涉及的标签:
<mvc:annotation-driven/>:开启SpringMVC框架注解的支持

web.xml:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <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:springmvc.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-->
  </servlet-mapping>
</web-app>

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

    <!--开启注解扫描-->
    <context:component-scan base-package="cn.huangyy"></context:component-scan>


    <!--视图解析器对象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property><!--前缀-->
        <property name="suffix" value=".jsp"></property><!--扩展名-->
    </bean>

    <!--开启SpringMVC框架注解的支持-->
    <mvc:annotation-driven/>
</beans>
详细图解:

在这里插入图片描述

在springMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。
使用<mvc:annotation-driven/>可自动加载RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器)。

请求参数的绑定

绑定机制:

  • 表单提交的数据都是k=v格式的
  • SpringMVC的参数绑定过程是把表单提交的参数,作为控制器中方法的参数进行绑定的
  • 要求:提交表单的name和参数名称是相同的

支持的数据类型:

  • 基本数据类型和字符串类型
  • 实体类型(JavaBean)
  • 集合类型(List、Map集合等)

基本数据类型和字符串类型

  • 提交表单的name和参数名称是相同的
  • 区分大小写
<a href="param/testParam?username=hehe&password=1234">请求参数绑定</a>
@RequestMapping("/testParam")
public String testParam(String username,String password){
    System.out.println("输出了!");
    System.out.println("用户名:"+username);
    System.out.println("密码:"+password);
    return "success";
}

实体类型

  • 提交表单的name和JavaBean中的属性名称需要一致
  • 如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性 如:address.name
<form action="param/saveAccount" method="post">
    用户名:<input type="text" name="username" ><br>
    密码:<input type="text" name="password" ><br>
    金额:<input type="text" name="money" ><br>
    <input type="submit" value="提交"><br>
</form>
@RequestMapping("/saveAccount")
public String testSaveAccount(Account account){
    System.out.println("输出了!");
    System.out.println(account);
    return "success";
}

若有引用,则可:

姓名:<input type="text" name="user.uname" ><br>
年龄:<input type="text" name="user.age" ><br>

乱码过滤器的配置:
在web.xml中:

<filter>
  <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

集合类型
若account中有以下两个类型

private List<User> list;
private Map<String,User> map;
姓名:<input type="text" name="list[0].uname" ><br>
年龄:<input type="text" name="list[0].age" ><br>

姓名:<input type="text" name="map['one'].uname" ><br>
年龄:<input type="text" name="map['one'].age" ><br>

自定义类型转换器
MVC内部有进行数据类型转换,但并不是所有都能转

如自定义一个String转Date
写一个类继承Converter:

public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        if (s==null){
            throw new RuntimeException("传入参数为空!");
        }
        DateFormat df=new SimpleDateFormat("yyyy-MM-dd");

        try {
            return df.parse(s);
        } catch (Exception e) {
            throw new RuntimeException("数据类型转换出现异常!");
        }
    }
}

在springmvc.xml配置文件中配置:

<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="cn.huangyy.utils.StringToDateConverter"/>
        </set>
    </property>
</bean>

<!--开启SpringMVC框架注解的支持-->
<mvc:annotation-driven conversion-service="conversionService"/>

获取Servlet原生的API
只需要在参数中写入HttpServletRequest和HttpServletResponse即可

常用注解:

@RequestParam
把请求中制定名称的参数给控制器中的形参赋值。
属性

  • value:请求参数中的名称。也是name属性
  • required:默认为true,则必须传一个
<a href="anno/testRequestParam?name=haha">RequestParam</a>
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam("name") String username){
    System.out.println("完成了");
    System.out.println(username);
    return "success";
}

@RequestBody
用于获取请求体的内容,直接使用得到的是k=v&k=v这样结构的数据,get请求方式不适用

@RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("完成了");
        System.out.println(body);
        return "success";
    }

@PathVaribale
用于绑定url占位符。例如url中/delete/{id},这个{id}就是url占位符
属性:

  • value:用于指定url占位符名称
  • required:是否必须提供占位符
@RequestMapping("/testPathVaribale/{sid}")
public String testPathVaribale(@PathVariable(value = "sid") String id){
    System.out.println("完成了");
    System.out.println(id);
    return "success";
}
<a href="anno/testPathVaribale/10">testPathVaribale</a>

@RequestHeader
用于获取请求消息头。

@RequestMapping("/testRequestHeader")
public String testRequestHeader (@RequestHeader("Accept") String header){
    System.out.println("完成了");
    System.out.println(header);
    return "success";
}

@CookieValue
用于把制定cookie名称的值传入控制器方法参数

@RequestMapping("/testCookieValue")
public String testCookieValue (@CookieValue("JSESSIONID") String cookieValue){
    System.out.println("完成了");
    System.out.println(cookieValue);
    return "success";
}

@ModelAttribute

  • 出现在方法上,表示当前方法会在控制器的方法执行之前先执行。他可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
  • 出现在参数上,获取制定的数据给参数赋值。

属性value:用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key
出现在方法上:

 @RequestMapping("/testModelAttribute")
 public String testModelAttribute (){
     System.out.println("testModelAttribute方法执行了");
     return "success";
 }

 @ModelAttribute
 public void showUser(){
     System.out.println("showUser方法执行了");
 }

在这里插入图片描述

可以用在提前用其中的属性查询数据库,如有名字年龄生日,可拿名字先查,当要用到时,倘若没传,则用数据库中查询到的

@RequestMapping("/testModelAttribute")
public String testModelAttribute (User user){
    System.out.println("testModelAttribute方法执行了");
    System.out.println(user);
    return "success";
}

@ModelAttribute
public User showUser(String uname){
    System.out.println("showUser方法执行了");
    //通过名字查询数据库(模拟)
    User user=new User();
    user.setUname(uname);
    user.setAge(20);
    user.setDate(new Date());
    return user;
}

在这里插入图片描述
属性上的用法,倘若方法没有返回值,则:

@RequestMapping("/testModelAttribute")
public String testModelAttribute (@ModelAttribute("abc") User user){
    System.out.println("testModelAttribute方法执行了");
    System.out.println(user);
    return "success";
}

@ModelAttribute
public void showUser(String uname, Map<String,User> map){
    System.out.println("showUser方法执行了");
    //通过名字查询数据库(模拟)
    User user=new User();
    user.setUname(uname);
    user.setAge(20);
    user.setDate(new Date());
    map.put("abc",user);
}

在这里插入图片描述

@SessionAttribute
用于多次执行控制器方法间的参数共享。

@RequestMapping("/testSessionAttribute")
public String testSessionAttribute (Model model){
    System.out.println("testSessionAttribute完成了");
    //底层会存入request域中
    model.addAttribute("msg","帅帅");
    return "success";
}

在类上加@SessionAttributes会存入session中,传入的参数是一个数组

@SessionAttributes("msg")
public class AnnoController {
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>入门成功</h3>
    ${sessionScope}
</body>
</html>

在这里插入图片描述
额外:ModelMap的get可取值,SessionStatus的setComplete可删除

响应后的返回值

返回值为String
@RequestMapping("/testString")
public String testString(Model model){
    System.out.println("testString执行了。。");
    User user=new User();
    user.setUsername("zhangsan");
    user.setPassword("123456");
    user.setAge(23);

    model.addAttribute("user",user);
    return "success";
}

可在jsp中用EL表达式获取

返回值为void
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("testVoid执行了。。");
    response.setCharacterEncoding("UTF-8");

    /*转发:request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);*/
    /*重定向:response.sendRedirect(request.getContextPath()+"/index.jsp");*/

    response.setContentType("text/html;charset=UTF-8");
    response.getWriter().print("hello");
    return;
}

返回值为ModelAndView

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    //创建ModelAndView对象
    ModelAndView mv=new ModelAndView();
    System.out.println("testModelAndView执行了。。");
    User user=new User();
    user.setUsername("zhangsan");
    user.setPassword("123456");
    user.setAge(23);
    //把user对象传入mv对象中,也会把user对象存入到request域中
    mv.addObject("user",user);
    //跳转到哪个界面
    mv.setViewName("success");
    return mv;
}
使用forward和redirect进行页面跳转
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect(){
    System.out.println("testForwardOrRedirect执行了。。");

    /*转发:return "forward:/WEB-INF/pages/success.jsp";*/
    return "redirect:/index.jsp";//重定向
}
响应json数据过滤静态资源

需在springmvc.xml中配置:

<!--告诉前端控制器,以后哪些静态资源不拦截,一般是js、图片、css-->
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/images/**" location="/image/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<script src="js/jquery-3.5.1.min.js"></script>
<script>
    $(function (){
        $("#btn").click(function (){
            $.ajax({
                url:"user/testJson",
                contentType:"application/json;charset=UTF-8",
                data:'{"username":"hehe","password":"123","age":30}',
                dataType:"json",
                type:"post",
                success:function (data){
                }
            });
        });
    });
</script>
@RequestMapping("/testJson")
public void testJson(@RequestBody String body){
    System.out.println("testJson执行了。。");
    System.out.println(body);
}

封装到对象当中

@RequestMapping("/testJson")
public @ResponseBody User testJson(@RequestBody User user){
    System.out.println("testJson执行了。。");
    System.out.println(user);
    //模拟查数据库
    user.setUsername("haha");
    user.setAge(23);
    //做响应
    return user;
}
success:function (data){
  /*可接受到响应回来的数据*/
  alert(data);
  alert(data.username);
  alert(data.age)
}

文件上传

要求:

  • form表单的enctype(表单请求正文的类型)取值必须是:multipart/form-data
  • method属性的取值必须是Post
  • 提供一个文件选择域

需使用Commons-fileupload组件实现文件上传,需导入fileupload和io的jar包。

<form action="user/fileupload1" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="upload"/><br/>
    <input type="submit" value="上传"/>
</form>
@RequestMapping("/fileupload1")
public String fileupload1(HttpServletRequest request) throws Exception {
    System.out.println("文件上传...");
    //使用fileupload组件完成文件上传
    //上传的位置
    String path = request.getSession().getServletContext().getRealPath("/uploads/");
    //判断该路径是否存在
    File file=new File(path);
    if (!file.exists()){
        file.mkdirs();
    }

    //解析request对象,获取上传文件项
    DiskFileItemFactory factory=new DiskFileItemFactory();
    ServletFileUpload upload=new ServletFileUpload(factory);
    upload.setHeaderEncoding("UTF-8");
    //解析request
    List<FileItem> items = upload.parseRequest(request);
    for (FileItem item : items) {
        //进行判断,当前item对象是否是上传文件项
        if (item.isFormField()){
            //说明是普通表单项

        }else {
            //是上传文件项
            String filename = item.getName();
            //改唯一名称
            String uuid = UUID.randomUUID().toString().replace("-", "");
            filename=uuid+"_"+filename;
            item.write(new File(path,filename));
            item.delete();
        }
    }
    return "success";
}

SpringMVC的文件上传

springMVC框架文件上传的原理:
前端控制器接收到request后会传到配置文件解析器中,解析器会进行解析返回一个upload,前端控制器会将upload传到一个方法中,参数为MultipartFile upload,这个upload必须和input标签中name的值一致

在springmvc中配置文件解析器:

<!--配置文件解析器对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--文件最大大小-->
    <property name="maxUploadSize" value="10485760">
    <property name="defaultEncoding" value="UTF-8"></property></property>
</bean>

方法的改动:

@RequestMapping("/fileupload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
    System.out.println("文件上传...");
    //上传的位置
    String path = request.getSession().getServletContext().getRealPath("/uploads/");
    //判断该路径是否存在
    File file=new File(path);
    if (!file.exists()){
        file.mkdirs();
    }
	//获取文件上传的名称
    String filename = upload.getOriginalFilename();
    //改唯一名称
    String uuid = UUID.randomUUID().toString().replace("-", "");
    filename=uuid+"_"+filename;
    //完成文件上传
    upload.transferTo(new File(path,filename));

    return "success";
}

跨服务器的文件上传

会先将图片上传至应用服务器,服务器再传到图片服务器,当需要图片时,会直接去图片服务器找。

需要用到jersey的core和client jar包
需要创建一个新的服务器,要改端口号

@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
    System.out.println("文件上传...");
    String path="http://localhost:9090/ch04_upload/";
    //获取上传文件的名称
    String filename = upload.getOriginalFilename();
    String encodingFilename= URLEncoder.encode(filename,"utf-8");
    //改唯一名称
    String uuid = UUID.randomUUID().toString().replace("-", "");
    filename=uuid+"_"+filename;

    //创建客户端对象
    Client client = Client.create();
    //和图片服务器进行连接
    WebResource webResource = client.resource(path + encodingFilename);
    //上传文件
    webResource.put(upload.getBytes());

    return "success";
}

SpringMVC异常处理

配置一个异常处理器处理异常

  • 编写一个自定义异常的类(做提示信息)
  • 编写异常处理器
  • 配置异常处理器(跳转到提示页面)

自定义异常类:

public class SysException extends Exception{
    private String message;

    public SysException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

编写异常处理器

public class SysExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        //获取异常对象
        SysException ex=null;
        if (e instanceof SysException){
            ex= (SysException) e;
        }else {
            ex=new SysException("系统正在维护。。。");
        }
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.addObject("errorMsg",ex.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

配置异常处理器

<bean id="sysExceptionResolver" class="cn.huangyy.exception.SysExceptionResolver"/>

拦截器

过滤器和拦截器的区别

  • 过滤器是servvlet规范中的一部分,任何javaweb工程都可以使用
  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 过滤器在url-pattern中配置/*之后又,会对所有要访问的资源拦截
  • 拦截器只会拦截访问的控制器方法,如果访问的是jsp、html、css、image或者js是不会进行拦截的

自定义拦截器,要求必须实现:HandlerInterceptor接口

步骤:

  • 编写拦截器类,实现HandlerInterceptor接口
  • 配置拦截器
public class MyInterceptor1 implements HandlerInterceptor {

    //预处理,controller方法执行前
    //return true 放行,执行下一个拦截器,如果没有,执行方法
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器执行了...前");
        return true;
    }

    //后处理方法
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器执行了...后");
    }

    //success页面执行后,该方法会执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器执行了...最终");
    }
}
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--要拦截的具体方法-->
        <mvc:mapping path="/user/*"/>
        <!--不拦截的方法-->
        <!--<mvc:exclude-mapping path=""/>-->
        <!--配置拦截器对象-->
        <bean class="cn.huangyy.interceptor.MyInterceptor1"/>
    </mvc:interceptor>
</mvc:interceptors>

在这里插入图片描述
再配一个也一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值