Administrator
发布于 2022-10-10 / 43 阅读
0
0

SpringMVC与helloworld

概述

1. Spring 为展现层提供的基于 MVC 设计理念的优秀的 Web 框架,是目前最主流的 MVC 框架之一。

2. Spring3.0 后全面超越 Struts2,成为最优秀的 MVC 框架。

3. Spring MVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。

4. 支持 REST 风格的 URL 请求。

5. 采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。

SpringMVC是一种轻量级的、基于MVC的Web层应用框架。偏前端而不是基于业务逻辑层。
	 是Spring框架的一个后续产品。

用处

天生与Spring框架集成,如:(IOC,AOP)

支持Restful风格

进行更简洁的Web层开发

支持灵活的URL到页面控制器的映射

非常容易与其他视图技术集成,如:Velocity、FreeMarker等等。

因为模型数据不存放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用)

非常灵活的数据验证、格式化和数据绑定机制、能使用任何对象进行数据绑定,不必实现特定框架的API

更加简单、强大的异常处理

对静态资源的支持

支持灵活的本地化、主题等解析

功能组件

将Web层进行了职责解耦,也就和struts2一样,基于请求-响应模型

常用主要组件

DispatcherServlet:前端控制器

Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,
		用于对请求进行处理

HandlerMapping :请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutiongChain对象
		(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)

ViewResolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,
		很容易更换其他视图技术;

如InternalResourceViewResolver将逻辑视图名映射为JSP视图

LocalResolver:本地化、国际化

MultipartResolver:文件上传解析器

HandlerExceptionResolver:异常处理器

HelloWorld

  1. 导包(略)
  2. 写配置
    web.xml要写什么?框架自身的配置文件要写什么?
  3. 测试

配置

在web.xml中配置DispatcherServlet(前端控制器),这个在idea建立web工程的时候会自动生成
web.xml

<web-app>
  <display-name>Archetype Created Web Application</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
       <!-- 实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 
       而使用默认的.默认的配置文件为: /WEB-INF/< servlet-name >-servlet.xml-->
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--SpringMVC思想是有一个前端控制器能拦截所有请求,并智能派发
      前端控制器是一个Servlet;应该在web.xml配置
  -->

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--
           servlet启动加载,servlet原本是第一次访问创建对象;
           load-on-startup:服务器启动时创建对象;值越小优先级越高
       -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <!--
        /*和/都是拦截所有请求;/:会拦截所有请求,但是不会拦截*.jsp;保证jsp访问正常;
                           /*:的范围更大;会拦截*.jsp;一旦拦截jsp页面就不能正常显示
    -->

        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

SpringMVC配置文件:

    <!--扫描所有组件-->
    <context:component-scan base-package="com.lly"/>
    <!--注解生效-->
    <mvc:annotation-driven/>

    <!--配置一个视图解析器;能帮我们拼接页面地址-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

Controller:


@Controller
public class MyFirstController {

    @RequestMapping("/hello")
    public String hello() {
        System.out.println("处理hello请求");
        return "success";
    }
}

jsp:

<html>
<body>
<h2>Hello World!</h2>
<a href="hello">hello</a>
</body>
</html>

HelloWorld的运行流程:

1、客户端点击链接会发送http://localhost:8080/hello 请求

2、来到tomcat服务器

3、SpringMVC的前端控制器收到所有请求

4、看请求地址和和@RequestMapping注解哪个匹配,来找到到底使用那个类的哪个方法来处理请求

5、前端控制器找到了目标处理器类和目标方法,直接利用反射执行目标方法

6、目标方法执行完成以后会有一个返回值,SpringMVC认为这返回值,就是要去的页面地址

7、拿到方法返回值后;用视图解析器进行拼串得到完整的页面地址

8、拿到页面地址后,前端控制器帮我们转发到页面

url-pattern细节

/:拦截所有请求,不拦截jsp页面,*.jsp请求
/*:拦截所有请求,拦截jsp页面,*.jsp请求            
处理*.jsp是tomcat做的事;所有项目的小web.xml都是继承于tomcat的大web.xml
     DefaultServlet是Tomcat中处理静态资源的!
        除过jsp,和servlet外剩下的都是静态资源;
        index.html:静态资源,tomcat就会在服务器下找到这个资源并返回;
        我们前端控制器的/禁用了tomcat服务器中的DefaultServlet  (相当于子类重写父类,父类失效)        
    
    1)服务器的大web.xml中有一个DefaultServlet是url-pattern=/
    2)我们的配置中前端控制器 url-pattern=/
           DefaultServlet失效后, 静态资源会来到DispatcherServlet(前端控制器),
           然后看哪个方法的RequestMapping是这个index.html
    3)为什么jsp又能访问;因为我们没有覆盖服务器中的JspServlet的配置
    4) /*  直接就是拦截所有请求;我们写/;也是为了迎合后来Rest风格的URL地址

RequestMapping


/**
 * RequestMapping的其他属性
 * method:限定请求方式、
 *      HTTP协议中的所有请求方式:
 *          【GET】, HEAD, 【POST】, PUT, PATCH, DELETE, OPTIONS, TRACE
 *      GET、POST
 *      method=RequestMethod.POST:只接受这种类型的请求,默认是什么都可以;
 *          不是规定的方式报错:4xx:都是客户端错误
 *              405 - Request method 'GET' not supported
 *
 * params:规定请求参数
 * params 和 headers支持简单的表达式:
 *      param1: 表示请求必须包含名为 param1 的请求参数
 *          eg:params={"username"}:
 *              发送请求的时候必须带上一个名为username的参数;没带都会404
 *
 *      !param1: 表示请求不能包含名为 param1 的请求参数
 *          eg:params={"!username"}
 *              发送请求的时候必须不携带上一个名为username的参数;带了都会404
 *      param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
 *          eg:params={"username!=123"}
 *              发送请求的时候;携带的username值必须不是123(不带username或者username不是123)
 *
 *      {“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
 *          eg:params={"username!=123","pwd","!age"}
 *              请求参数必须满足以上规则;
 *              请求的username不能是123,必须有pwd的值,不能有age
 *
 * headers:规定请求头;也和params一样能写简单的表达式
 *
 *
 *
 * consumes:只接受内容类型是哪种的请求,规定请求头中的Content-Type
 * produces:告诉浏览器返回的内容类型是什么,给响应头中加上Content-Type:text/html;charset=utf-8
 */
    @RequestMapping(value="/handle02",method=RequestMethod.POST)
    public String handle02(){
        System.out.println("handle02...");
        return "success";
    }

    /**
     *
     * @return
     */
    @RequestMapping(value="/handle03",params={"username!=123","pwd","!age"})
    public String handle03(){
        System.out.println("handle03....");
        return "success";
    }

    /**
     * User-Agent:浏览器信息;
     * 让火狐能访问,让谷歌不能访问
     *
     * 谷歌:
     * User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
     * 火狐;
     * User-Agent   Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
     * @return
     *
     */
    @RequestMapping(value="/handle04",
    headers={"User-Agent=Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"})
    public String handle04(){
        System.out.println("handle04....");
        return "success";
    }
    

