【SpringMVC】快速入门

三次框架

在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层。

表现层

也就是常说的web层。
它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web需要接收http请求,完成http响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
表现层的设计一般都使用MVC模型。(MVC是表现层的设计模型,和其他层没有关系)

业务层

也就是常说的service层。
它负责业务逻辑处理,和我们开发项目的需求息息相关。
web层依赖业务层,但是业务层不依赖web层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)

持久层

也就是常说的dao层。
负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。
通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。

MVC模型

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。
Model(模型): 通常指的就是我们的数据模型。作用一般情况下用于封装数据。
View(视图): 通常指的就是我们的jsp或者html。作用一般就是展示数据的。 通常视图是依据模型数据创建的。
Controller(控制器): 是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。

SpringMVC是什么

SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于 Spring FrameWork 的后续产品,已经融合在Spring Web Flow里面。

SpringMVC和Struts2的优势分析

共同点:

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

区别:

Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
Spring MVC 是基于方法设计的,而Struts2是基于类,Struts2每次执行都会创建一个动作类。所以Spring MVC 会稍微比 Struts2 快些。 Spring MVC 使用更加简洁,同时还支持 JSR303, 处理 ajax 的请求更方便。
Struts2 的OGNL 表达式使页面的开发效率相比Spring MVC 更高些,但执行效率并没有比JSTL提升,尤其是struts2的表单标签,远没有html执行效率高。

SpringMVC中的部分组件

DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

HandlerMapping:处理器映射器

HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

Handler:处理器

它就是我们开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。

HandlAdapter:处理器适配器

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

View Resolver:视图解析器

View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

View:视图

SpringMVC框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。

入门案例

配置核心控制器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>
  <!-- 配置spring mvc的核心控制器 -->
  <servlet>
    <!--固定的写控制器组件DispatcherServlet,起控制作用,相当于servlet的指挥中心-->
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
    <!-- 配置初始化参数,用于读取SpringMVC的配置文件 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <!--配置文件的位置-->
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    
    <!-- 配置servlet的对象的创建时间点:应用加载时创建。 取值只能是非0正整数,表示启动顺序 -->
    <!--配置为1代表当Tomcat启动的时候DispatcherServlet就会被创建-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <!--用servlet拦截-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!--填写"/"代表拦截全部页面-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

创建SpringMVC的配置文件springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

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

    <!--配置视图解析器,视图解析器对象,id和class都是固定不变的,它可以帮助我们跳转到指定页面-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--prefix指定跳转到文件的位置-->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!--suffix指定跳转到的文件的后缀名-->
        <property name="suffix" value=".jsp"></property>
    </bean>

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

</beans>

编写控制器并使用注解配置

@Controller
public class HelloController {
    //@RequestMapping请求映射标签,当该类被调用的时候会执行这个方法
    //path属性指定方法的请求路径
    @RequestMapping(path="/hello")
    public String sayHello(){
        System.out.println("Hello StringMVC");
        //默认表示为跳转到名称为success.jsp的页面
        return "success";
    }
}

SpringMVC的请求响应流程

在这里插入图片描述

@RequestMapping注解的作用

源码:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

作用:用于建立请求URL和处理请求方法之间的对应关系。
出现的位置:该注解可以写在类上也可以写在方法上。
出现在类上:
请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。 它出现的目的是为了使我们的URL可以按照模块化管理:

例如: 
		账户模块: 
				/account/add 
         		/account/update 
         		/account/delete ... 
         订单模块: 
         		/order/add
           		/order/update
            	/order/delete

上面例子中 /account 和 /order 可以使用 @RequestMapping 填写在类上, 而 /add 等这些作为二级目录写在方法上

方法上: 请求URL的第二级访问目录。

注解的属性:

  • value:用于指定请求的URL。它和path属性的作用是一样的。
  • method:用于指定请求的方式(选择GET,POST,PUT,PATCH等方式)。
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样。
    例如: params = {“accountName”},表示请求参数必须有accountName params = {“moeny!100”},表示请求参数中money不能是100。
  • headers:用于指定限制请求消息头的条件。

请求参数的绑定

  1. 请求参数的绑定说明
    1. 绑定机制
      1. 表单提交的数据都是key=value格式的 username=haha&password=123
      2. SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的
      3. 要求:提交表单的name和参数的名称是相同的
  2. 支持的数据类型
    1. 基本数据类型和字符串类型
    2. 实体类型(JavaBean)
    3. 集合数据类型(List、map集合等)

零散接收参数-案例

编写一个简单的jsp页面,超链接中传递两个参数:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求参数绑定</title>
</head>
<body>
    <a href="param/testParam?username=hehe&password=12345">请求参数绑定</a>
