spring技术内幕17-Spring HTTP调用器实现远程调用

来源:互联网 发布:mac怎么循环播放视频 编辑:程序博客网 时间:2024/06/05 06:23

1、Spring中,HTTPInvoker(HTTP调用器)是通过基于HTTP协议的分布式远程调用解决方案,和java RMI一样,HTTP调用器也需要使用java的对象序列化机制完成客户端和服务器端的通信。HTTP调用器的远程调用工作原理如下:

(1)客户端:

a、向服务器发送远程调用请求:

远程调用信息——>封装为远程调用对象——>序列化写入到远程调用HTTP请求中——>向服务器端发送。

b、接收服务器端返回的远程调用结果:

服务器端返回的远程调用结果HTTP响应——>反序列化为远程调用结果对象。

(2)服务器端:

a、接收客户端发送的远程调用请求:

客户端发送的远程调用HTTP请求——>反序列化为远程调用对象——>调用服务器端目标对象的目标方法处理。

b、向客户端返回远程调用结果:

服务器端目标对象方法的处理结果——>序列化写入远程调用结果HTTP响应中——>返回给客户端。

接下来我们将从客户端和服务器端分别分析HTTP调用器远程调用的具体实现。

2、HTTP调用器客户端配置:

使用HTTP调用器之前,首先需要对客户端进行如下配置:

<!-- 客户端HTTP调用器代理 -->

<bean id="proxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">

<property name="serviceUrl">

<value>http://yourhost:8080/远程调用URL</value>

</property>

<property name="serviceInterface">

<value>远程调用服务接口路径</value>

</property>

</bean>

<bean id="客户端bean” class="客户端bean全路径">

   <property name="remoteService">

             <ref bean="proxy"/>

    </property>

</bean>

在HTTP调用器客户端代理HttpInvokeProxyFactoryBean中封装远程调用服务URL和服务接口,客户端程序通过HTTP调用代理可以调用实现了指定接口的目标服务端对象。

3、HttpInvokeProxyFactoryBean创建远程调用代理对象:

HTTP调用器客户端代理HttpInvokeProxyFactoryBean是一个实现了Spring FactoryBean接口的IoC容器,其作用是对远程服务客户端封装,源码如下:

public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor implements FactoryBean<Object>{

        //远程对象的代理

        private Object serviceProxy;

        //在IoC容器注入完成之后调用

         public void afterPropertesSet(){

               //调用父类容器的回调方法

                super.afterPropertiesSet();

             //getServiceInterface()方法用于获取配置的远程调用接口

                if(getServiceInterface() == null){

                    throw new IllegalArgumentException("Property 'serviceInterface' is required");

                }

              //使用ProxyFactory代理工厂生成远程代理对象,注意第二个参数this,因为HttpInvokerProxyFactoryBean继承了HttpInvokerClientInterceptor,

             //所以代理对象的拦截器设置为HttpInvokerClientInterceptor

               this.serviceProxy = new ProxyFactory(getServiceInterface(),this).getProxy(getBeanClassLoader());

        }

        //向IoC容器索取被管理对象的方法,获取产生的远程调用代理对象

       public Object getObject(){

               return this.serviceProxy;

       }

      //获取对象类型,返回配置的远程调用接口

      public Class<?> getObjectType(){

             return getServiceInterface();

       }

       //是否是单态类型,默认Spring IoC容器产生的都是单态类型

        public boolean isSingleton(){

              return true;

          }

}

通过上面对HttpInvokerProxyFactoryBean源码的分析我们看到,当通过getObject方法向Spring IoC容器索取远程调用对象是,触发afterPropertiesSet回调方法,创建远程调用的代理对象,最后将该远程调用对象返回。在创建远程调用代理对象时,使用其父类HttpInvokerClientInterceptor作为远程调用代理对象的拦截器,该拦截器将拦截对代理对象的方法调用。下面我们分析HttpInvokerClientInterceptor代理拦截器对代理对象的方法拦截处理。

4、HttpInvokerClientInterceptor拦截对远程调用代理的方法调用:

当客户端通过HTTP请求调用远程调用代理的方法时,将会触发HttpInvokerClientInterceptor拦截器的invoke方法对当前的请求进行封装处理,将客户端的java对象序列化传输到服务器端,在远程服务器端执行完请求之后,又将处理结果java对象序列化返回给客户端。其源码如下:

public class HttpInvokerClientInterceptor extends RemoteInvocationBasedAccessor implements MethodInterceptor,HttpInvokerClientConfiguration {

