5-2 用户模块-7个接口实现

  • web端注册
  • app端注册(所需参数更少)
  • 登录
  • 用户详情
  • 修改密码
  • 用户开关
  • 修改用户信息

一、新增类

  1. AppUserService.java

此类需定义和mapper层接口大致相同的方法(仅做返回值取舍,因为提供使用的对象不一样),被实现类实现后直接对访问接口提供能力,也就是供调用方进出

package com.a2j.service.user;

public interface AppUserService {
    // 方法实现细节不做展开
}
  1. AppUserImpl.java

该类为 AppUserService.java接口的实现类,用于实现接口和数据库之间的主要逻辑,是逻辑处理最重要的一个类,记得加上 @Service 注解

package com.a2j.service.user;

// 标注service层,一定要在实现类,不能在service接口层
@Service
public class AppUserImpl implements AppUserService{

    // 使用如下注解,注入mapper层对象引用
    @Autowired
    AppUserMapper mapper;

    // 方法实现细节不做展开
}
  1. AppUserController.java
package com.a2j.controller.user;

// 以下两个早就学过的用于controller的注解
@RestController
@RequestMapping("/user")
public class AppUserController {

    @Autowired
    AppUserService userService;

    // 方法实现细节不做展开
}

二、请求方法

GetMappingPostMappingPutMappingDeleteMapping

  • 通常选用规则:

仅查询用 GET,新增用 POST,修改用 PUT。较为复杂的入参建议使用 @RequestBody(如带分页的查询接口)

但有一种情况需要注意,@RequestBody最好不要和@GetMapping一起使用
否则你就会收获错误 “required request body is missing:...” 
由于GET的参数是通过Url方式传递而不是请求体传递的,所以无法通过@RequestBody注解来接收(实际情况是我用postman测试可以,swagger调试报错)。

https://stackoverflow.com/questions/34956899/does-spring-requestbody-support-the-get-method
  • PUT和POST是可以兼容使用的,使用方法如下:
@RequestMapping(value = "/xxx", method = {RequestMethod.POST, RequestMethod.PUT})

一、接口分类

使用上面的规则,针对接口进行分类:

查:GET

  • 用户详情

增:POST

  • web端注册
  • app端注册(所需参数更少)

改:PUT

  • 登录
  • 修改密码
  • 用户开关
  • 修改用户信息

二、使用样例

  • GetMapping

GET的参数拼接类似retrofit里的一种写法,使用@PathVariable参数注解,标记url引用

@GetMapping("/userDetail/{id}")
public BaseBean<AppUser> appUserDetail(@PathVariable Integer id) {
    return userService.appUserDetail(id);
}
  • PostMapping
@PostMapping("/register")
public BaseBean<String> register(@RequestBody AppUser record) {
    userService.register(record);
    return BaseBean.success(ResponseCode.SUCCESS);
}
  • PutMapping
@PutMapping("/login")
public BaseBean<AppUser> login(@RequestParam("account") String username, @RequestParam String password) {
    return userService.login(username, password);
}

@RequestParam注解有两个作用,定义如下

  1. 标记参数是否必传,默认必传
  2. 不想暴露内部参数名时,可以提供一个自定义的参数名给调用方
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default true;

    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

三、重点提取

  1. @Service注解,一定要在实现类,不能在service接口层
  2. 必要参数用 @RequestParam 注解标记
  3. 不想暴露的参数使用 @RequestParam 重命名

四、举例错误排查

在调用测试的时候遇到错误,因为mapper层的接口方法还没和mapper.xml中SQL的id同步导致的:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

出现这个问题有三种情况:

  1. mapper命名空间问题
  2. 方法名和id对不上
  3. 对象或者属性对不上
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.a2j.mapper.user.AppUserMapper.findUserByAccount
	at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235)
	at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:53)
	at org.apache.ibatis.binding.MapperProxy.lambda$cachedInvoker$0(MapperProxy.java:108)
	at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
	at org.apache.ibatis.util.MapUtil.computeIfAbsent(MapUtil.java:36)
	at org.apache.ibatis.binding.MapperProxy.cachedInvoker(MapperProxy.java:95)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
	at com.sun.proxy.$Proxy63.findUserByAccount(Unknown Source)
	at com.a2j.service.user.AppUserImpl.register(AppUserImpl.java:31)
	at com.a2j.controller.LoginController.register(LoginController.java:52)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1064)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)