详解Java是如何通过接口来创建代理并进行http请求

场景

现在想要做这么一个事情,公司的dubbo服务都是内网的,但是提供了一个对外的出口,通过链接就能请求到对应的dubbo服务。(具体怎么做的应该就是个网关,然后将http请求转为dubbo请求,通过泛化调用去进行调用。代码看不到。)现在为了方便测试,我需要将配置的接口,通过http请求去请求对应的链接。

分析

项目的思想其实跟mybatis-spring整合包的思想差不多,都是生成代理去执行接口方法。
https://www.jb51.net/article/153378.htm
项目是个简单的spring项目就行了,然后项目引入项目的api,然后通过配置对应的服务名称,通过spring生成代理,注入spring容器,然后执行方法就是根据对应的域名+接口全路径+方法名去进行请求,参数是json。为了方便项目使用了hutool工具类,直接使用fastjson去进行序列化。

操作

首先创建工厂bean,就是用来返回代理的FactoryBean

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Proxy;

/**
 * @Title:相对于BeanFactory这个大工厂,这是一个小工厂,专门用于创建某种类型的bean(默认创建的是单例bean)
 * @Description 创建代理对象
 * @Version
 */
public class HttpProxyFactoryBean<T> implements FactoryBean<T> {

    /**
     *【注意】
     * 这里之所以可以进行自动装配,是因为当前的这个HttpProxyFactoryBean是会被注册到Spring中的
     * 只不过它的注册方式 跟一般的不一样(一般会在类上,加一个如@Component、@Service这样的注解 )
     * 它是通过注册BeanDefinition的方式注册的,可能会注册多个,而其中的每一个HttpProxyFactoryBean实例都会被自动装配同一个HttpProxyInvocationHandler实例
     *
     * 也有等价的做法是:
     * 利用ApplicationContextAware接口的setApplicationContext获取到applicationContext,
     * 然后把applicationContext 作为属性设置到当前类中
     *  再利用applicationContext的getBean方法来获取InvocationHandler的实例
     */
    @Autowired
    private HttpProxyInvocationHandler httpProxyInvocationHandler;

    private Class<T> rpcInterface;

    public HttpProxyFactoryBean(Class<T> rpcInterface){
        this.rpcInterface = rpcInterface;
    }

    @Override
    public T getObject() throws Exception {
        //这里应该放ComputerService接口
        return (T)Proxy.newProxyInstance(rpcInterface.getClassLoader(),new Class[]{rpcInterface} ,httpProxyInvocationHandler);
    }

    @Override
    public Class<?> getObjectType() {
        return rpcInterface;
    }

}

每一个动态代理类都必须要实现InvocationHandler这个接口

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

我们可以直接将实现InvocationHandler的实现类注入spring容器中,然后每一个接口走同一个innvoke方法,当然也可以每一个都new一个,然后可以在构造方法中塞入特定的一些参数。我这边因为对应的每一个代理没啥特殊的就走同一个了:
定义一些参数,请求的urlproxy.serverUrl,和请求添加的项目,proxy.project

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 把服务方法的调用转换为对远程服务的http请求
 * @Version
 */
@Component
public class HttpProxyInvocationHandler implements InvocationHandler {

    @Value("${proxy.serverUrl}")
    private String serverUrl;

    @Value("${proxy.project}")
    private String serverProject;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Class<?> declaringClass = method.getDeclaringClass();
        if (Object.class.equals(declaringClass)) {
            return method.invoke(this, args);
        }

        String methodName = method.getName();
        String name = method.getDeclaringClass().getName();

        //拼接请求地址
        String url = serverUrl + name + "/" + methodName;
//        String url = "http://test:8080/soa/com.rdd.TestService/createActivity";
        HashMap<String, String> paramMap = new HashMap<>();

//        String result = HttpRequest.post(url).headerMap(paramMap, true).body("[" + JSONObject.toJSONString(args) + "]").execute().body();
        String result = HttpRequest.post(url).headerMap(paramMap, true).body(JSONObject.toJSONString(args)).execute().body();

        System.out.println(">>>" + url + "的响应结果为:" + result);

        //将响应结果转换为接口方法的返回值类型
        Class<?> returnType = method.getReturnType();
        if (returnType.isPrimitive() || String.class.isAssignableFrom(returnType)) {
            if (returnType == int.class || returnType == Integer.class) {
                return Integer.valueOf(result);
            } else if (returnType == long.class || returnType == Long.class) {
                return Long.valueOf(result);
            }
            return result;
        } else if (Collection.class.isAssignableFrom(returnType)) {
            return JSONArray.parseArray(result, Object.class);
        } else if (Map.class.isAssignableFrom(returnType)) {
            return JSON.parseObject(result, Map.class);
        } else {
            return JSONObject.parseObject(result, returnType);
        }
    }
}

