>>>我的博客<<<
昨天(2018-08-01)我同事Damon在分享Spring AOP注解失效的主题上,顺带着介绍了下《自定义方法参数解析器》的使用,个人觉得还挺有用的,再次记录一下,以后用的时候好查。
场景
一个业务场景,最近微信小程序如日中天,大有一发不可收拾之局势,我司新接的一些业务也是趁热打铁以微信小程序的形式公诸于世,因此也有了这个场景。
与之前玩APP不一样了,之前的app用户注册,基本上都是手机号+验证码的形式,也就是说用户上来都有手机号登录的过程,登陆以后放回token,访问替他页面会带着这个token,后台也就通过token识别出这个用户是谁了。
现在小程序,自然是微信登录最方便,而且不用麻烦用户再绑定手机号,那么就会有一个用户唯一标识的问题,这就是微信提供的openId,我们现在的做法就是在所有要请求的接口上在head里都带着这个openId,后台通过这个openId检查是新用户还是老用户,用户的信息也就相应的拿到了。
业务处理
举个例子,前端请求是这样的:
image.png
1、各自接口分别处理
在需要判读当前用户是谁的接口逻辑里分别加上,本次实验,放在controller层,实际写代码时下面的逻辑应该在service代码里实现。
@GetMapping("/public/api/demo1")
public Object getUser1(HttpServletRequest request) {
String mobile = request.getHeader("openId");
CurrentUser currentUser = (CurrentUser) redisService.find(mobile);
if(currentUser == null){
List<HaierUser> userList = userRepository.findByMobileAndUserState(mobile, "1");
if(userList.size() > 0 ){
currentUser = new CurrentUser();
BeanUtils.copyProperties(userList.get(0), currentUser);
redisService.add(mobile, currentUser);
}
}
return new RtnResult<>(currentUser);
}
结果:
image.png
2、自定义方法参数解析器实现
先定义一个方法参数解析器
import com.hczt.haier.db.user.entity.HaierUser;
import com.hczt.haier.db.user.repository.HaierUserRepository;
import com.hczt.haier.mobileoffice.usercenter.service.api.RedisService;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* 精诚所至,金石为开。
* 石の上にも三年;陽気の発する所金石亦透る。
* Faith moves mountains.
*
* @author 马海强
* @create 2018-08-03 14:38
* @desc 方法参数解析器
* 使用
**/
@Component
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
@Resource
private RedisService redisService;
@Resource
private HaierUserRepository userRepository;
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType().equals(CurrentUser.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
String openId = request.getHeader("openId");
CurrentUser currentUser = (CurrentUser) redisService.find(openId);
if(currentUser == null){
List<HaierUser> userList = userRepository.findByMobileAndUserState(openId, "1");
if(userList.size() > 0 ){
currentUser = new CurrentUser();
BeanUtils.copyProperties(userList.get(0), currentUser);
redisService.add(openId, currentUser);
}
}
return currentUser;
}
}
别忘了要将自定义的参数解析器配置到spring容器中,新建一个config类,继承WebMvcConfigurationSupport
,,内容如下:
import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import javax.annotation.Resource;
import java.util.List;
/**
* 精诚所至,金石为开。
* 石の上にも三年;陽気の発する所金石亦透る。
* Faith moves mountains.
*
* @author 马海强
* @create 2018-08-03 16:23
* @desc ${DESCRIPTION}
**/
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Resource
private CurrentUserArgumentResolver currentUserArgumentResolver;
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentUserArgumentResolver);
// 注册Spring data jpa pageable的参数分解器
argumentResolvers.add(new PageableHandlerMethodArgumentResolver());
}
// swagger2 访问
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
}
然后访问的controller里接收current这个参数,将变得如此简单:
@GetMapping("/public/api/demo2")
public Object getUser2(CurrentUser currentUser) {
System.out.println(currentUser);
return new RtnResult<>(currentUser);
}
检测结果
image.png
贴上Current类:
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
*
* @Auther: marvin.mma
* @Date: 2018/8/3 14:38
* @return
*/
@Data
@ToString
public class CurrentUser implements Serializable{
private static final long serialVersionUID = 1L;
private String userId;
/**
* 手机号
*/
private String mobile;
/**
* 邮箱
*/
private String mail;
/**
* 用户工牌号
*/
private String cardNo;
/**
* 用户姓名
*/
private String fullName;
/**
* 用户姓名拼音
*/
private String fullNamePinyin;
}
注意事项
1、为什么不用拦截器或者过滤器?
拦截器或者过滤器都能实现获取request的header的openId,也能校验redis和数据库中有没有该数据,没有的话新建到数据库或者redis中,但是就是返回一个对象实现起来麻烦一些。上重点了,最主要是不是每个接口都需要根据openId获取用户信息,这样的话拦截器就有点多余了,配置哪些需要哪些不需要又太麻烦。所以使用方法参数解析器最合适了。
2、自定的方法参数解析器要创建一个继承WebMvcConfigurationSupport
的配置类,然后添加到解析器中,否则是不会生效的。
3、在使用spring-data-jpa的框架里,因为使用了自定义解析器,导致了自带分分页不好使的问题,其原因是自定义解析器以后,往 HandlerMethodArgumentResolver
里面添加了什么解析器,容器就有了什么解析器,没添加的不会默认添加了,因此需要手动添加,就是下面这样的意思
import java.io.IOException;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
/**
*
* @Auther: marvin.mma
* @Date: 2018/8/3 16:38
* @return
*/
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
// 注册Spring data jpa pageable的参数分解器
argumentResolvers.add(new PageableHandlerMethodArgumentResolver());
}
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
/**
* 序列换成json时,将所有的long变成string
* 因为js中得数字类型不能包含所有的java long值
*/
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
// 将返回值null处理成空值
SerializerProvider serializerProvider = objectMapper.getSerializerProvider();
serializerProvider.setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString("");
}
});
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
converters.add(new StringHttpMessageConverter());
// super.configureMessageConverters(converters);
}
}
该配置文件增加了两个json转换处理,顺便贴出来,知道在哪加怎么加就行,跟本文无关。
4、此处是以手机号举例的,实际生产中,视情况而定。
5、关于注解不生效的问题,Damon主要以Asyc和Transactional为例说明的,但是就其不生效的原因,
玩转Spring —— 消失的事务基本上讲明白了。
完美~~~
网友评论