       private String codebaseUrl;

        //HTTP调用请求执行器

        private HttpInvokerRequestExecutor httpInvokerRequestExecutor;

        public void setCodebaseUrl(String codebaseUrl) {

            this.codebaseUrl = codebaseUrl;

        }

        public String getCodebaseUrl(){

            return this.codebaseUrl;

        }

        public void setHttpInvokerRequestExecutor(HttpInvokerRequestExecutor httpInokerRequestExecutor){

              this.httpInvokerRequestExecutor = httpInvokerRequestExecutor;

        }

        //获取HTTP定义请求执行器,如果HTTP定义请求执行器没有设置,则使用SimpleHttpInvokerRequestExecutor作为HTTP调用请求执行器

        public HttpInvokerRequestExecutor getHttpInvokerRequestExecutor(){

                 if(this.httpInvokerRequestExecutor == null){

                      SimpleHttpInvokerRequestExecutor executor = new SimpleHttpInvokerRequestExecutor();

                       executor.setBeanClassLoader(getBeanClassLoader());

                       this.httpInvokerRequestExecutor = executor;

                }

                return this.httpInvokerRequestExecutor;

        }

        //IoC容器初始化完成回调方法

          public void afterPropertiesSet(){

              //调用父类的初始化回调方法

               super.afterPropertiesSet();

            //获取HTTP调用请求执行器

              getHttpInvokerRequestExecutor();

          }

         //拦截器代理对象方法调用入口,拦截器将客户端对远程调用代理的调用封装为MethodInvocation对象

          public Object invoke(MethodInvocation methodInvocation) throws Throwable {

                if(AopUtis.isToStringMethod(methodInvocation.getMethod())){

                      return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]";

                 }

                //创建远程调用对象,封装了远程调用

                RemoteInvocation invocation = createRemoteInvocation(methodInvocation);

               //远程调用结果

                RemoteInvocationResult result = null;

                try{

                     //远程调用入口

                      result = executeRequest(invocation,methodInvocation);

                 }

                 catch(Throwable ex){

                      throw converHttpInvokerAccessException(ex);

                }

               try{

                    //返回远程调用结果

                    return recreateRemoteInvocationResult(result);

                }

               catch(Throwable ex){

                      if(result.hasInvocationTargetException()){

                              throw ex;

                      }

               else{

                   throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +

                                   "] failed in HTTP invoker remote service at [ " + getServiceUrl() + "] ", ex);

                }

            }

         }

        //执行远程调用入口

         protected RemoteInvocationResult executeRequest(RemoteInvocation invocation,MethodInvocation originalInvocation) throws Exception {

                      return executeRequest(invocation);

          }

          //通过HTTP调用请求执行器执行远程调用

         protected RemoteInvocationResult executeRequest(RemoteInvocation invocation) throws Exception {

               return getHttpInvokerRequestExecutor().executeRequest(this,invocation);

         }

        //将远程调用异常转换成Spring异常

          protected RemoteAccessException convertHttpInvokerAccessException(Throwable ex){

                  if(ex instanceof ConnectException){

                       throw new RemoteConnectFailureException("Could not connect to HTTP invoker remote service at [" + getServiceUrl() + "]",ex);

             }

             else if(ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError || ex instanceof InvalidClassException){

                      throw new RemoteAccessException("Could not deserialize result from HTTP invoker remote service [" + getServiceUrl() + "]" ,ex);

          }

          else{

                   throw new RemoteAccessException("Could not access HTTP invoker remote service at [" + getServiceUrl() + "]",ex);

           }

      }

  }

通过上面对HttpInvokerClientInterceptor拦截器的源码分析,我们可以看出,拦截器将客户端对远程调用的HTTP请求封装成了MethodInvocation对象,拦截器在调用远程调用的代理对象时,又将方法调用封装成了RemoteInvocation远程调用,RemoteInvocation数据对象中封装了调用的具体信息,如方法名、方法参数以及参数类型等。

真正执行远程调用的是HTTP调用请求执行器SimpleHttpInvokerRequestExecutor,下面我们继续分析SimpleHttpInvokerRequestExecutor远程调用的具体过程。

5、SimpleHttpInvokerRequestExecutor远程调用:

SimpleHttpInvokerRequestExecutor封装了基于HTTP协议的远程调用过程,具体源码如下:

public class SimpleHttpInvokerRequestExecutor extends AbstractHttpInvokerRequestExecutor{