最后后将对应的工厂bean封装成bean定义,注入到spring容器中

我们的接口一般都是jar形式的,我就简单的写在一个proxy.txt文件中,然后去读取对应的接口全路径,注入到spring容器中,当然也可以通过扫描某个包,自定义注解等等方式实现。

import cn.hutool.core.io.file.FileReader;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Title:bean工厂的后置处理器,用于动态注册bean
 * @Date 2021/3/23 10:13
 * @Description
 * @Version
 */
@Component
@PropertySource("classpath:application.properties")
public class HttpProxyRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * 该方法用来注册更多的bean到spring容器中
     *
     * @param beanDefinitionRegistry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        //默认UTF-8编码,可以在构造中传入第二个参数做为编码
        FileReader fileReader = new FileReader("proxy.txt");
        List<String> classStrList = fileReader.readLines();
        Set<Class<?>> proxyClazzSet = new HashSet<>();
        for (String s : classStrList) {
            if (StringUtils.isBlank(s)) {
                continue;
            }
            try {
                Class<?> aClass = Class.forName(s);
                proxyClazzSet.add(aClass);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        for (Class<?> targetClazz : proxyClazzSet) {
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
            //设置构造方法的参数  对于Class<?>,既可以设置为Class,也可以传Class的完全类名
            //definition.getConstructorArgumentValues().addGenericArgumentValue(targetClazz);
            definition.getConstructorArgumentValues().addGenericArgumentValue(targetClazz.getName());

            //Bean的类型,指定为某个代理接口的类型
            definition.setBeanClass(HttpProxyFactoryBean.class);
            //表示 根据代理接口的类型来自动装配
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            beanDefinitionRegistry.registerBeanDefinition(targetClazz.getName(),definition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

到此这篇关于详解Java是如何通过接口来创建代理并进行http请求的文章就介绍到这了,更多相关java创建代理进行http请求内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java如何发起http请求的实现(GET/POST)

    前言 在未来做项目中,一些功能模块可能会采用不同的语言进行编写.这就需要http请求进行模块的调用.那么下面,我将以Java为例,详细说明如何发起http请求. 一.GET与POST GET和POST是HTTP的两个常用方法. GET指从指定的服务器中获取数据 POST指提交数据给指定的服务器处理 1.GET方法 使用GET方法,需要传递的参数被附加在URL地址后面一起发送到服务器. 例如:http://121.41.111.94/submit?name=zxy&age=21 特点: GET请求

  • 在java中http请求带cookie的例子

    如下所示: String urlPath = "你的请求链接"; String cookie = "要发送的cookie"; URL url = new URL(urlPath); URLConnection conn = url.openConnection(); conn.setRequestProperty("Cookie", cookie); conn.setDoInput(true); BufferedReader br = new B

  • 解决Java处理HTTP请求超时的问题

    在发送POST或GET请求时,返回超时异常处理办法: 捕获 SocketTimeoutException | ConnectTimeoutException | ConnectionPoolTimeout 异常 三种异常说明: SocketTimeoutException:是Java包下抛出的异常,这定义了Socket读数据的超时时间,即从server获取响应数据须要等待的时间:当读取或者接收Socket超时会抛出SocketTimeoutException. ConnectTimeoutExc

  • Java Http请求传json数据乱码问题的解决

    业务场景:调easyui的dialog打开一个弹窗,传参是用json封装的,而且有中文,然后在极速模式是正常的,在ie11测试发现中文出现乱码了 var params = JSON.stringify(writParamList); top.dialog({ id: 'noticeList', title:'列表', width:900, height:500, url:'${root}/notice/multiNoticeList.do?params='+params, onclose:fun

  • 详解Java发送HTTP请求

    前言 请求http的Demo是个人亲测过,目前该方式已经在线上运行着.因为是http请求,所有发送post 和get 请求的demo都有在下方贴出,包括怎么测试,大家可直接 copy到自己的项目中使用. 正文 使用须知 为了避免大家引错包我把依赖和涉及到包路径给大家 import java.net.HttpURLConnection; import java.net.URI; import org.apache.http.HttpResponse; import org.apache.http.

  • Java发起http请求的完整步骤记录

    前言 在未来做项目中,一些功能模块可能会采用不同的语言进行编写.这就需要http请求进行模块的调用.那么下面,我将以Java为例,详细说明如何发起http请求. 一.GET与POST GET和POST是HTTP的两个常用方法. GET指从指定的服务器中获取数据 POST指提交数据给指定的服务器处理 1.GET方法 使用GET方法,需要传递的参数被附加在URL地址后面一起发送到服务器. 例如:http://121.41.111.94/submit?name=zxy&age=21 特点: GET请求

  • Java http请求封装工具类代码实例

    java实现http请求的方法常用有两种,一种则是通过java自带的标准类HttpURLConnection去实现,另一种是通过apache的httpclient去实现. 本文用httpclient去实现,需要导入httpclient和httpcore两个jar包,测试时用的httpclient-4.5.1和httpcore-4.4.3. HttpMethod.java package demo; public enum HttpMethod { GET, POST; } HttpHeader.

  • 详解Java是如何通过接口来创建代理并进行http请求

    场景 现在想要做这么一个事情,公司的dubbo服务都是内网的,但是提供了一个对外的出口,通过链接就能请求到对应的dubbo服务.(具体怎么做的应该就是个网关,然后将http请求转为dubbo请求,通过泛化调用去进行调用.代码看不到.)现在为了方便测试,我需要将配置的接口,通过http请求去请求对应的链接. 分析 项目的思想其实跟mybatis-spring整合包的思想差不多,都是生成代理去执行接口方法. https://www.jb51.net/article/153378.htm 项目是个简单

  • 详解Java 中的 AutoCloseable 接口

    一.前言 最近用到了 JDK 7 中的新特性 try-with-resources 语法,感觉到代码相对简洁了很多,于是花了点时间详细学习了下,下面分享给大家我的学习成果. 二.简单了解并使用 try-with-resources语法比较容易使用,一般随便搜索看下示例代码就能用起来了.JDK 对这个语法的支持是为了更好的管理资源,准确说是资源的释放. 当一个资源类实现了该接口close方法,在使用try-with-resources语法创建的资源抛出异常后,JVM会自动调用close 方法进行资

  • 详解Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制.通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以,这是一种动态获取类的信息以及动态调用对象方法的能力. 想要使用反射机制,就必须要先获取到该类

  • 详解JAVA中的Collection接口和其主要实现的类

    Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些Collection允许相同的元素而另一些不行.一些能排序而另一些不行.Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的"子接口"如List和Set,详细信息可见官方文档http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/

  • 详解java集成支付宝支付接口(JSP+支付宝20160912)

    吐槽一下: 支付宝的接口和微信的DEMO和文档真心太难看懂了,乱七八糟,都不知道去哪里找自己要的东西,最近几天我们公司需要做类似的开发,我作为先锋,率先解决Java集成支付宝支付和微信支付接口工作. 我们的工作环境:JSP网站+支付接口,目前工作的支付宝接口为20160912,微信为V3版本,如遇到版本升级,请联系相关机构的客户服务人员升级. 本文介绍JSP+支付宝接口,本文非原创. 新手注意: 1.本文使用的接口地址和参数为沙箱的地址,无论你在使用沙箱或者正式地址,务必核实使用接口地址和参数,

  • 详解Java 中的函数式接口

    目录 @FunctionalInterface注解 最简单的函数式接口 基础数据类型的函数表达式 二元输入参数的函数 Two-Arity Function Specializations Suppliers 供给型接口 & Consumers 消费型接口 Predicates 断言型接口 Operators 总结 @FunctionalInterface注解 如果你想自己定义个新的函数式接口,强烈建议你加上*@FunctionalInterface* 注解.可以更好地揭示我们定义这个接口的意思,

  • 详解Java的内置异常以及创建自定义异常子类的方法

    内置异常子类 在标准包java.lang中,Java定义了若干个异常类.前面的例子曾用到其中一些.这些异常一般是标准类RuntimeException的子类.因为java.lang实际上被所有的Java程序引入,多数从RuntimeException派生的异常都自动可用.而且,它们不需要被包含在任何方法的throws列表中.Java语言中,这被叫做未经检查的异常(unchecked exceptions ).因为编译器不检查它来看一个方法是否处理或抛出了这些异常. java.lang中定义的未经

  • 详解Java中Comparable和Comparator接口的区别

    详解Java中Comparable和Comparator接口的区别 本文要来详细分析一下Java中Comparable和Comparator接口的区别,两者都有比较的功能,那么究竟有什么区别呢,感兴趣的Java开发者继续看下去吧. Comparable 简介 Comparable 是排序接口. 若一个类实现了Comparable接口,就意味着"该类支持排序".  即然实现Comparable接口的类支持排序,假设现在存在"实现Comparable接口的类的对象的List列表(

  • 详解java中接口与抽象类的区别

    详解java中接口与抽象类的区别 1.abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系.但是,一个类却可以实现多个interface. 2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的. 3.abstract c

  • 详解Java Callable接口实现多线程的方式

    在Java 1.5以前,创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口.无论我们以怎样的形式实现多线程,都需要调用Thread类中的start方法去向操作系统请求io,cup等资源.因为线程run方法没有返回值,如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. Callable和Future介

随机推荐