通过rmi协议发布一个Java服务,使用java.rim包下的Naming.bind()方法即可,但是jdk默认rmi协议的实现需要接口类继承java.rmi.Remote,接口方法抛出RemoteException,实现类需要继承UnicastRemoteObject类比较繁琐。spring框架也提供了一套rmi协议的实现,参考spring帮助文档中Exposing services using RMI部分,spring对rmi协议的支持,不需要我们的业务接口去继承任何父类,业务代码更加简洁,RmiProtocol类的实现使用spring框架的相关工具类。
在第一篇中我们在maven配置文件中只添加了netty,jboss-marshalling,javassist三个依赖,现在需要增加上spring的依赖:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.1.6.RELEASEversion>
dependency>
在第五篇中有Protocol接口的子类关系图,抽象类AbstractProxyProtocol实现了Protocol接口,RmiProtocol类和LdubboProtocol类又分别继承了该抽象类,这个结构也是跟dubbo源码中的Protocol接口类似的。好的,先看一下抽象类AbstractProxyProtocol的实现,代码如下:
package com.lipenglong.ldubbo.rpc;
import com.lipenglong.ldubbo.config.RegistryConfig;
/**
* AbstractProxyProtocol
*
* Created by lipenglong on 2017/7/27.
*/
public abstract class AbstractProxyProtocol implements Protocol {
@Override
public void export(Class interfaceClass, Object ref) {
doExport(interfaceClass, ref);
}
@Override
public T refer(Class> interfaceClass, RegistryConfig registryConfig) {
return doRefer(interfaceClass, registryConfig);
}
protected abstract void doExport(Class interfaceClass, Object ref);
protected abstract T doRefer(Class interfaceClass, RegistryConfig registryConfig);
}
在ServiceConfig类的export()方法中,调用protocol.export(interfaceClass, ref);
方法,这里调用的就是AbstractProxyProtocol抽象类的export()方法,看上面的类代码,它又调用了抽象方法doExport(interfaceClass, ref);
这个抽象方法就是具体的协议类需要实现的方法。
接下来看RmiProtocol类的实现代码:
package com.lipenglong.ldubbo.rpc.protocol;
import com.lipenglong.ldubbo.config.RegistryConfig;
import com.lipenglong.ldubbo.rpc.AbstractProxyProtocol;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import org.springframework.remoting.rmi.RmiServiceExporter;
import java.rmi.RemoteException;
/**
* rmi协议通信类
*
* Created by lipenglong on 2017/7/25.
*/
public class RmiProtocol extends AbstractProxyProtocol {
private static final Integer DEFAULT_PORT = 1099;
@Override
protected void doExport(Class interfaceClass, Object ref) {
RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
rmiServiceExporter.setRegistryPort(DEFAULT_PORT);
rmiServiceExporter.setServiceName(interfaceClass.getName());
rmiServiceExporter.setServiceInterface(interfaceClass);
rmiServiceExporter.setService(ref);
try {
rmiServiceExporter.afterPropertiesSet();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected T doRefer(Class interfaceClass, RegistryConfig registryConfig) {
return null;
}
}
doExport方法通过spring的RmiServiceExporter类实现服务的发布,首先创建一个RmiServiceExporter对象,设置端口、服务名称、服务接口、实现类,然后通过afterPropertiesSet()方法暴露服务,这个方法是不是有点熟悉?spring的RmiServiceExporter对象也是实现了InitializingBean接口的,当bean初始化后,执行该方法。doExport方法我们是用编码的方式自己调用的,spring帮助文档上面的例子是xml配置文件的方式,写在配置文件中时afterPropertiesSet()方法会自动调用的。
所以spring框架的rmi和dubbo框架发布rpc服务方式的入口是一样的,如果大家看一下dubbo框架的RmiProtocol类的源码,和上面我们自己写的代码是一样的。(doRefer引用服务的方法暂时先不实现)
到目前为止,我们先实现了一个rmi协议的服务发布,那么先来测试一下这个rpc框架的server端能否正常运行起来
把ldubbo项目运行maven的install命令打好jar包,看一下ldubbo项目的结构:(common和remote包先忽略)
新建一个接口ldubbo-api项目,创建一个测试用的接口类UserService:
package com.lipenglong.ldubbo.api.service;
import com.lipenglong.ldubbo.api.domain.User;
import java.util.List;
/**
* 用户service接口类
*
* Created by lipenglong on 2017/7/24.
*/
public interface UserService {
List queryUserList();
User getUserById(Long id);
}
定义一个User对象:
package com.lipenglong.ldubbo.api.domain;
import java.io.Serializable;
/**
* User
*
* Created by lipenglong on 2017/7/24.
*/
public class User implements Serializable {
private static final long serialVersionUID = 2775326738215482673L;
private Long id;
private String name;
private String password;
private Integer role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getRole() {
return role;
}
public void setRole(Integer role) {
this.role = role;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", role=" + role +
'}';
}
}
接口项目定义完了,然后创建一个新的的provider项目,在pom中把ldubbo和ldubbo-api两个项目引用进来,再增加下面两个slf4j的日志工具包
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.9version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.9version>
dependency>
provider项目需要写一个UserService接口的实现,代码如下:
package com.lipenglong.ldubbo.provider.service;
import com.lipenglong.ldubbo.api.domain.User;
import com.lipenglong.ldubbo.api.service.UserService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* UserServiceImpl
*
* Created by lipenglong on 2017/7/24.
*/
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Override
public List queryUserList() {
List result = new ArrayList();
User user = new User();
user.setId(1L);
user.setName("user1");
user.setPassword("****");
user.setRole(1);
result.add(user);
return result;
}
public User getUserById(Long id) {
User user = new User();
user.setId(id);
user.setName("user" + id);
user.setPassword("****");
user.setRole(1);
return user;
}
}
实现类有了,定义spring的配置文件applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.lipenglong.ldubbo.provider.service"/>
<import resource="applicationContext-ldubbo.xml"/>
beans>
applicationContext-ldubbo.xml文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ldubbo="http://www.lipenglong.com/schema/ldubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.lipenglong.com/schema/ldubbo
http://www.lipenglong.com/schema/ldubbo/ldubbo.xsd" default-lazy-init="true">
<ldubbo:protocol name="rmi"/>
<ldubbo:service interface="com.lipenglong.ldubbo.api.service.UserService" ref="userService"/>
beans>
实现类,spring配置文件都定义完了,最后需要一个启动类,创建一个Provider类:
package com.lipenglong.ldubbo.provider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* ldubbo服务提供者启动类
*
* Created by lipenglong on 2017/7/25.
*/
public class Provider {
private static Log logger = LogFactory.getLog(Provider.class);
public static void main(String[] args) throws Exception {
new ClassPathXmlApplicationContext("applicationContext.xml");
logger.info(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " ldubbo service server started!");
}
}
在provider项目下还需定义一个log4j.xml的配置文件,如下:
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d [%p] %c.%M(%L) | %m%n"/>
layout>
appender>
<root>
<level value="INFO"/>
<appender-ref ref="CONSOLE"/>
root>
log4j:configuration>
ldubbo框架可以发布rmi协议的服务了!
下一篇先不写LdubboProtocol如何使用netty做服务发布,先写rmi的服务引用