美文网首页
Springboot 之 自定义方法参数解析器

Springboot 之 自定义方法参数解析器

作者: 小马将过河 | 来源:发表于2018-08-03 16:45 被阅读0次
    120495-16c6f8d2dc1aeb9b.jpg
    >>>我的博客<<<

    昨天(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 —— 消失的事务基本上讲明白了。

    完美~~~

    相关文章

      网友评论

          本文标题:Springboot 之 自定义方法参数解析器

          本文链接:https://www.haomeiwen.com/subject/nlnyvftx.html