0、出错在:org.seckill.servce.SeckillServiceImpl类中两个@autowired处
报错:Could not autowire. No beans of “SeckillDao” type found.
报错:Could not autowire. No beans of “SeckillDao” type found.
// 在spring容器中获取Dao实例,注入到Service依赖: @Autowired // 自动注入 private SeckillDao seckillDao; @Autowired private SuccessKilledDao successKilledDao;
1、Dao接口
package org.seckill.dao;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.seckill.entity.Seckill;
import org.springframework.web.bind.annotation.ResponseBody;
/*
* 这个DAO接口负责实体Seckill中属性的增删改查的设计
*/
@ResponseBody
public interface SeckillDao {
/**
* 减库存(更新库存):要减库存id,执行减库存的时间
* @param seckillId
* @param killTime 秒杀时间
* @return 更新语句影响的行数:假如 >=1,表示这条更新语句没有成功
*/
int reduceNumber(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);
/**
* 根据id查询秒杀对象
* @param seckillId
* @return
*/
Seckill queryById(long seckillId);
/**
* 根据偏移量查询秒杀商品列表
* @param offset 偏移量
* @param limit 在偏移量之后,查询多少条记录
* @return
*/
List<Seckill> queryAll(@Param("offset") int offset, @Param("limit") int limit);
}
2、Service实现类
package org.seckill.service.impl;
import java.util.Date;
import java.util.List;
import org.seckill.dao.SeckillDao;
import org.seckill.dao.SuccessKilledDao;
import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillExecution;
import org.seckill.entity.Seckill;
import org.seckill.entity.SuccessKilled;
import org.seckill.enums.SeckillStateEnum;
import org.seckill.exception.RepeatKillException;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
// spring提供的注解:@component 代表全部的组件、@Service、@Dao、@Controller
@Service
public class SeckillServiceImpl implements SeckillService {
// 日志 slf4j接口
private Logger logger = LoggerFactory.getLogger(this.getClass());
// 在spring容器中获取Dao实例,注入到Service依赖:
@Autowired // 自动注入
private SeckillDao seckillDao;
@Autowired
private SuccessKilledDao successKilledDao;
// md5盐值字符串,用户混淆md5: 乱敲的,越复杂越好。
private final String slat = "sfdhasfosadihf23oihikefGFUUI5778^&*^%^*sdjkfakj";
public List<Seckill> getSeckillList() {
return seckillDao.queryAll(0, 4);
}
public Seckill getById(long seckillId) {
return seckillDao.queryById(seckillId);
}
public Exposer exportSeckillUrl(long seckillId) {
// 优化点:缓存优化
/**
* get from catch
* if null
* get db
* put cache
* return
* 这是缓存的伪代码 --> 但是不能写在(业务逻辑)service中。
* 正确的方式:是写在dao包中(dao:数据访问对象。dao包:访问MySQL数据的,也可以有访问redis数据的。)
*/
Seckill seckill = seckillDao.queryById(seckillId);
// 未查到秒杀商品
if(seckill == null)
return new Exposer(false, seckillId);
Date startTime = seckill.getStartTime();
Date endTime = seckill.getEndTime();
// 当前系统的时间
Date nowTime = new Date();
// 秒杀还未开始或 已经结束。
// 注:nowTime.getTime() 返回日期时间的毫秒数(long类型)
if( nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime() )
return new Exposer(false, seckillId, nowTime.getTime(), startTime.getTime(), endTime.getTime());
// md5 转化特定字符串的过程,且不可逆
String md5 = getMD5(seckillId);
return new Exposer(true, md5, seckillId);
}
// 生成md5 -- 用来加密
private String getMD5 (long seckillId){
String base = seckillId + "/" + slat;
String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
return md5;
}
@Transactional // 事务
/*
* 使用注解控制声明式事务的优点:
* 1、开发团队达成一致约定,明确标注事务方法的编程方法。
* 2、保证事务方法的执行时间尽可能短,不要加网络操作(如RPC/HTTP请求)。假如必须加网络请求,可以将它们剥离到事物方法之外。
* 3、不是全部的方法都需要事务。如只有一条修改操作、只读操作都是不需要事务控制的。(当有两条以上的修改操作需要同时完成时
* 需要事务,如本例中的秒杀=减库存+记录购买明细。或只读操作需要...。详细可看MySQL行级锁相关的文档。)
*/
public SeckillExecution executeSeckill (long seckillId, long userPhone, String md5)
throws SeckillException, RepeatKillException, SeckillCloseException {
if(md5 == null || !md5.equals(getMD5(seckillId)))
throw new SeckillException("seckill data rewrite");
try {
// 执行秒杀逻辑:减库存 + 记录秒杀行为
Date nowTime = new Date();
int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
if(updateCount <= 0)
// 没有更新数据记录。即,秒杀结束了
throw new SeckillCloseException("seckill is closed");
else {
// 秒杀成功了,则要记录购买行为
int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
if(insertCount <= 0)
// 插入行数为 0,则重复秒杀
throw new RepeatKillException("seckill repeat");
else {
// 成功秒杀
SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);
}
}
} catch (SeckillCloseException e1) {
throw e1;
} catch (RepeatKillException e2) {
throw e2;
} catch (Exception e) {
logger.error(e.getMessage(), e);
// 全部编译期异常,转化为运行期异常
// 这样做的优势:spring申明式事务会给全部运行期异常做回滚(rollback),而编译期异常就不行
// 目的: 就是,一旦出错,就立即回滚。为的是减库存的操作和记录购买行为的操作没有同时的执行。
throw new SeckillException("seckill inner error: " + e.getMessage());
}
}
}
3、扫描的service包
<?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:tx="http://www.springframework.org/schema/tx" 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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置:扫描service包下全部使用注解的类型(只要扫描就行,其他都交给注解) --> <context:component-scan base-package="org.seckill.service"/> <!-- 基于Spring的声明式事务的配置: Mybatis使用的是jdbc的事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据库的连接池 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置基于注解的声明式事务: 默认使用注解来管理事务行为 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
解决方案
40
实现类 注解个@Service。