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 = "[email protected]";//收件人郵箱賬號 String from = "[email protected]";//發件人郵箱賬號 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。