RequestMapping模糊匹配功能–ant风格

 * URL地址可以写模糊的通配符:
 **  ?:能替代任意一个字符
 **  *:能替代任意多个字符,和一层路径
 **  **:能替代多层路径
 **最终还是精确优先
@Controller
public class RequestMappingTest {
    
    @RequestMapping("/antTest01")
    public String antTest01(){
        System.out.println("antTest01...");
        return "success";
    }
    
    /**
     * ?匹配一个字符,0个多个都不行;
     *      模糊和精确多个匹配情况下,精确优先
     *
     * @return
     */
    @RequestMapping("/antTest0?")
    public String antTest02(){
        System.out.println("antTest02...");
        return "success";
    }
    
    /**
     *   *匹配任意多个字符
     * @return
     */
    @RequestMapping("/antTest0*")
    public String antTest03(){
        System.out.println("antTest03...");
        return "success";
    }
    
    /**
     *  *:匹配一层路径
     * @return
     */
    @RequestMapping("/a/*/antTest01")
    public String antTest04(){
        System.out.println("antTest04...");
        return "success";
    }
    
    @RequestMapping("/a/**/antTest01")
    public String antTest05(){
        System.out.println("antTest05...");
        return "success";
    }
    
    //路径上可以有占位符:  占位符 语法就是可以在任意路径的地方写一个{变量名}
    //   /user/admin    /user/leifengyang
    // 路径上的占位符只能占一层路径
    @RequestMapping("/user/{id}")
    public String pathVariableTest(@PathVariable("id")String id){
        System.out.println("路径上的占位符的值"+id);
        return "success";
    }

请求参数

