文档章节

[Spring cloud 一步步实现广告系统] 6. Service实现&Zuul配置&Test

IsaacZhang
 IsaacZhang
发布于 2019/07/29 19:52
字数 1701
阅读 10
收藏 0
DAO层设计实现

这里我们使用Spring DATA JPA来实现数据库操作,当然大家也可以使用Mybatis,都是一样的,我们依然以用户表操作为例:

/**
 * AdUserRepository for 用户数据库操作接口
 * 继承自JpaRepository<AdUser, Long>,第一个参数AdUser代表当前要操作的实体类的class定义,第二个参数Long表示该类的主键类型
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
 */

public interface AdUserRepository extends JpaRepository<AdUser, Long> { 
    /**
     * 根据用户名称获取用户
     *
     * @param username 名称
     * @return 用户对象
     */
    AdUser findByUserName(String username);

    List<AdUser> findAllByUserName(String userName);
}
  • JPARepository 的默认实现方法,如果我们只是继承了JpaRepository而没有实现具体的操作方法,我们也是可以通过使用它的默认方法来做CRUD操作的,如下:

    UTOOLS1564192818939.png

功能Service实现

创建service package,依然以用户操作为例,创建com.sxzhongf.ad.service.IUserServicecom.sxzhongf.ad.service.impl.UserServiceImpl,UserServiceImpl实现了IUserService

  1. 创建 IUserService 接口
/**
 * IUserService for 用户service
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
public interface IUserService {
    /**
     * 创建用户接口
     *
     * @param userRequestVO {@link UserRequestVO}
     * @return {@link UserResponseVO}
     * @throws AdException 错误
     */
    UserResponseVO createUser(UserRequestVO userRequestVO) throws AdException;

    List<AdUser> findAllByUserName(String userName);
}
  1. 使用IUserService接口
/**
 * UserServiceImpl for 用户service
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    private final AdUserRepository userRepository;

    @Autowired
    public UserServiceImpl(AdUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    /**
     * 创建用户
     *
     * @param userRequestVO {@link UserRequestVO}
     * @return result {@link UserResponseVO}
     */
    @Override
    @Transactional
    public UserResponseVO createUser(UserRequestVO userRequestVO) throws AdException {
        if (!userRequestVO.validate()) {
          	log.error("Request params error: {}", userRequestVO);
            throw new AdException(Constants.ErrorMessage.REQUEST_PARAM_ERROR);
        }
        //查重
        AdUser existUser = userRepository.findByUserName(userRequestVO.getUserName());
        if (existUser != null) {
            log.error("{} user is not exist.", userRequestVO.getUserName());
            throw new AdException(Constants.ErrorMessage.USER_EXIST);
        }
        AdUser user = userRepository.save(new AdUser(userRequestVO.getUserName(), CommonUtils.md5(userRequestVO.getUserName())));
        log.info("current user is : {}", user);
        return new UserResponseVO(user.getUserId(), user.getUserName(), user.getToken(),
                user.getCreateTime(), user.getUpdateTime());
    }

    @Override
    public List<AdUser> findAllByUserName(String userName) {
        return userRepository.findAllByUserName(userName);
    }
}
  1. 创建数据传输对象(dto/vo)

    其实好多人在这里都会特别郁闷,搞不清楚这些命名有什么区别,个人建议是大家不用纠结,dto(data transfer object),就是表示我们在各个层传递的对象,vo在展示层操作的对象。但是这个只是个命名,它的本质就是一个object, 你传递到DAO层可以吗?当然可以,你传单独字段都是可以的。所以,没必要过分纠结这种信息,咬文嚼字有时候反而会适得其反。

/**
 * UserRequestVO for 创建用户请求对象VO
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRequestVO {
    private String userName;
    public boolean validate() {
        return !StringUtils.isEmpty(userName);
    }
}

---
  
/**
 * UserResponseVO for 用户响应VO
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserResponseVO {
    private Long userId;
    private String userName;
    private String token;
    private Date createTime;
    private Date updateTime;
}
  1. 因为报错信息有可能是相同的,那我们抽取一个常量类来封装。
/**
 * Constants for TODO
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
public class Constants {
    /**
     * 通用错误信息异常类
     */
    public static class ErrorMessage {
        public static final String REQUEST_PARAM_ERROR = "请求参数异常";
        public static final String USER_EXIST = "用户已存在";
        public static final String USER_NOT_EXIST = "用户不存在";
    }
}
  1. 在Common Project 下面创建一个工具类com.sxzhongf.ad.common.utils.CommonUtils,用来对用户username进行md5加密来获取token信息。
