返回 登录
0

Spring4 Retrofit2 整合

阅读1089

Spring4 Retrofit2 整合

组件结构

Created with Raphaël 2.1.0开始spring实例化bean后实例化带有HttpService注解的类(实际为retrofit动态代理类)结束

实现

1.构造retrofit工厂用于创建服务

Retrofit LoggingInterceptor日期拦截器

public class LoggingInterceptor implements Interceptor {
    private static Logger logger = Logger.getLogger(LoggingInterceptor.class);

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        logger.info(String.format("发送请求:%s%n请求头:%s",
                request.url(), request.headers()));
        Response response = chain.proceed(request);
        return response;
    }
}

Retrofit FastJson转换器

public class FastJsonConverterFactory extends Converter.Factory{
    public static FastJsonConverterFactory create() {
        return new FastJsonConverterFactory();
    }

    /**
     * 需要重写父类中responseBodyConverter,该方法用来转换服务器返回数据
     */
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return new FastJsonResponseBodyConverter<>(type);
    }

    /**
     * 需要重写父类中responseBodyConverter,该方法用来转换发送给服务器的数据
     */
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        return new FastJsonRequestBodyConverter<>();
    }
}
public class FastJsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private final Type type;

    public FastJsonResponseBodyConverter(Type type) {
        this.type = type;
    }

    /*
    * 转换方法
    */
    @Override
    public T convert(ResponseBody value) throws IOException {
        BufferedSource bufferedSource = Okio.buffer(value.source());
        String tempStr = bufferedSource.readUtf8();
        bufferedSource.close();
        try {
            return JSON.parseObject(tempStr, type);
        }catch (JSONException e){
            return (T) tempStr;
        }

    }
}
public class FastJsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");

    @Override
    public RequestBody convert(T value) throws IOException {
        return RequestBody.create(MEDIA_TYPE, JSON.toJSONBytes(value));
    }
}

工厂生产的实体类

public class RetrofitBean {
    private Retrofit retrofit;
    private Map<Class,Object> service = new HashMap<>();

    public Retrofit getRetrofit() {
        return retrofit;
    }

    public void setRetrofit(Retrofit retrofit) {
        this.retrofit = retrofit;
    }

    public Map<Class, Object> getService() {
        return service;
    }

    public void setService(Map<Class, Object> service) {
        this.service = service;
    }
}

工厂类

public class RetrofitBeanFactory {
    //key:请求地址 value:当前请求地址下class所对应的service(key:class value:service)
    private static Map<String, RetrofitBean> resolvableDependencies = new HashMap(16);
    private static final int readTimeOut = 15;
    private static final int writeTimeOut = 15;
    private static final int connTimeOut = 15;

    /**
     * 获得service服务实体
     *
     * @param requiredType
     * @return
     * @throws BeansException
     */
    public static Object getBean(Class requiredType) throws BeansException {
        for (Map.Entry<String, RetrofitBean> entrySet : resolvableDependencies.entrySet()) {
            RetrofitBean retrofitBean = entrySet.getValue();
            for (Map.Entry<Class, Object> serviceSet : retrofitBean.getService().entrySet()) {
                if (requiredType == serviceSet.getKey()) {
                    return serviceSet.getValue();
                }
            }
        }
        return null;
    }

