1. 服务器端程序
-
自定义远程接口
在 Java RMI服务端,远程对象是自定义远程接口实现类的实例, 该自定义远程接口声明每个要远程调用的抽象方法。
该接口特点:
a、该接口必须继承java.rmi.Remote接口;
b、该接口中的每个抽象方法必须抛出RemoteException异常或RemoteException 的父类异常;package com.jackmouse.rmi.email.service; import java.rmi.Remote; import java.rmi.RemoteException; /** * 邮件服务远程接口 **/ public interface IEmailService extends Remote { /** * 发送邮件抽象方法实现 **/ boolean send(String from, String password, String title, String content, String to) throws RemoteException; }
-
自定义远程接口的实现类
该实现类特点:- 该实现类必须实现自定义远程接口内的每个远程抽象方法;
- 该实现类必须继承java.rmi.UnicastRemoteObject类。UnicastRemoteObject类可以让服务端远程对象与客户端建立一对一的连接;
- 由于UnicastRemoteObject类中默认构造方法抛出RemoteException异常,因此该实现类中默认的构造方法必须显示地写出来并且该构造方法必须声明抛出RemoteException异常,别忘了子类构造方法要与参数列表相同的父类构造方法一致,父类无参构造方法抛出了RemoteException异常,那么子类无参构造方法也需要抛出RemoteException异常;
- 该实现类也可以含有其它非远程接口定义的抽象方法或非接口方法(即实现类内部自定义的方法,这些方法不能使用@Override进行注释),但客户端不能调用这些新增的方法(别忘了,这些方法并不是自定义远程接口内的抽象方法)。
package com.jackmouse.rmi.email.service; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import com.jackmouse.local.messages.service.IMessagesService; import com.jackmouse.local.messages.service.MessagesService; /** * 邮件服务远程接口实现类 **/ public class EmailService extends UnicastRemoteObject implements IEmailService { private static final long serialVersionUID = 1L; private IMessagesService messagesService = new MessagesService(); public EmailService() throws RemoteException { } /** * 发送邮件抽象方法实现 **/ //这里做邮件发送测试,邮件服务器地址填自己的邮箱账号的服务器地址 public boolean send(String from, String password, String title, String content, String to) throws RemoteException { boolean state = false; try { HtmlEmail htmlEmail = new HtmlEmail(); //邮件服务器的地址 htmlEmail.setHostName("qq.com"); //邮件服务器账号和密码 htmlEmail.setAuthentication(from, password); //设置发件人:第一个参数为发件人地址;第二个参数为收件人收到邮件时看到的发件人“姓名” htmlEmail.setFrom(from); //设置收件人:第一个参数为收件人邮件地址;第二个参数为收件人收到邮件时看到的收件人“姓名” htmlEmail.addTo(to); //设置邮件内容编码方式 htmlEmail.setCharset("UTF-8"); //标题 htmlEmail.setSubject(title); //邮件内容 htmlEmail.setHtmlMsg(content);//发送包含html标签的邮件 //发送邮件 htmlEmail.send(); state = true; } catch (EmailException e) { e.printStackTrace(); } finally { return state; } } }
-
定义继承ServletContextListener监听器的类EmailServiceListener,并在web.xml配置如下,使在tomcat启动时直接加载该监听器,执行contextInitialized中的代码,启动rmi服务器。
<listener> <listener-class>com.jackmouse.rmi.email.EmailServiceListener</listener-class> </listener>
package com.jackmouse.rmi.email; import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import com.jackmouse.rmi.email.service.EmailService; import com.jackmouse.rmi.email.service.IEmailService; /** * 邮件服务监听器 */ public class EmailServiceListener implements ServletContextListener{ public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent arg0) { try { //需要远程机器访问时,需要设置hostname为远程服务器的公网ip System.setProperty("java.rmi.server.hostname","49.*.*.*"); int registryPort = 8888; IEmailService emailService = new EmailService(); LocateRegistry.createRegistry(registryPort); Naming.bind("rmi://localhost:" + registryPort + "/emailservice",emailService); System.out.println("RMI服务器启动成功!"); } catch (RemoteException e) { System.err.println("远程对象创建失败!"); e.printStackTrace(); } catch (AlreadyBoundException e) { System.err.println("发生重复绑定远程对象异常!"); e.printStackTrace(); } catch (MalformedURLException e) { System.err.println("绑定的URL不正确!"); e.printStackTrace(); } } }
2. 客户端程序
- 定义与服务器端一样的远程接口,包名也要相同,但不用再继承Remote,方法也不用抛出RemoteException
package com.jackmouse.rmi.email.service; /** * 发送邮件服务远程接口 **/ public interface IEmailService{ /** * 发送邮件抽象方法 **/ boolean send(String from, String password, String title, String content, String to); }
- 进行测试
运行后输出true即测试成功package packageofrmi; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import com.jackmouse.rmi.email.service.IEmailService; /** * 客户端测试 */ public class TestClient { public static void main(String[] args) { String title = "rmi测试";//邮件主题 String password = "XXX";//发件人密码 String to = "XXXX@qq.com";//收件人邮箱账号 String from = "XXXX@qq.com";//发件人邮箱账号 String content="123";//邮件内容 try { //依据服务名称通过Naming类的lookup方法获取RMI服务端特定的远程对象引用 ,为调用该对象中实现的接口中的方法奠定基础,其中lookup方法的参数标准格式为“rmi://host:registryPort/serviceName”,也可以“//host:registryPort/serviceName”。 IEmailService emailService = (IEmailService)Naming.lookup("rmi://127.0.0.1:8888/emailservice"); System.out.println(emailService.send(from, password, title, content, to)); System.out.println("发送成功"); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }
3. 注意点
- 服务器端的远程接口要继承java.rmi.Remote接口,方法要抛出RemoteException或其父类异常
- 服务器端的远程接口实现类必须继承java.rmi.UnicastRemoteObject类。由于UnicastRemoteObject类中默认构造方法抛出RemoteException异常,因此该实现类中默认的构造方法必须显示地写出来并且该构造方法必须声明抛出RemoteException异常。
- 服务器端的ServletContextListener监听器的子类EmailServiceListener中有这么一句代码:
当做本地rmi测试时,不需要这行代码。System.setProperty("java.rmi.server.hostname","49.*.*.*");
当把rmi服务器的web项目放到远程服务器上时,需要设置hostname为远程服务器的公网ip。