  //HTTP调用请求执行器真正进行远程调用的方法,该方法有由其父类AbstractHttpInvokerRequestExecutor的executeRequest方法调用

   protected RemoteInvocationResult doExecuteRequest(HttpInvokerClientConfiguration config,ByteArrayOutputStream baos) throws IOException,ClassNotFoundException{

        //打开一个标准的J2SE HttpURLConnection

        HttpURLConnection con = openConnection(config);

       //准备连接

        prepareConnection(con,baos.size());

       //远程调用被封装成了RemoteInvocation对象,它通过序列化被写到对应的HttpURLConnection中

        writeRequestBody(config,con,baos);

        //获取远程调用的结果,校验返回的结果

         validateResponse(config,con);

         InputStream responseBody = readResponseBody(config,con);

         //将远程调用结果转换成RemoteInvocationResult返回

         return readRemoteInocationResult(responseBody,config.getCodebaseUrl());

     }

    //打开一个HttpURLConnection

     protoected HttpURLConnection openConnection(HttpInvokerClientConfiguration config) throws IOException {

        //getServiceUrl()方法获取配置的远程调用URL,打开一个URL连接

         URLConnection con = new URL(config.getServiceUrl()).openConnection();

          if(!(con instanceof HttpURLConnection)){

               throw new IOException("Service URL [" + config.getServiceUrl() + "] is not an HTTP URL");

         }

         return (HttpURLConnection) con;

    }

    //准备HTTP请求连接

    protected void prepareConnection(HttpURLConnection con,int contentLength) throws IOException {

            con.setDoOutput(true);

            //HTTP调用器只支持POST请求方法

            con.setRequestMethod(HTTP_METHOD_POST);

          //设置HTTP请求头内容类型,设置为:application/x -java-serialized-object

          con.setRequestProperty(HTTP_HEADER_CONTENT_TYPE,getContentType());

          //设置HTTP请求头内容长度

          con.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH,Integer.toString(contentLength));

          LocalContext local = LocalContextHolder.getLocalContext();

         //设置HTTP请求的Local

          if(local != null){

               con.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE,StringUtils.toLanguageTag(local.getLocal()));

          }

         //设置HTTP请求压缩方式

         if(isAcceptGzipEncoding()){

            con.setRequestProperty(HTTP_HEADER_ACCEPT_ENCODING,ENCODING_GZIP);

         }

    }

    //把序列化对象输出到HTTP请求体中

    protected void writeRequestBody(HttpInvokerClientConfiguration config,HttpURLConnection con,ByteArrayOutputStream baos) throws IOException {

             baos.writeTo(con.getOutputStream());

   }

   //校验远程调用的HTTP响应

   protected void validateResponse(HttpInvokerClientConfiguration config,HttpURLConnection con) throws IOException {

     //如果HTTP响应状态码大于等于300,则证明调用发生错误

     if(con.getResponseCode() >= 300){

         throw new IOException("Did not receive successful HTTP response: status code = " + con.getResponseCode() +

                       ", status message = [" + con.getResponseMessage() + "]");

        }

  }

//提取远程调用结果的HTTP响应信息

protected InputStream readResponseBody(HttpInvokerClientConfiguration config,HttpURLConnection con) throws IOException {

    //如果响应信息是GZIP压缩的,则需要先解压

     if(isGzipResponse(con)){

            return new GZIPInputStream(con.getInputStream());

      }

     //正常的HTTP响应

     else{

            return con.getInputStream();

      }

}

//是否是Gzip格式压缩

protected boolean isGzipResponse(HttpURLConnection con) {

        //获取HTTP响应头信息中的压缩方式

        String encodingHeader = con.getHeaderField(HTTP_HEADER_CONTENT_ENCODING);

         return (encodingHeader != null && encodingHeader.toLowerCase().indexOf(ENCODING_GZIP) != -1);

    }

}

通过对SimpleHttpInvokerRequestExecutor的分析,我们看到,HTTP调用请求执行器的处理逻辑是:首先,打开指定的URL的HTTP连接,设置连接属性。其次,将封装请求的RemoteInvocation对象序列化到请求体中,HTTP请求发送到服务器端。最后,从服务器端的HTTP响应中读取输入流,并将响应结果转换成RemoteInvocationResult。

将远程调用的HTTP响应转换为RemoteInvocationResult是由AbstractHttpInvokerRequestExecutor的readRemoteInvocationResult方法实现,下面我们将分析其将HTTP响应结果转换成RemoteInvocationResult的实现。