/**
 * CommonUtils for 工具类
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
@Slf4j
public class CommonUtils {
    /**
     * md5 加密
     */
    public static String md5(String value) {
        return DigestUtils.md5Hex(value).toUpperCase();
    }
}

参考创建用户的实现,依次实现其他表操作。

Controller实现

依然以用户功能实现为例:

/**
 * UserController for 用户controller
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 */
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;

    @PostMapping(path = "/create")
    public UserResponseVO createUser(@RequestBody UserRequestVO requestVO) throws AdException {
        log.info("ad-sponsor: createUser -> {}", JSON.toJSONString(requestVO));
        return userService.createUser(requestVO);
    }

    @GetMapping(path = "/get")
    public CommonResponse getUserList(@Param(value = "username") String username) throws AdException {
        log.info("ad-sponsor: getUserList -> {}", JSON.toJSONString(username));
        return new CommonResponse(userService.findAllByUserName(username));
    }
}

在网关中配置广告投放系统

我们在投放系统的配置中,配置了server.servlet.context-path:/ad-sponsor这么一个路径,意味着所有请求当前系统的路径都需要带有ad-sponsor, 例如:http://xxx/ad-sponsor/user/get?username=yyy,这是网关请求所必需的。根据上述,我们在网关服务中配置我们当前的投放系统:

spring:
  application:
    name: ad-gateway-zuul
server:
  port: 1111
eureka:
  client:
    service-url:
      defaultZone: http://server1:7777/eureka/,http://server2:8888/eureka/,http://server3:9999/eureka/
  instance:
    hostname: ad-gateway-zuul
##############################################
# 以下为重要信息
zuul:
  ignored-services: '*' # 过滤所有请求,除了下面routes中声明过的服务
  # 配置网关路由规则
  routes:
    sponsor: #在路由中自定义服务路由名称
      path: /ad-sponsor/**
      serviceId: mscx-ad-sponsor #微服务name
      strip-prefix: false
    search: #在路由中自定义服务路由名称
      path: /ad-search/**
      serviceId: mscx-ad-search #微服务name
      strip-prefix: false
  prefix: /gateway/api
  strip-prefix: false #不对 prefix: /gateway/api 设置的路径进行截取,默认转发会截取掉配置的前缀

Test
  • 直接访问投放系统

    调用curl -G http://localhost:7000/ad-sponsor/user/get?username=Isaac%20Zhang,返回结果:

{
  code: 0,  // 统一成功标示
  message: "success", // 统一处理结果message
  data: [  // 具体的对象信息
    {
      userId: 10,
      userName: "Isaac Zhang",
      token: "2D3ABB6F2434109A105170FB21D00453",
      userStatus: 1,
      createTime: 1561118873000,
      updateTime: 1561118873000
    }
  ]
}
  • 通过网关调用

    因为我在网关配置中加了前缀prefix: /gateway/api,因此,我们访问的时候需要添加上这个前缀信息,否则会报404错误。

    curl -G http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang,我们发现结果并没有按照我们想象的展示出来。

    bogon:~ zhangpan$ http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang
    -bash: http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang: No such file or directory
    

    为什么呢?我们来查看一下日志:

    2019-07-27 20:44:19.093  INFO 4766 --- [nio-1111-exec-4] c.s.a.g.filter.ValidateTokenFilter       : GET request to http://localhost:1111/gateway/api/ad-sponsor/user/get
    2019-07-27 20:44:19.093  WARN 4766 --- [nio-1111-exec-4] c.s.a.g.filter.ValidateTokenFilter       : access token is empty
    2019-07-27 20:44:19.098  INFO 4766 --- [nio-1111-exec-4] c.s.ad.gateway.filter.AccessLogFilter    : Request "/gateway/api/ad-sponsor/user/get" spent : 0 seconds.
    2019-07-27 20:48:37.801  INFO 4766 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
    

    我们可以清晰的看到,ValidateTokenFilter : access token is empty,为什么会有这么一个报错呢?那是因为我在配置网关的时候,添加了一次拦截:

    /**
     * ValidateTokenFilter for 服务token校验
     *
     * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
     */
    @Slf4j
    @Component
    public class ValidateTokenFilter extends ZuulFilter {
    ...
        @Override
        public Object run() throws ZuulException {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
    
            Object accessToken = request.getHeader("accessToken"); //.getParameter("accessToken");
            if (accessToken == null) {
                log.warn("access token is empty");
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
    //            ctx.setResponseBody(body)对返回body内容进行编辑
                return null;
            }
            log.info("access token ok");
            return null;
        }
    }
    

    观察代码我们发现,会从RequestHeader中获取accessToken参数,我们没有提供,当然就会报错了呀。接下来,我们提供上该参数再试:

    bogon:~ zhangpan$ curl -H "accessToken:true" http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang
    ---返回
    {"code":0,"message":"success","data":[{"userId":10,"userName":"Isaac Zhang","token":"2D3ABB6F2434109A105170FB21D00453","userStatus":1,"createTime":1561118873000,"updateTime":1561118873000}]}
    

