四、高并发秒杀API之Web层设计与实现

Web层涉及到的技术:

前端交互:页面之间的交互和交互细节

Restful:前端设计接口之间的一种常用的规范

Spring MVC:框架整合,以及如何应用设计和实现Restful接口

Bootstrap和jquery:前者负责页面布局和样式控制,后者负责交互的实现。 


1 前端分析与设计


1.1 前端交互设计部分

前端页面流程:

                     

        根据细致的流程逻辑,前端工程师设计页面,后端工程师开发对应的代码,可以使得前端和后端之间相互碰触,更加容易的协调一致。所以,前端交互流程是系统开发中一个很重要的部分。


1.2 Restful接口设计

      什么是Restful? 

     它是一种优雅的URI表述方式,用来设计我们资源的访问URL;通过这个URL的设计,我们就可以很自然的感知到这个URL代表的是哪种业务场景或者什么样的数据或资源。基于Restful设计的URL,对于我们接口的使用者、前端、web系统或者搜索引擎甚至是我们的用户,都是非常友好的。

     看看URL设计规范:

                                 

 

       关于Restful的了解不再做详细介绍,下面看看我们这个秒杀系统的URL设计:

                                             

       接下来基于上述资源接口来开始我们对SpringMVC框架的使用。 


2 整合配置Spring MVC框架

2.1 Spring MVC运行流程

          使用SpringMVC始终是围着着Handle开发,Handler最终的产出就是Model和View,即,模型和视图。下面先回顾一下Spring MVC的运行路程图:

                                            

工作原理(运行流程):

       

1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将请求转交给DispatcherServlet.

2、DipatcherServlet接收到这个请求之后将根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandlerMapping(默认使用DefaultAnnotationHandlerMapping)的配置来映URL,找到处理请求的处理器Handler(每一个请求都对应了一个Handler)。

3-4、DispatcherServlet将处理权交给根据HandlerMapping找到对应Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用:默认使用DefaultAnnotationHandlerAdapter来对上一步的Handler进行适配,匹配到我们自己写的Controller类(如果使用到拦截器,则同样把拦截器绑定到这个流程当中)。

5、Handler对数据处理完成以后将返回一个ModelAndView()对象交付给DispatcherServlet。

6、Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver(利用视图解析器InternalResourceViewResolver)将逻辑视图转化为真正的视图View,并用model来承载数据。

7、Dispatcher通过model对ModelAndView()中的参数进行解析,最终展现出完整的view并返回给客户端。


2.1.1 Http请求映射原理

          下面再看一下Http请求地址的映射原理:

                                

          用户发送的Http请求,首先会发送到Servlet容器(Tomcat或Jetty),而Spring MVC则使用的是HandlerMapping来映射URL,然后使用Handler方法来最终执行。(默认使用 DefaultAnnotationHandlerMapping注解来映射,也可以通过XML配置编程来映射)

 

注解映射的技巧:

                

请求方法的细节处理:

1. 请求参数绑定

2. 请求方式限制

3. 请求转发和重定向

4. 数据模型赋值

5. 返回Json数据

6. Cookie访问

     看下面的一个例子:

            

       主要应用了URL书写方式,限制了数据提交方式,判定使用url转发还是重定向,使用model承载数据,返回字符串修改URL链接。


2.1.2如何返回Json数据

               


2.1.3 Cookie访问

                      


 

2.2 Spring MVC整合

          在WEB-INF项目中配置文件web.xml中配置我们的前端中央控制器DispatcherServlet的配置,也是Spring MVC的核心所在,并加载mybatis配置文件

spring-dao.xml,spring配置文件spring-service.xml、spring-web.xml文件,将三个框架整合起来,文件内容如下:

<!DOCTYPE web-app PUBLIC
 "-//SunMicrosystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app version="2.4"metadata-complete="true"
        xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
        <!--用maven创建的web-app需要修改servlet的版本为3.1-->
<!--配置DispatcherServlet-->
    <servlet>
        <servlet-name>seckill-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置SpringMVC 需要配置的文件:
                  spring-dao.xml,spring-service.xml,spring-web.xml
            Mybites -> spring ->springMvc
            Mybatis被整合进SPring中,Spring要整合进SPring MVC当中;
                                   不过,Spring MVC本来就出自Spring框架,同属一个框架,已经无缝衔接整合了
        -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-*.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>seckill-dispatcher</servlet-name>
        <!--默认匹配所有请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 
</web-app>

 

然后,在Spring容器中进行web层相关的bean配置,即Controller的配置,在Spring包下创建一个spring-web.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
         http://www.springframework.org/schema/context/spring-context-3.0.xsd
         http://www.springframework.org/schema/mvc
         http://www.springframework.org/schema/mvc/spring-mvc.xsd
         " >
    <!--配置spring mvc-->
    <!--1:开启springmvc注解模式-->
    <!-- 监护配置
        a.自动注册DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter
        b.默认提供一系列的功能:数据绑定,数字和日期的format@NumberFormat,@DateTimeFormat,xml,json的默认读写支持-->
    <mvc:annotation-driven/>
 
    <!--2:静态资源默认servlet配置-->
    <!--
        1):加入对静态资源处理:js,gif,png
        2):允许使用 "/" 做整体映射
    -->
    <mvc:default-servlet-handler/>
 
    <!--3:配置JSP 显示ViewResolver-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
 
    <!--4:扫描web相关的bean-->
    <context:component-scan base-package="org.seckill.web"/>
         
