spring源码学习spring的远程调用实现源码分析

Posted 无信不立

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring源码学习spring的远程调用实现源码分析相关的知识,希望对你有一定的参考价值。

【一】spring的远程调用提供的基础类

(1)org.springframework.remoting.support.RemotingSupport

===>spring提供实现的远程调用客户端实现的基础类

===>例子:org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean

      org.springframework.remoting.caucho.HessianProxyFactoryBean

(2)org.springframework.remoting.support.RemoteExporter

===>spring提供实现的远程调用服务端实现的基础类

===>例子:org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter

     org.springframework.remoting.caucho.HessianServiceExporter

 

【二】spring的远程调用基于Http协议实现的封装,以该例子分析远程调用实现原理和源码分析

(1)HttpInvokerProxyFactoryBean  客户端的实现

===>HttpInvokerProxyFactoryBean类是一个FactoryBean的实现接口,注入IOC后,未来向IOC申请bean,其实返回的是getObject()方法返回的实现.在实例化阶段会调用afterPropertiesSet() 进行初始化.根据配置创建代理对象.

===>在getObject()方法中,是完成了一个代理对象的封装.代理增强的配置:serviceUrl和serviceInterface.一个配置的请求url,一个配置的要代理的接口.

===>该代理对象的增强实现就是org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor的invoke(MethodInvocation methodInvocation) 方法.也就是HttpInvokerProxyFactoryBean的父类.将来会作为增强实现,加入到代理对象中.

===>未来发起调用.其实底层是代理对象的拦截器,也就是HttpInvokerClientInterceptor调用invoke方法.将数据类序列化,利用serviceUrl向远程调用接口发送http请求.

 

(2)HttpInvokerServiceExporter  服务端的实现

===>HttpInvokerServiceExporter类是org.springframework.web.HttpRequestHandler的实现类.该类在实例化的时候,会调用afterPropertiesSet()初始化.如果配置有拦截器(即属性Object[] interceptors),则需要为实际调用的facadeImpl创建代理对象.

===>该类将来会作为一个bean加入到org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping中.

===>当客户端发送请求,进入DispatcherServlet中,从beanNameUrlHandlerMapping中获取该bean,再用该bean找到org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.该HttpRequestHandlerAdapter会判断bean是否是HttpRequestHandler的实现类的实例.如果是,调用HttpInvokerServiceExporter类的handleRequest()方法去实现真正facadeImpl的调用

===>在配置HttpInvokerServiceExporter的时候的配置

  >需要配置url,将来作为和客户端的请求地址的匹配.

  >需要配置所代理的接口.

  >需要配置真正的实现类的实例,该实例也可能在实例化bean的时候如果有aop配置,其实也是一个代理对象.这就是多层代理.多层代理在技术层面是允许的.

(3)BeanNameUrlHandlerMapping,HttpRequestHandlerAdapter是如何加载进IOC容器中的?

===>初始化DispatcherServlet的时候,最后会调用initStrategies(ApplicationContext context)方法,内部有初始化的方法,从配置文件里加载,然后编码方式加入IOC容器.从DispatcherServlet.properties文件中获取相应的配置.

===>BeanNameUrlHandlerMapping是ApplicationContextAware 接口的实现类.在IOC容器实例化阶段,会调用setApplicationContext(ApplicationContext context)进行映射配置.

 

【三】以HttpInvoker为例子写一个服务端和客户端

(1)facade接口

技术分享
package com.mobile.thinks.user.facade;

import com.mobile.thinks.user.dto.UserDTO;
/**
 * 接口
 * @author sxf
 *
 */
public interface UserFacade {
    
    public  UserDTO updateUserByUserDTO(UserDTO userDTO);
}
View Code

(2)facade接口的参数

技术分享
package com.mobile.thinks.user.dto;

import java.io.Serializable;

public class UserDTO implements Serializable {

    private String id;
    private String name;
    private String address;
    private int age;
    private String sex;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    
}
View Code

(3)facade实现

技术分享
package com.mobile.thinks.user.facade.impl;

import org.springframework.stereotype.Component;

import com.mobile.thinks.user.dto.UserDTO;
import com.mobile.thinks.user.facade.UserFacade;

@Component(value="userFacade")
public class UserFacadeImpl implements UserFacade{

    @Override
    public UserDTO updateUserByUserDTO(UserDTO userDTO) {
        System.out.println("传过来的参数ID====>"+userDTO.getId());
        System.out.println("传过来的参数Name===>"+userDTO.getName());
        System.out.println("传过来的参数Address===>"+userDTO.getAddress());
        System.out.println("传过来的参数Age===>"+userDTO.getAge());
        System.out.println("传过来的参数Sex===>"+userDTO.getSex());
        
        UserDTO dto=new UserDTO();
        dto.setId("abcdefghijklmnopqrstuvwxyz");
        dto.setName("尚晓飞");
        dto.setSex("男");
        dto.setAge(28);
        dto.setAddress("三门峡");
        return dto;
    }
    
    

}
View Code

(4)客户端配置实现

技术分享
<bean id="userFacade" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
              <property name="serviceUrl"><value>http://localhost:8080/thinks-webservice/http/userFacade</value></property>
              <property name="serviceInterface"><value>com.mobile.thinks.user.facade.UserFacade</value></property>
          </bean>
View Code

(5)服务端配置实现

技术分享
 <bean name="/http/userFacade" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="serviceInterface"><value>com.mobile.thinks.user.facade.UserFacade</value></property>
        <property name="service" ref="userFacade" />
    </bean>
View Code

 

以上是关于spring源码学习spring的远程调用实现源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Spring 源码学习系列ApplicationContextAware#setApplicationContext 方法的调用时机

Spring 源码学习系列BeanNameAware#setBeanName 方法的调用时机

spring源码学习spring的AOP面向切面编程的实现解析

Spring 源码学习系列BeanNameAware#setBeanName 方法的调用时机

2Spring 源码学习 ~ Spring 容器的基本实现

2Spring 源码学习 ~ Spring 容器的基本实现