    /**
     * 创建service服务实体
     *
     * @param baseUrl
     * @param serviceClass
     */
    public static Object putBean(String baseUrl, Class serviceClass, Class... interceptorClass) {
        if (StringUtils.isEmpty(baseUrl)) {
            return null;
        }
        RetrofitBean retrofitBean = resolvableDependencies.get(baseUrl);
        //如果为空设置一个
        if (retrofitBean == null) {
            retrofitBean = new RetrofitBean();
            OkHttpClient.Builder clientBuilder = new OkHttpClient().newBuilder()
                    .connectTimeout(readTimeOut, TimeUnit.SECONDS)
                    .writeTimeout(writeTimeOut, TimeUnit.SECONDS)
                    .readTimeout(connTimeOut, TimeUnit.SECONDS)
                    .addInterceptor(new LoggingInterceptor());
            if (interceptorClass != null && interceptorClass.length > 0) {
                for (Class clazz : interceptorClass) {
                    if (Interceptor.class.isAssignableFrom(clazz)) {
                        try {
                            clientBuilder.addInterceptor((Interceptor) clazz.newInstance());
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(clientBuilder.build())
                    .addConverterFactory(FastJsonConverterFactory.create())
                    .build();
            retrofitBean.setRetrofit(retrofit);
            resolvableDependencies.put(baseUrl, retrofitBean);
        }
        Retrofit retrofit = retrofitBean.getRetrofit();
        Object bean = retrofit.create(serviceClass);
        retrofitBean.getService().put(serviceClass, bean);
        return bean;
    }

    public static Map<String, RetrofitBean> getResolvableDependencies() {
        return resolvableDependencies;
    }
}

2.扩展spring生命周期

自定义配置文件读取工具

public class CustomPropertyPlaceholderConfigurer extends
        PropertyPlaceholderConfigurer {
    private Properties props;

    @Override
    protected void processProperties(
            ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {
        super.processProperties(beanFactory, props);
        this.props = props;
    }

    public Object getProperty(String key) {
        return props.get(key);
    }
}

自定义注解

/**
 * Created by zhangbowen on 2016/5/12.
 * 用于标识服务接口类
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface HttpApi {
    String value() default "";//通过key获得配置文件中的值
    Class[] interceptor() default {};
}
/**
 * Created by zhangbowen on 2016/5/12.
 * 用于自动注入服务
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface HttpService {
}

spring注入实体后的回调方法

public class RetrofitAutowiredProcessor extends InstantiationAwareBeanPostProcessorAdapter {
    private Logger logger = Logger.getLogger(RetrofitAutowiredProcessor.class);
    @Autowired
    private CustomPropertyPlaceholderConfigurer propertyConfigurer;

    @Override
    public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                HttpService httpApi = field.getAnnotation(HttpService.class);
                if (httpApi == null) {
                    return;
                }
                createRetrofitService(bean, field, field.getType());
            }
        });
        return super.postProcessAfterInstantiation(bean, beanName);
    }


    private void createRetrofitService(Object bean, Field field, Class clazz) throws IllegalAccessException {
        //读取注解中的值
        HttpApi httpApi = (HttpApi) clazz.getAnnotation(HttpApi.class);
        String key = httpApi.value();
        if (StringUtils.isBlank(key)) {
            return;
        }
        //根据注解的key获得配置文件中的url
        Object value = propertyConfigurer.getProperty(key);
        if (value == null) {
            return;
        }
        //根据地址创建retrofit
        Object object = RetrofitBeanFactory.putBean(value.toString(), clazz,httpApi.interceptor());
        if (object == null) {
            return;
        }
        field.setAccessible(true);
        field.set(bean, object);
    }
}

3.在spring.config中注册组件

<!--注册加载retrofit服务的processor 只能在非controller中使用-->
    <bean id="retrofitProcessor" class="com.miyan.common.modules.retrofit.processor.RetrofitAutowiredProcessor"></bean>

4. 使用

Maven

Spring的就不再引入,建议使用Spring4,引入retrofit2框架

 <dependency>
      <groupId>com.squareup.retrofit2</groupId>
      <artifactId>retrofit</artifactId>
      <version>2.0.2</version>
 </dependency>

properties

定义服务的地址

IM_BASE_URL=https://a1.easemob.com/huapingxin2015/miyan/

创建retrofit服务接口

//IM_BASE_URL在配置文件中配置
//interceptor 为可选参数,与retrofit拦截器相同,实现拦截http
@HttpApi(value = "IM_BASE_URL", interceptor = {ImInterceptor.class})
public interface IMApi {
 /**
     * 修改群组信息
     *
     * @param updateGroupInfoBean
     * @param groupId
     * @return
     */
    @PUT("chatgroups/{group_id}")
    Call<String> updateGroup(@Body UpdateGroupInfoBean updateGroupInfoBean, @Path("group_id") String groupId);
}

ImInterceptor

/**
 * Created by zhangbowen on 2016/5/17.
 * 环信公用请求头拦截器
 */
public class ImInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        String token = CacheFacade.getObject(Constants.IM.TOKEN);
        Request request = originalRequest
                .newBuilder()
                .addHeader(Constants.Http.AUTHORIZATION, "Bearer " + token)
                .build();
        return chain.proceed(request);
    }
}

在Service层中使用

/**
 * Created by zhangbowen on 2016/1/21.
 * 环信及时通讯服务
 */
@Service
public class IMService {
    @HttpService
    private IMApi imApi;
      /**
     * 修改群组信息
     *
     * @param updateGroupInfoBean
     * @param groupId
     */
        public void updateGroupInfo(UpdateGroupInfoBean updateGroupInfoBean, String groupId) {
            try {
                imApi.updateGroup(updateGroupInfoBean, groupId).execute();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}

5.小结

仅仅是将retrofit整合进spring,并没有扩展retortfit功能,通过BeanPostProcessor的子实现类InstantiationAwareBeanPostProcessor的生命周期postProcessAfterInstantiation实现,其中用到的拦截器,定义服务接口中的注解以及请求都是retorfit2.0中提供的。retrofit2.0是Android中比较流行的网络框架,是对okhttp的再次封装
与spring整合后,web应用中调用三方接口的网络请求会简单清晰很多,比自己封装的网络框架好很多
附retorfit github:
http://square.github.io/retrofit/

评论