</beans>

如此,便完成了SpringMVC框架的配置,将框架整合到了项目当中,接下来则是开发Controller了,这里是基于Restful接口进行我们的项目的Controller开发。

 

3 基于Restful的Controller开发

         这里Restful接口使用Spring MVC实现的,Controller中的每一个方法都对应我们系统中的一个资源URL,其设计应该遵循Restful接口的设计风格。在org.seckill包下创建一个web包用于放web层Controller开发的代码,在该包下创建一个SeckillController.java :

@Controller
@RequestMapping("/seckill") //url:/模块/资源/{id}/细分/seckill/list
public class SeckillController {
    @Autowired
    private SeckillService seckillService;
   
    @RequestMapping(value="/list",method=RequestMethod.GET)
    public String list(Model model)
    {
        List<Seckill> list=seckillService.getSeckillList();
        model.addAttribute("list",list);
        //list.jsp+model=ModelAndView
        return "lsit";
    }
   
    @RequestMapping(value="/{seckillId}/detail",method=RequestMethod.GET)
    public String detail(@PathVariable("seckillId")Long seckillId,Model model){
        if(seckillId==null) //请求不存在的时候,直接重定向回到列表页
        {
            return "redirect:/seckill/list";
        }
        Seckill seckill=seckillService.getById(seckillId);
        if(seckill==null)
        {
            //如果请求对象不存在
            return "forward:/seckill/list";
           
        }
        return "detail";       
    }
   
    @RequestMapping(value = "/{seckillId}/exposer",
            method = RequestMethod.POST,
            produces = { "application/json;charset=UTF-8" })
    @ResponseBody //封装成json
    public SeckillResult<Exposer> exposer(Long seckillId) {
        SeckillResult<Exposer> result;
        try {
            //Exposer:存放是否秒杀的状态信息。
            Exposer exposer = seckillService.exportSeckillUrl(seckillId);
           
            result = new SeckillResult<Exposer>(true, exposer);
        } catch (Exception e) {
            e.printStackTrace();
            result = new SeckillResult<Exposer>(false, e.getMessage());
        }
 
        return result;
    }
 
    /*
     * md5:验证用户的请求有没有被篡改
     * 默认的ajax输出是Json格式,所以将输出结果都封装成Json格式。
     */
    @RequestMapping(value = "/{seckillId}/{md5}/execution",
            method = RequestMethod.POST,
            produces = { "application/json;charset=UTF-8" })
    @ResponseBody
    public SeckillResult<SeckillExecution> execute(
            @PathVariable("seckillId") Long seckillId,
            @PathVariable("md5") String md5,
            //required = false表示电话号码不是必须的。
            @CookieValue(value = "killPhone", required = false) Long phone) {
        //Spring MVC valid
        if (phone == null) {
            return new SeckillResult<SeckillExecution>(false, "未注册");
        }
       
        SeckillResult<SeckillExecution> result;
        try {
            SeckillExecution execution = seckillService.executeSeckill(seckillId, phone, md5);
            return new SeckillResult<SeckillExecution>(true, execution);
        } catch (RepeatKillException e1) {
            SeckillExecution execution = new SeckillExecution(seckillId,
                    SeckillStateEnum.REPEAT_KILL);
            return new SeckillResult<SeckillExecution>(false, execution);
        } catch (SeckillCloseException e2) {
            SeckillExecution execution = new SeckillExecution(seckillId,
                    SeckillStateEnum.END);
            return new SeckillResult<SeckillExecution>(false, execution);
        } catch (Exception e) {
            SeckillExecution execution = new SeckillExecution(seckillId,
                    SeckillStateEnum.INNER_ERROR);
            return new SeckillResult<SeckillExecution>(false, execution);
        }
 
    }
 
    // 获取系统时间
    @RequestMapping(value = "/time/now", method = RequestMethod.GET)
    public SeckillResult<Long> time() {
        Date now = new Date();
        return new SeckillResult<Long>(true, now.getTime());
    }
 
}

 

Controller中的方法的开发完全是按照Service接口中的方法进行开发的:

第一个方法用于访问我们商品的列表页;

第二个方法访问商品的详情页;

第三个方法用于返回一个json数据,数据中封装了我们商品的秒杀地址;

第四个方法用于封装用户是否秒杀成功的信息;

第五个方法用于返回系统当前时间。

代码中涉及到一个将返回秒杀商品地址封装为json数据的一个Vo类,即SeckillResult.java,在dto包中创建此类:

//所有ajax请求的返回类型,封装为json结果类型
public class SeckillResult<T> //泛型类型的类
{
    private boolean success;
    private T data;
    private String error;
    public SeckillResult(boolean success, T data) {
        super();
        this.success = success;
        this.data = data;
    }
    public SeckillResult(boolean success, String error) {
        super();
        this.success = success;
        this.error = error;
    }
    //getter and setter……,自行补充
}

 

到此处,Controller的开发内容完成,剩下的就是对Web页面内容进行开发。


4 基于bootstrap开发页面结构

bootStrap是目前比较受欢迎的前端框架,是基于HTML、CSS、JavaScript的,简洁灵活,使得Web开发更加快捷方便。

BootStrap详细教程可参考:http://www.runoob.com/bootstrap/bootstrap-tutorial.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值