Spring Boot集成rmi快速入门demo

1.什么是rmi?

RMI(Remote Method Invocation)即远程方法调用,是分布式编程中的一个基本思想。实现远程方法调用的技术有很多,比如CORBA、WebService,这两种都是独立于各个编程语言的。 而Java RMI是专为Java环境设计的远程方法调用机制,是一种用于实现远程调用(RPC,Remote Procedure Call)的Java API,能直接传输序列化后的Java对象和分布式垃圾收集。它的实现依赖于JVM,因此它支持从一个JVM到另一个JVM的调用。 在Java RMI中,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法,其中对象是通过序列化方式进行编码传输的。所以平时说的反序列化漏洞的利用经常是涉及到RMI,就是这个意思。 RMI依赖的通信协议为JRMP(Java Remote Message Protocol,Java远程消息交换协议),该协议是为Java定制的,要求服务端与客户端都必须是Java编写的。

交互过程

rmi_architecture

registry

  交互过程可简单概述为:

  1. 首先,启动RMI Registry服务,启动时可以指定服务监听的端口,也可以使用默认的端口(1099);
  2. 其次,Server端在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming/Context/Registry等类的bind或rebind方法将刚才实例化好的实现类注册到RMI Registry上并对外暴露一个名称;
  3. 最后,Client端通过本地的接口和一个已知的名称(即RMI Registry暴露出的名称),使用RMI提供的Naming/Context/Registry等类的lookup方法从RMI Service那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,便可以实现远程调用对象的方法了;

2.代码工程

实验目标

实验一个简单rmi服务,并且通过客户端调用它

rmi-server

这是一个服务端工程,主要提供rmi service接口

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>rmi</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>rmi-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.et</groupId>
            <artifactId>rmi-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
    </dependencies>
</project>
config
package com.et.rmi.server.config;

import com.et.rmi.server.dao.CustomerRepository;

import com.et.rmi.server.model.Customer;
import com.et.rmi.server.service.CustomerServiceImpl;
import om.et.rmi.common.CustomerService;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.rmi.RmiServiceExporter;
import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
public class RmiServerApplicationRunner implements ApplicationRunner {

    private CustomerRepository repository;
    private CustomerServiceImpl customerService;
    private final Logger log = Logger.getLogger(this.getClass().getName());

    public RmiServerApplicationRunner(CustomerServiceImpl customerService) {
        this.customerService = customerService;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Customer customer1 = new Customer("John", "Smith", "123-456-7890");
        customerService.saveCustomer(customer1);
        customerService.getCustomers().forEach(System.out::println);
    }

    @Bean
    public RmiServiceExporter customerServiceExporter() {
        RmiServiceExporter customerServiceExporter = new RmiServiceExporter();
        customerServiceExporter.setRegistryPort(1199);
        customerServiceExporter.setServiceName("customerService");
        customerServiceExporter.setServiceInterface(CustomerService.class);
        customerServiceExporter.setService(customerService);
        log.info("Started RMI Server");
        return customerServiceExporter;
    }
}
service
package com.et.rmi.server.service;

import com.et.rmi.server.dao.CustomerRepository;

import com.et.rmi.server.mapper.CustomerMapper;
import com.et.rmi.server.model.Customer;
import om.et.rmi.common.CustomerDTO;
import om.et.rmi.common.CustomerService;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CustomerServiceImpl implements CustomerService {

    private CustomerRepository repository;

    public CustomerServiceImpl(CustomerRepository repository) {
        this.repository = repository;
    }

    @Override
    public CustomerDTO getCustomer(long id) {
        Customer customer = repository.findById(id).orElseThrow(IllegalArgumentException::new);
        CustomerMapper mapper = new CustomerMapper();
        CustomerDTO dto = mapper.mapToDTO(customer);
        System.out.println(dto);
        return dto;
    }

    public List<Customer> getCustomers() {
        return (List<Customer>)repository.findAll();
    }
    public void saveCustomer(Customer customer) {
        repository.save(customer);
    }
}
dao
package com.et.rmi.server.dao;

import com.et.rmi.server.model.Customer;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
    Optional<Customer> findById(long id);
}
mapper
package com.et.rmi.server.mapper;

import com.et.rmi.server.model.Customer;

import om.et.rmi.common.CustomerDTO;

public class CustomerMapper {

    public CustomerMapper() {
    }

    public CustomerDTO mapToDTO(Customer customer){
        CustomerDTO dto = new CustomerDTO();
        dto.setFirstName(customer.getFirstName());
        dto.setLastName(customer.getLastName());
        dto.setSocialSecurityCode(customer.getSocialSecurityCode());
        return dto;
    }
}
model
package com.et.rmi.server.model;


import javax.persistence.*;

@Entity
@SequenceGenerator(name = "CUST_SEQ", initialValue = 1_000_001)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUST_SEQ")
    private long id;
    private String firstName;
    private String lastName;
    private String socialSecurityCode;

    public Customer() {
    }

    public Customer(String firstName, String lastName, String socialSecurityCode) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.socialSecurityCode = socialSecurityCode;
    }

    public long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getSocialSecurityCode() {
        return socialSecurityCode;
    }

    public void setSocialSecurityCode(String socialSecurityCode) {
        this.socialSecurityCode = socialSecurityCode;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", socialSecurityCode='" + socialSecurityCode + '\'' +
                '}';
    }
}
application.properties
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