6、AbstractHttpInvokerRequestExecutor将HTTP响应结果转换成RemoteInvocationResult:

AbstractHttpInvokerRequestExecutor中处理远程调用结果,并将HTTP响应转换成RemoteInvocationResult的主要方法如下:

//从HTTP响应中读取远程调用结果入口方法

protected RemoteInvocationResult readRemoteInvocationResult(InputStream is,String codebaseUrl) throws IOException,ClassNotFoundException {

      //根据给定的输入流和类创建对象输入流

       ObjectInputStream ois = createObjectInputStream(decorateInputStream(is),codebaseUrl);

       try{

          //从对象输入流中读取远程调用结果

          return doReadRemoteInvocationResult(ois);

       }

       finally{

           ois.close();

       }

  }

//从对象输入流中读取远程调用结果

protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois) throws IOException,ClassNotFoundException {

       //获取对象输入流中的对象

       Object obj = ois.readObject();

       if(!(obj instanceof RemoteInvocationResult)){

             throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocationResult.class.getName() + "]: " + obj);

       }

       //将获取到的对象封装为RemoteInvocationResult

       return (RemoteInvocationResult) obj;

 }

7、HTTP调用器的服务器端配置:

和HTTP调用器客户端类似,服务器端也需要进行如下的配置:

<bean name="/客户端的远程调用URL" class = "org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">

<property name="service">

<ref bean= "服务器端实现bean"/>

</property>

<property name="serviceInterface">

<value>远程调用服务接口全路径</value>

</property>

通过对服务器端配置的列子,我们可以看出,真正处理远程调用的服务器实现是由service属性中指定的服务器端bean提供的,HttpInvokerServiceExporter将远程调用服务接口和服务实现类进行封装,主要提供HTTP协议封装和java对象序列化功能。

Spring的HttpInvokerServiceExporter是与Spring的MVC结合在一起的,它本质上是Spring MVC的一个Controller,客户端发来的远程调用HTTP请求由Spring MVC的中央控制器DispatcherServlet转发到指定的URL的HttpInvokerServiceExporter上。

8、HttpInvokerServiceExporter导出和执行远程调用服务:

HttpInvokerServcieExporter响应客户端发送的远程调用HTTP请求,它从HTTP请求中读取远程调用并将其反序列化为RemoteInvocation对象,然后调用目标服务对象的目标方法完成远程调用服务,当服务执行完成之后,通过HTTP响应把执行结果对象序列化输出到客户端。其源码如下:

public class HttpInvokerServiceExporter extends RemoteInvocationSerializingExporter implements HttpRequestHandler {

    //处理客户端发来的远程调用HTTP请求

   public void handRequest(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {

          try{

              //从HTTP请求中反序列化出RemoteInvocation远程调用对象

               RemoteInvocation invocation = readRemoteInvocation(request);

            //调用目标服务对象,完成远程调用请求,并创建调用结果

              RemoteInvocationResult result = invokeAndCreateResult(invocation,getProxy());

            //将调用结果写到HTTP响应中

              writeRemoteInvocationResult(request,response,result);

      }

     catch(ClassNotFoundException ex){

             throw new NestedServletException("Class not found during deserialization",ex);

     }

}

//从HTTP请求中读取RemoteInvocation远程调用对象入口方法

   protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException,ClassNotFoundException {

           //将从HTTP请求中读取远程调用对象

            return readRemoteInvocation(request,request.getInputStream());

      }

//从HTTP请求中读取远程调用对象

 protected RemoteInvocation readRemoteInvocation(HttpServletRequest request,InputStream is) throws IOException,ClassNotFoundException {

     //根据HTTP请求输入流创建对象输入流

     ObjectInputStream ois = createObjectInputStream(decorateInputStream(request,is));

     try{

           //从对象输入流中读取远程调用对象

          return doReadRemoteInvocation(ois);

       }

       finally{

            ois.close();

        }

   }

  //获取HTTP请求输入流

  protected InputStream decorateInputStream(HttpServletRequest request,InputStream is) throws IOException {

             return is;

  }

//将远程调用执行结果写到HTTP响应中

protected void writeRemoteInvocationRequest(HttpServletRequest request,HttpServletRespose respose,RemoteInvocationResult result) throws IOException {

     //设置HTTP响应的内容类型为:application/x -java-serialized-object

     respose.setContentType(getContentType());

    //将远程调用结果写到HTTP响应中

     writeRemoteInvocationResult(request,response,result,response.getOutputStream());

}

//将远程调用执行结果写入HTTP响应中

protected void writeRemoteInvocationResult(HttpServletRequest request,HttpServeltResponse response,RemoteInvocationResult result,OutputStream os)

          throws IOException {

     //获取HTTP响应对象输出流

     ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(request,response,os));

     try{

        //将远程调用执行结果写到HTTP响应对象输出流中

          doWriteRemoteInvocationResult(result,oos);

      }

     finally{

          oos.close();

      }

}

//获取HTTP响应对象输入流

protected OutputStream decorateOutputStream(HttpServletRequest request,HttpServletRespose response,OutputStream os) throws IOException {

   return os;

   }

}