</body>
</html>

参数将会进入@RequestMapping指定的控制类的方法中:

@Controller
@RequestMapping("/param")
public class ParamController {
    //请求参数绑定入门
    @RequestMapping("/testParam")
    public String testParam(String username, String password) {
        System.out.println("testParam执行了。。");
        System.out.println("账户:" + username + ",密码:" + password);
        return "success";
    }
}

当访问该jsp页面并点击该链接后就会调用testParam方法,在控制台输出:
账户:hehe,密码:1234

用类封装参数-案例

当时如果传入的参数比较多,就应该把参数封装到一个类中:
新建一个实体类Account并添加get,set和toString方法:

public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    //省去get,set和toString方法
}

创建一个表单,并且地址指向一个方法:

    <!--创建一个表单-->
    <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>

在测试类中创建这个方法,直接在方法的形参中输入这个类,就可以直接封装进去:

@Controller
@RequestMapping("/param")
public class ParamController {
    //请求参数封装到类中
    @RequestMapping("/saveAccount")
    public String saveAccount(Account account) {
        System.out.println("testParam执行了。。");
        System.out.println(account);
        return "success";
    }
}

输入:
输入页面
控制台输出:
在这里插入图片描述

如果Account 类中包含另一个类的实例。应该怎么封装呢?

User类:

public class User implements Serializable {
    private String uname;
    private Integer age;
}

Account :

public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    //新增
    private User user;

那么在创建表单的时候可以这样实现:

    <!--创建一个表单-->
    <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="text" name="user.uname"/><br>
        用户年龄:<input type="text" name="user.age"/><br>
        <input type="submit" value="提交"><br>
    </form>

解决中文乱码问题:在过滤器中实现:

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类中还包含了List与Map集合应该怎么封装?

public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    //新增
    private User user;
    //新增
    List<User> users;
}

jsp中的表单:

    <!--创建一个表单,类中存在List、Map集合-->
    <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="text" name="user.uname"/><br>
        用户年龄:<input type="text" name="user.age"/><br>

        <%--封装到list中的一个User对象里--%>
        路人姓名0<input type="text" name="users[0].uname"/><br>
        路人年龄0<input type="text" name="users[0].age"/><br>
        <%--封装到list中的一个User对象里--%>
        路人姓名1<input type="text" name="users[1].uname"/><br>
        路人年龄1<input type="text" name="users[1].age"/><br>
        <%--封装到map中的一个User对象里,键设置为‘key2’--%>
        路人姓名2<input type="text" name="map['key2'].uname"/><br>
        路人年龄2<input type="text" name="map['key2'].age"/><br>

        <input type="submit" value="提交"><br>
    </form>

如此就可以实现集合的封装。
输入:
在这里插入图片描述
控制台输出;
testParam执行了。。
Account{username=‘张张’, password=‘123456’, money=200.0, user=User{uname=‘打哈’, age=22}, users=[User{uname=‘甲’, age=21}, User{uname=‘乙’, age=25}], map={key2=User{uname=‘丙’, age=30}}}

自定义类型转换器

在User中添加一个生日:

public class User implements Serializable {
    private String uname;
    private Integer age;
    private Date date;
}

创建表单:

    <!--创建一个表单-->
    <form action="param/saveUser" method="post">
        姓名:<input type="text" name="uname"/><br>
        年龄:<input type="text" name="age"/><br>
        生日:<input type="text" name="date"/><br>
        <input type="submit" value="提交"><br>
    </form>

处理提交的数据:

    //自定义类型转换器
    @RequestMapping("/saveUser")
    public String saveUser(User user) {
        System.out.println("saveUser执行了。。");
        System.out.println(user);
        return "success";
    }

输入:
在这里插入图片描述
此时控制台会打印:
saveUser执行了。。
User{uname=‘张张’, age=123456, date=Sun Feb 02 00:00:00 CST 2020}
但是如果想把Date类型的输入进行修改:
在这里插入图片描述
访问就会报错:
在这里插入图片描述
此时我们需要自定义类型转换器:

/**
 * 字符串转换成日期
 * @author liwenlong
 * @data 2020/5/21
 */
public class StringToDate 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 (ParseException e) {
            throw new RuntimeException("错误");
        }
    }
}

在springmvc.xml文件中注册类型转换器:

    <!--配置自定义类型转换器-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!--注册类型转化器-->
        <property name="converters">
            <set>
                <bean class="com.lwl.util.StringToDate"></bean>
            </set>
        </property>
    </bean>
    
    <!--开启SpringMVC框架注解的支持-->
    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

此时输入2020-02-02就不会再报错,但是原来的2020/02/02就不能用了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章