rmi-client

这是一个客户端工程,主要调用远程的rmi服务

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>rmi</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>rmi-cilent</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.et</groupId>
            <artifactId>rmi-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
controller
package com.et.rmi.client.controller;


import om.et.rmi.common.CustomerDTO;
import om.et.rmi.common.CustomerService;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "customers")
public class CustomerController {

    private RmiProxyFactoryBean proxyFactoryBean;

    public CustomerController(RmiProxyFactoryBean proxyFactoryBean) {
        this.proxyFactoryBean = proxyFactoryBean;
    }

    @RequestMapping(value = "{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<CustomerDTO> getCustomer(@PathVariable long id) {
        CustomerService service = (CustomerService) proxyFactoryBean.getObject();
        CustomerDTO dto = service.getCustomer(id);
        return ResponseEntity.ok(dto);
    }
}

config
package com.et.rmi.client.config;

import om.et.rmi.common.CustomerService;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.logging.Logger;

@Component
public class Config {

    public final Logger log = Logger.getLogger(this.getClass().getName());

    @Bean
    public RmiProxyFactoryBean proxyFactoryBean() {
        String remoteHost = System.getProperty("RMI_SERVER_HOST");
        if(StringUtils.isEmpty(remoteHost)){
            remoteHost="127.0.0.1";
        }
        String rmiHost = String.format("rmi://%s:1199/customerService", remoteHost);
        log.info("RMI Host name is " + rmiHost);
        RmiProxyFactoryBean proxy = new RmiProxyFactoryBean();
        proxy.setServiceInterface(CustomerService.class);
        proxy.setServiceUrl(rmiHost);
        proxy.afterPropertiesSet();
        return proxy;
    }
}
application.properties
server.port=8081

rmi-common

这是一个公共包,server和client都要引用

package om.et.rmi.common;

import java.io.Serializable;

public class CustomerDTO implements Serializable {

    private String firstName;
    private String lastName;
    private String socialSecurityCode;

    public CustomerDTO() {
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getSocialSecurityCode() {
        return socialSecurityCode;
    }

    public void setSocialSecurityCode(String socialSecurityCode) {
        this.socialSecurityCode = socialSecurityCode;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("CustomerDTO{");
        sb.append("firstName='").append(firstName).append('\'');
        sb.append(", lastName='").append(lastName).append('\'');
        sb.append(", socialSecurityCode='").append(socialSecurityCode).append('\'');
        sb.append('}');
        return sb.toString();
    }
}
package om.et.rmi.common;

public interface CustomerService {
    CustomerDTO getCustomer(long id);
}

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

3.测试

  • 启动rmi-server服务
  • 启动rmi-client服务
  • 访问http://127.0.0.1:8081/customers/1000001
  • 返回{"firstName":"John","lastName":"Smith","socialSecurityCode":"123-456-7890"}

4.引用

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
A:以下是一个简单的 Spring RPC 示例: 1.创建一个 Maven 项目并添加以下依赖项: ``` <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.8.RELEASE</version> </dependency> </dependencies> ``` 2.创建一个接口: ``` public interface CalculatorService { int add(int a, int b); } ``` 3.创建一个实现类: ``` public class CalculatorServiceImpl implements CalculatorService { @Override public int add(int a, int b) { return a + b; } } ``` 4.在 Spring 配置文件中定义 Bean: ``` <bean id="calculatorService" class="com.example.CalculatorServiceImpl" /> ``` 5.创建一个 Spring Controller: ``` @Controller public class CalculatorController { @Autowired private CalculatorService calculatorService; @RequestMapping(value="/add", method=RequestMethod.POST) @ResponseBody public int add(@RequestParam("a") int a, @RequestParam("b") int b) { return calculatorService.add(a, b); } } ``` 6.在 Spring 配置文件中注册 Controller: ``` <mvc:annotation-driven /> <context:component-scan base-package="com.example" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/" /> <property name="suffix" value=".jsp" /> </bean> ``` 7.对外提供 RPC 服务,使用 RMI 或 Hessian 等协议。 ``` <bean id="calculatorServiceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter"> <property name="serviceName" value="CalculatorService" /> <property name="service" ref="calculatorService" /> <property name="serviceInterface" value="com.example.CalculatorService" /> <property name="registryPort" value="1099" /> </bean> <!-- 或者 --> <bean id="calculatorServiceExporter" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="calculatorService" /> <property name="serviceInterface" value="com.example.CalculatorService" /> </bean> ``` 8.在客户端通过 RMI 或 Hessian 等协议访问 CalculatorService。 ``` // RMI Remote remoteService = Naming.lookup("rmi://localhost:1099/CalculatorService"); CalculatorService calculatorService = (CalculatorService) UnicastRemoteObject.exportObject(remoteService, 0); // Hessian String url = "http://localhost:8080/CalculatorService"; HessianProxyFactory factory = new HessianProxyFactory(); CalculatorService calculatorService = (CalculatorService) factory.create(CalculatorService.class, url); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HBLOGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值