通过对HttpInvokerServiceExporter的源码分析,我们可以看出,真正执行远程对象调用的是RemoteInvocationResult = invokeAndCreateResult(invocation,getProxy());它调用了RemoteInvocationBasedExporter的invokerAndCreateResult方法调用远程目标对象方法,并创建远程调用执行结果,下面我们继续分析执行服务器端远程调用目标对象方法的实现。

9、RemoteInvocationBasedExporter调用服务器目标对象:

RemoteInvocationBasedExporter的invokeAndCreateResult方法调用服务器目标对象方法,RemoteInvocationBasedExporter源码如下:

public abstract class RemoteInvocationBasedExporter extends RemoteExporter {

    //远程调用执行器

   private RemoteInvocationExecutor remoteInvocationExecutor = new DefaultRemoteInvocationExecutor();

   public RemoteInvocationExecutor getRemoteInvocationExecutor(){

           return this.remoteInvocationExecutor;

   }

   protected RemoteInvocationResult invokeAndCreateResult(RemoteInvocation invocation,Object targetObject) {

        try{

            //调用服务器端目标对象的方法

             Object value = invoke(invocation,targetObject);

            //根据执行结果创建RemoteInvocationResult

              return new RemoteInvocationResult(value);

         }

         catch(Throwable ex){

              return new RemoteInvocationResult(ex);

         }

   }

  //调用目标对象的方法

    protected Object invoke(RemoteInvocation invocation,Object targetObject) throws NoSuchMethodException,IllegalAccessException,InvocationTargetException {

         if(logger.isTraceEnabled()){

                logger.trace("Executing " + invocation);

          }

         try{

               //获取远程调用执行器,由远程调用执行器调用目标对象的方法,即通过DefaultRemoteInvocationExecutor来调用目标对象的方法

                return getRemoteInvocationExecutor().invoke(invocation,targetObject);

           }

          catch(NoSuchMethodException ex){

                 if(logger.isDebugEnabled()){

                        logger.warn("Could not find target method for "+ invocation,ex);

                 }

                 throw ex;

          }

          catch(InvocationTargetException ex){

               if(logger.isDebugEnabled()){

                      logger.debug("Target method failed for " + invocation,ex.getTargetException());

               }

               throw ex;

           }

       }

}

通过上面对RemoteInvocationBasedExporter源码分析我们看到,真正调用目标对象的是DefaultRemoteInvocationExecutor的invoke方法,下面我们继续分析DefaultRemoteInvocationExecutor调用目标对象方法的实现。

10、DefaultRemoteInvocationExecutor调用目标对象的方法实现远程调用:

DefaultRemoteInvocationExecutor用于调用目标对象的指定方法实现远程对象调用服务,其源码如下:

public class DefaultRemoteInvocationExecutor implements RemoteInvocationExecutor {

      //调用目标对象的方法

      public Object invoke(RemoteInvocation invocation,Object targetObject) throws NoSuchMethodException,IllegalAccessException,InvocationTargetException {

           Assert.notNull(invocation,"RemoteInvocation must not be null");

           Assert.notNull(targetObject,"Target object must not be null");

          //调用RemoteInvocation的invoke方法

          return invocation.invoke(targetObject);

       }

}

RemoteInvocation的invoke方法源码如下:

public Object invoke(Object targetObject) throws NoSuchMethodException,IllegalAccessException,InvocationTargetException {

       //获取远程调用对象的方法名称和参数类型

        Method method = targetObject.getClass().getMethod(this.methodName,this.parameterTypes);

      //利用JDK反射机制,调用目标对象指定参数的方法

        return method.invoke(targetObject,this.arguments);

}

 

 

0 0
原创粉丝点击