 /**
     * SpringMVC如何获取请求带来的各种信息 默认方式获取请求参数:
     	直接给方法入参上写一个和请求参数名相同的变量。这个变量就来接收请求参数的值;
     * 带:有值,没带:null;
     *
     * @RequestParam:获取请求参数的;参数默认是必须带的;
     * @RequestParam("user")String username username =
     *                             request.getParameter("user")
     *
     *
     * @RequestParam("user")
     * @PathVariable("user")
     *                       /book/【{user}pathvariable】?【user=admin(requestparam)
     *                       】
     *
     *                       value:指定要获取的参数的key
     *						 required:这个参数是否必须的
     *                       defaultValue:默认值。没带默认是null;
     *
     *
     * @RequestHeader:获取请求头中某个key的值; request.getHeader("User-Agent");
     * @RequestHeader("User-Agent")String userAgent userAgent =
     *                                    request.getHeader("User-Agent")
     *                                    如果请求头中没有这个值就会报错; value() required()
     *                                    defaultValue()
     *
     * @CookieValue:获取某个cookie的值; 以前的操作获取某个cookie;
     *                         Cookie[] cookies = request.getCookies(); 
     *                        for(Cookie c:cookies){
     *                            if(c.getName().equals("JSESSIONID")){ 
     *								 String cv = c.getValue(); } }
     * value()
     * required()
     * defaultValue()
     */

eg:

@RequestMapping("/handle01")
public String handle02(
        @RequestParam(value = "user", required = false, defaultValue = "你没带") String username,
        @RequestHeader(value = "AHAHA", required = false, defaultValue = "她也没带") String userAgent,
        @CookieValue(value="JSESSIONID",required=false)String jid) {
    System.out.println("这个变量的值:" + username);
    System.out.println("请求头中浏览器的信息:" + userAgent);
    System.out.println("cookie中的jid的值"+jid);
    return "success";
}

pojo传参问题

/**
 * 如果我们的请求参数是一个POJO;
 * SpringMVC会自动的为这个POJO进行赋值?
 * 1)、将POJO中的每一个属性,从request参数中尝试获取出来,并封装即可;
 * 2)、还可以级联封装;属性的属性
 * 3)、请求参数的参数名和对象中的属性名一一对应就行
 
 POJO对象是如何封装的?
 *     1)、SpringMVC创建一个pojo对象,每个属性都有默认值;
 *     2)、将请求中所有与pojo对应的属性一一设置过来;

 *
 * 提交的数据可能有乱码:
 * 请求乱码:
 *         GET请求:改server.xml;在8080端口处URIEncoding="UTF-8"
 *         POST请求:
 *             在第一次获取请求参数之前设置
 *             request.setCharacterEncoding("UTF-8");
 *             自己写一个filter;SpringMVC有这个filter
 *
 * 响应乱码:
 *         response.setContentType("text/html;charset=utf-8")
 * @param book
 * @return
 */

eg:

    @RequestMapping("/book")
    public String addBook(Book book){
        System.out.println("我要保存的图书:"+book);
        return "success";
    }

补充:
这样的操作在进行表单提交的时候确实可以封装上,

<form method="post" action="/book">
    name:<input type="text" name="name">
    author:<input type="text" name="author">
    price:<input type="text" name="price">
    stock:<input type="text" name="stock">
    sales:<input type="text" name="sales">
    <input type="submit">
</form>
但是选择用postman的form-data提交时却封装不上。
在目前的开发中一般都要加上@RequestBody注解。
所以,知道表单提交有这么个操作就行

filter:

  <!-- 配置字符集 -->
  <filter>
      <filter-name>encodingFilter</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>
      <init-param>
          <param-name>forceEncoding</param-name>
          <param-value>true</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>encodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

总结
即使用SpringMVC,在配置前端控制器后立马上手写字符拦截器的filter。
tomcat一装上,就在server.xml的8080出添加URIEncoding="UTF-8"
ps:配置其他过滤器之前,如(HiddenHttpMethodFilter),否则不起作用。
因为其他的过滤器可能在你字符编码过滤器对字符进行操作之前,就已将字符拿到,所以显得过滤器没有作用

以上只是针对请求方式是post的,对于请求方式为get的请求,在server.xml中找到以下代码
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
并添加URIEncoding="UTF-8"

**该操作我未曾实现,所以不知可行性。且有说tomcat8以后对于get的请求已有默认的处理方式,不需对此做处理
--

/**
 * SpringMVC可以直接在参数上写原生API;
 *
 * HttpServletRequest
 * HttpServletResponse
 * HttpSession
 *
 *
 * java.security.Principal
 * Locale:国际化有关的区域信息对象
 * InputStream:
 *         ServletInputStream inputStream = request.getInputStream();
 * OutputStream:
 *         ServletOutputStream outputStream = response.getOutputStream();
 * Reader:
 *         BufferedReader reader = request.getReader();
 * Writer:
 *         PrintWriter writer = response.getWriter();
 *
 * @throws IOException

 *
 *
 */

eg:

    @RequestMapping("/handle03")
    public String handle03(HttpSession session,
            HttpServletRequest request,HttpServletResponse response) throws IOException {
        request.setAttribute("reqParam", "我是请求域中的");
        session.setAttribute("sessionParam", "我是Session域中的");

        return "success";
    }

评论