《Java高并发秒杀API》知识总结
最近几天看了这个项目《Java高并发秒杀API》,目前除了高并发知识没有涉及到,关于Dao层,service层,Web层,以及前端交互界面都有涉及。整个项目就是平常我们看到的秒杀界面,包含用户登录手机号,查看商品列表,查看商品秒杀状态,以及执行秒杀和秒杀后的结果反馈!
下面具体看一看每一层的功能。
Dao层
Dao层主要是两个接口的设计
- 接口1:秒杀接口的设计
- 减少库存
- 根据id来查询秒杀商品对象
- 根据偏移量查询秒杀商品列表页
- 接口2:秒杀成功对象接口的设计
- 插入购买明细
- 根据独有的id和userPhone查询秒杀成功的对象并携带秒杀商品的信息(因为多个商品的信息会对应一个人)
具体接口功能的实现用的是mybatis框架来实现,主要是可以自己控制sql语句的编写,达到想要的数据输出,建立相应的xml文件,编写实现功能的sql语句。
首先是配置mybatis-config.xml全局配置文件
<configuration>
<!-- 配置全局属性 -->
<settings>
<!-- 使用jdbc的getGeneratedKey获取数据库自增主键值 -->
<setting name="useGeneratedKeys" value="true"/>
<!-- 使用列别名替换列名 -->
<setting name="useColumnLabel" value="true"/>
<!-- 使用驼峰命名法 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
spring-dao.xml的配置,这里用的是c3p0连接池,可以人为的设定属性参数
具体配置步骤如下:
<!-- 配置整合mybatis过程 -->
<!-- 数据库连接池相关参数properties属性 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 数据库的连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=UTC"></property>
<property name="user" value="root"></property>
<property name="password" value="******"></property>
<!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30"></property>
<property name="minPoolSize" value="5"></property>
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false"></property>
<!-- 获取当前超时时间 -->
<property name="checkoutTimeout" value="1000"></property>
<!-- 当获取连接失败尝试次数 -->
<property name="acquireRetryAttempts" value="2"></property>
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置mybatis-config.xml全局配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 扫描entity 别名设置 -->
<property name="typeAliasesPackage" value="com.duanxicao.entity"></property>
<!-- 需要扫描mapper需要的xml文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!-- 配置扫描Dao接口包,动态实现dao接口,注入到spring容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 给出需要扫描的Dao接口 -->
<property name="basePackage" value="com.duanxicao.dao"></property>
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
Service层
service主要是实现给用户的接口(需要站在用户使用的角度上设计接口)
- 查询所有的秒杀记录
- 查询单个的秒杀记录
- 秒杀开启输出秒杀地址,否则输出系统时间,告诉用户还需等待多久
- 运用了dto,数据传输对象的模式,传入参数,让具体的传输类做处理,最后返回我们所需要的数据
- 这里用到的是接口暴露的传输类,简称exposer类,主要是包含是否开启秒杀的判断,MD5加密值,以及秒杀对象商品id,当前时间,秒杀开启时间,秒杀结束时间。通过设定不同的构造方法,实现不同的处理,得到不同情况的不同结果
- 执行秒杀操作(这里需要设定秒杀过程中可能产生的异常,用exception包中的相关异常类接收并处理)
- 这里运用传输类execution执行操作,判断用户秒杀状态,以及执行后的具体完成的消息,并反馈
编写实现类impl
开启秒杀地址实现方法:
先通过商品id查询到商品对象,如果商品对象为null,回传给数据传输对象失败+商品id的数据,若不为空,则获取当前系统时间,与对应商品秒杀开启时间和结束时间做一个对比,时间不符合,则回传响应的false+id+startTime+endTime,否则则是成功,此时生成md5,传入true+md5+id
执行秒杀操作:
先判断暴露接口中md5值与获得的md5值是否相等,不相等的话,则数据被篡改,抛出异常。否则获取当前时间,完成减库存的操作,若减库存成功,则完成插入秒杀成功明细操作,若插入明细不成功,由于设定了手机号为唯一的值,则说明重复秒杀,抛出异常,否则则说明成功此时获取到成功秒杀对象,传入数据传输类中做具体处理。
spring-service.xml配置
<!-- 扫描service包下使用注解的类型 -->
<context:component-scan base-package="com.duanxicao.service"></context:component-scan>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置基于注解的声明式事务
默认使用注解来管理事务行为
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
Web层
web层主要是通过注解的方式来完成相应的数据操作,
- 具体明细(所有秒杀shangpin)
- 相关细节(伴随秒杀商品id)
- 暴露秒杀接口(具体商品id)
- 执行秒杀操作
- 获取系统当前时间
spring-web.xml配置
<!-- 配置SpringMVC -->
<!-- 1:开启SpringMVC注解模式 -->
<!-- 简化配置
1.自动注册DefaultAnnotationHandlerMapping,DefaultAnnotationHandlerAdapter
2.提供一系列的数据绑定:数字和日期格式
xml,json默认读写的支持
-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 2:开启对静态资源的支持配置
1.加入对静态资源的处理
2.允许使用“/"做映射
-->
<mvc:default-servlet-handler/>
<!-- <mvc:resources location="/js/" mapping="/js/**"></mvc:resources> -->
<!-- 配置jsp,显示viewResolver -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 扫描web相关的bean -->
<context:component-scan base-package="com.duanxicao.web"></context:component-scan>
最后就是前后端交互界面了。
注:SpringMVC运行过程
- 客户端(浏览器)发送请求,直接请求到DispatcherServlet,
- DispathcerServlet根据请求信息调用HandlerMapping,DefaultAnnotationHandlerMapping解析请求对象的handler,
- 解析到对应的Handler之后,HandlerAdapter调用组件DefaultAnnotationHandlerAdapter适配器做处理
- 处理调用Controller中的HandlerMethod方法,当HandlerMethod执行完成之后,会返回一个ModelAndView,
- viewResolver会根据逻辑对view进行视图解析,解析之后调用.jsp对应的.class文件并运行,得到结果
- 最后把运行的.class文件的结果响应给客户端,以上就是SpringMVC运行原理