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。