至此,我们的广告投放系统简单功能已经全部实现完毕,并且可以通过网关进行转发。

© 著作权归作者所有

IsaacZhang
粉丝 2
博文 51
码字总数 70063
作品 0
东城
程序员
私信 提问
加载中

评论(0)

springcloud(十):服务网关zuul初级篇

为什么需要API Gateway 1、简化客户端调用复杂度 在微服务架构模式下后端服务的实例数一般是动态的,对于客户端而言很难发现动态改变的服务实例的访问地址信息。因此在基于微服务的项目中为了...

itcloud
2018/11/29
223
0
Spring Cloud 入门教程8、服务网关Zuul+Hystrix:断路处理与监控

一、前言 1、本篇主要内容 通过实现FallbackProvider进行Zuul网关路由断路处理 Zuul+Hystrix路由断路监控配置与说明 2、本篇环境信息 框架 版本 Spring Boot 2.0.0.RELEASE Spring Cloud Fin...

吴伟祥
2019/02/19
70
0
Spring Cloud 入门教程9、服务限流/API限流(Zuul+RateLimiter)

一、前言 1、什么是RateLimiter、Spring Cloud Zuul RateLimiter? RateLimiter是Google开源的实现了令牌桶算法的限流工具(速率限制器)。http://ifeve.com/guava-ratelimiter/ Spring Clou...

吴伟祥
2019/02/19
961
0
企业分布式微服务云SpringCloud SpringBoot mybatis (五)路由网关(zuul)

在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。一个简...

itcloud
2019/02/21
39
0
java B2B2C 仿淘宝电子商城系统-服务网关zuul初级篇

为什么需要API Gateway 1、简化客户端调用复杂度 需要JAVA Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码 一零三八七七四六二六 在微服务架构模式下后端服务的实例数一般是...

明理萝
2019/04/17
41
1

没有更多内容

加载失败,请刷新页面

加载更多

工作流-个人任务

设置个人任务处理人 1. BPMN中定义 <process id="group" name="组任务" isexecutable="true"> <startevent id="startevent1" name="Start"></startevent><!-- 直接设置处理人 --> ......

小小明1995
4分钟前
25
0
没有名为MySQLdb的模块

我正在使用Python 2.5.4版并安装MySQL 5.0版和Django。 Django在Python上运行良好,但在MySQL上运行良好。 我在Windows Vista中使用它。 #1楼 感谢derevo,但我认为还有另一种好方法: 下载并...

技术盛宴
16分钟前
28
0
MySQL学习笔记(一)InnoDB内存数据结构浅析

以下文章来源于腾讯云数据库,作者陈俊熹 Innodb存储引擎是目前MySQL最主流的存储引擎,学习Innodb, 可以先从其最基础的数据结构开始。Innodb的数据结构主要包括内存数据结构(In-MemoryStruc...

小知_知数堂
22分钟前
35
0
linux常用命令及用法示例

一、 帮助命令 ##1. 常用快捷键 快捷键| 功能 -|- ctrl + c |停止进程 ctrl+l |清屏;彻底清屏是:reset ctrl + q |退出 tab键 |提示(更重要的是可以防止敲错) 上下键 |查找执行过的命令 ct...

幻境fairy
29分钟前
20
0
gitlab重置用户密码

用户lms登录gitlab的密码忘记了,需要重置密码。 gitlab重置用户lms的密码: [root@gitlab ~]# gitlab-rails console production #进入gitlab管理控制台Loading production environment ...

xiaomin0322
32分钟前
32
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部