1. 程式人生 > >RMI簡單例項與Spring整合RMI

RMI簡單例項與Spring整合RMI

     rmi遠端方法呼叫,用於伺服器端和客戶端之間的會話通訊。

      本文以兩種方式實現rmi,一種為單獨的rmi例項 一種為spring中整合rmi    記錄學習的腳步

    1.rmi與jpa的融合

/*
 * 自己編寫rmi的話 只需三步 前兩步針對伺服器端 第三步針對客戶端
 * 1.讓遠端服務實現類繼承UnicastRemoteObject  並讓遠端服務介面繼承Remote
 * 2.使用LocateRegistry.createRegistry註冊RMI的服務埠 Naming.rebind繫結遠端服務物件
 * 3.客戶端通過Naming.lookup查詢遠端服務物件
 * 
 */

遠端服務介面類 IStusDAO.java 繼承Remote類
package com.undergrowth.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.sql.Timestamp;
import java.util.List;

import com.undergrowth.bean.Stus;

/**
 * Interface for StusDAO.
 * 
 * @author MyEclipse Persistence Tools
 */

public interface IStusDAO extends Remote{
	/**
	 * Perform an initial save of a previously unsaved Stus entity. All
	 * subsequent persist actions of this entity should use the #update()
	 * method. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#persist(Object)
	 * EntityManager#persist} operation.
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * IStusDAO.save(entity);
	 * EntityManagerHelper.commit();
	 * </pre>
	 * 
	 * @param entity
	 *            Stus entity to persist
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void save(Stus entity) throws RemoteException;

	/**
	 * Delete a persistent Stus entity. This operation must be performed within
	 * the a database transaction context for the entity's data to be
	 * permanently deleted from the persistence store, i.e., database. This
	 * method uses the {@link javax.persistence.EntityManager#remove(Object)
	 * EntityManager#delete} operation.
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * IStusDAO.delete(entity);
	 * EntityManagerHelper.commit();
	 * entity = null;
	 * </pre>
	 * 
	 * @param entity
	 *            Stus entity to delete
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void delete(Stus entity) throws RemoteException;

	/**
	 * Persist a previously saved Stus entity and return it or a copy of it to
	 * the sender. A copy of the Stus entity parameter is returned when the JPA
	 * persistence mechanism has not previously been tracking the updated
	 * entity. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge}
	 * operation.
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * entity = IStusDAO.update(entity);
	 * EntityManagerHelper.commit();
	 * </pre>
	 * 
	 * @param entity
	 *            Stus entity to update
	 * @return Stus the persisted Stus entity instance, may not be the same
	 * @throws RuntimeException
	 *             if the operation fails
	 */
	public Stus update(Stus entity) throws RemoteException;

	public Stus findById(String id) throws RemoteException;

	/**
	 * Find all Stus entities with a specific property value.
	 * 
	 * @param propertyName
	 *            the name of the Stus property to query
	 * @param value
	 *            the property value to match
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Stus> found by query
	 */
	public List<Stus> findByProperty(String propertyName, Object value,
			int... rowStartIdxAndCount) throws RemoteException;

	public List<Stus> findByStuName(Object stuName, int... rowStartIdxAndCount) throws RemoteException;

	public List<Stus> findByStuAge(Object stuAge, int... rowStartIdxAndCount) throws RemoteException;

	/**
	 * Find all Stus entities.
	 * 
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Stus> all Stus entities
	 */
	public List<Stus> findAll(int... rowStartIdxAndCount) throws RemoteException;
}

遠端服務實現類 StusDAO.java 繼承UnicastRemoteObject
package com.undergrowth.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.sql.Timestamp;
import java.util.List;
import java.util.logging.Level;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.undergrowth.bean.EntityManagerHelper;
import com.undergrowth.bean.Stus;

/**
 * A data access object (DAO) providing persistence and search support for Stus
 * entities. Transaction control of the save(), update() and delete() operations
 * must be handled externally by senders of these methods or must be manually
 * added to each of these methods for data to be persisted to the JPA datastore.
 * 
 * @see com.undergrowth.bean.Stus
 * @author MyEclipse Persistence Tools
 */
public class StusDAO extends UnicastRemoteObject implements IStusDAO {
	
	public StusDAO() throws RemoteException {
		super();
		// TODO Auto-generated constructor stub
	}

	// property constants
	public static final String STU_NAME = "stuName";
	public static final String STU_AGE = "stuAge";

	private EntityManager getEntityManager() {
		return EntityManagerHelper.getEntityManager();
	}

	/**
	 * Perform an initial save of a previously unsaved Stus entity. All
	 * subsequent persist actions of this entity should use the #update()
	 * method. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#persist(Object)
	 * EntityManager#persist} operation.
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * StusDAO.save(entity);
	 * EntityManagerHelper.commit();
	 * </pre>
	 * 
	 * @param entity
	 *            Stus entity to persist
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void save(Stus entity) throws RemoteException{
		EntityManagerHelper.log("saving Stus instance", Level.INFO, null);
		try {
			getEntityManager().persist(entity);
			EntityManagerHelper.log("save successful", Level.INFO, null);
		} catch (RuntimeException re) {
			EntityManagerHelper.log("save failed", Level.SEVERE, re);
			throw re;
		}
	}

	/**
	 * Delete a persistent Stus entity. This operation must be performed within
	 * the a database transaction context for the entity's data to be
	 * permanently deleted from the persistence store, i.e., database. This
	 * method uses the {@link javax.persistence.EntityManager#remove(Object)
	 * EntityManager#delete} operation.
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * StusDAO.delete(entity);
	 * EntityManagerHelper.commit();
	 * entity = null;
	 * </pre>
	 * 
	 * @param entity
	 *            Stus entity to delete
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void delete(Stus entity) throws RemoteException{
		EntityManagerHelper.log("deleting Stus instance", Level.INFO, null);
		try {
			entity = getEntityManager().getReference(Stus.class,
					entity.getStuId());
			getEntityManager().remove(entity);
			EntityManagerHelper.log("delete successful", Level.INFO, null);
		} catch (RuntimeException re) {
			EntityManagerHelper.log("delete failed", Level.SEVERE, re);
			throw re;
		}
	}

	/**
	 * Persist a previously saved Stus entity and return it or a copy of it to
	 * the sender. A copy of the Stus entity parameter is returned when the JPA
	 * persistence mechanism has not previously been tracking the updated
	 * entity. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge}
	 * operation.
	 * 
	 * <pre>
	 * EntityManagerHelper.beginTransaction();
	 * entity = StusDAO.update(entity);
	 * EntityManagerHelper.commit();
	 * </pre>
	 * 
	 * @param entity
	 *            Stus entity to update
	 * @return Stus the persisted Stus entity instance, may not be the same
	 * @throws RuntimeException
	 *             if the operation fails
	 */
	public Stus update(Stus entity) throws RemoteException{
		EntityManagerHelper.log("updating Stus instance", Level.INFO, null);
		try {
			Stus result = getEntityManager().merge(entity);
			EntityManagerHelper.log("update successful", Level.INFO, null);
			return result;
		} catch (RuntimeException re) {
			EntityManagerHelper.log("update failed", Level.SEVERE, re);
			throw re;
		}
	}

	public Stus findById(String id) throws RemoteException{
		EntityManagerHelper.log("finding Stus instance with id: " + id,
				Level.INFO, null);
		try {
			Stus instance = getEntityManager().find(Stus.class, id);
			return instance;
		} catch (RuntimeException re) {
			EntityManagerHelper.log("find failed", Level.SEVERE, re);
			throw re;
		}
	}

	/**
	 * Find all Stus entities with a specific property value.
	 * 
	 * @param propertyName
	 *            the name of the Stus property to query
	 * @param value
	 *            the property value to match
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            number of results to return.
	 * @return List<Stus> found by query
	 */
	@SuppressWarnings("unchecked")
	public List<Stus> findByProperty(String propertyName, final Object value,
			final int... rowStartIdxAndCount) throws RemoteException{
		EntityManagerHelper.log("finding Stus instance with property: "
				+ propertyName + ", value: " + value, Level.INFO, null);
		try {
			final String queryString = "select model from Stus model where model."
					+ propertyName + "= :propertyValue";
			Query query = getEntityManager().createQuery(queryString);
			query.setParameter("propertyValue", value);
			if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
				int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult(rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					int rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults(rowCount);
					}
				}
			}
			return query.getResultList();
		} catch (RuntimeException re) {
			EntityManagerHelper.log("find by property name failed",
					Level.SEVERE, re);
			throw re;
		}
	}

	public List<Stus> findByStuName(Object stuName, int... rowStartIdxAndCount) throws RemoteException{
		return findByProperty(STU_NAME, stuName, rowStartIdxAndCount);
	}

	public List<Stus> findByStuAge(Object stuAge, int... rowStartIdxAndCount) throws RemoteException{
		return findByProperty(STU_AGE, stuAge, rowStartIdxAndCount);
	}

	/**
	 * Find all Stus entities.
	 * 
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Stus> all Stus entities
	 */
	@SuppressWarnings("unchecked")
	public List<Stus> findAll(final int... rowStartIdxAndCount) throws RemoteException{
		EntityManagerHelper.log("finding all Stus instances", Level.INFO, null);
		try {
			final String queryString = "select model from Stus model";
			Query query = getEntityManager().createQuery(queryString);
			if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
				int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult(rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					int rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults(rowCount);
					}
				}
			}
			return query.getResultList();
		} catch (RuntimeException re) {
			EntityManagerHelper.log("find all failed", Level.SEVERE, re);
			throw re;
		}
	}

}

註冊遠端服務物件 RmiServer.java
package com.undergrowth.junit;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

import com.undergrowth.rmi.IStusDAO;
import com.undergrowth.rmi.StusDAO;

/*
 * 自己編寫rmi的話 只需三步 前兩步針對伺服器端 第三步針對客戶端
 * 1.讓遠端服務實現類繼承UnicastRemoteObject  並讓遠端服務介面繼承Remote
 * 2.使用LocateRegistry.createRegistry註冊RMI的服務埠 Naming.rebind繫結遠端服務物件
 * 3.客戶端通過Naming.lookup查詢遠端服務物件
 * 
 */

public class RmiServer {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			IStusDAO istusDAO=new StusDAO();
			//System.setProperty("java.rmi.server.hostname", "192.168.38.172");
			//建立訪問rmi的遠端埠
			//啟動rmiregister程式
			//Runtime.getRuntime().exec("rmiregistry 4567");
			LocateRegistry.createRegistry(4567);
			//註冊遠端服務物件
			Naming.rebind("rmi://192.168.38.172:4567/IStusDAO", istusDAO);
			System.out.println("註冊遠端服務物件成功");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

在cmd下檢視rmi繫結的埠


客戶端測試 RmiClient.java

package com.undergrowth.junit;

import java.rmi.Naming;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;

import com.undergrowth.bean.Stus;
import com.undergrowth.rmi.IStusDAO;

public class RmiClient {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			IStusDAO iStusDAO=(IStusDAO) Naming.lookup("rmi://192.168.38.172:4567/IStusDAO");
			List<Stus> listStus=iStusDAO.findAll();
			for (Stus stus : listStus) {
				System.out.println(stus);
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

客戶端輸出
Stus [stuId=014ED6D01D9B4C6E882E3624032FA9CD, stuName=sz, stuAge=21.0, stuBirthday=2014-04-19 15:07:33.0]
Stus [stuId=946E460A4FA14D8496EF601E754FD314, stuName=gz, stuAge=20.0, stuBirthday=2014-04-19 15:07:33.0]

2.spring整合rmi和jpa
/*
 * spring支援的rmi很簡單  
 * 不用服務介面繼承Remote
 * 不用服務實現類繼承UnicastRemoteObject
 * 也不用我們自己註冊rmi的遠端服務實現類
 * 
 * 伺服器端 你需要做的僅僅是 寫好你需要提供遠端服務的實現類
 * 然後將其交給RmiServiceExporter類 RmiServiceExporter會將實現類釋出為RMI服務
 * 
 * 客戶端 也很簡單
 * 只需要使用RmiProxyFactoryBean從伺服器端的URL從獲取服務物件  並進行封裝給你定義的id
 * 然後從spring容器中獲取RmiProxyFactoryBean封裝的id即可
 * 
 * 此測試程式碼中 伺服器和客戶端都在一個專案中 也可換成多個專案 在不同的電腦中
 * 只需要在伺服器的RmiServiceExporter中加入 p:registryHost="ip地址" 即可
 * 客戶端將localhost換成ip地址即可
 */


spring的配置檔案 如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
	xmlns:tx="http://www.springframework.org/schema/tx">

    <!-- spring 的jpa的事務管理  -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="SpringRmiAndJpa" />
	</bean>
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager" />
	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
	</bean>
	<bean
		class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor">
	</bean>
	
	<bean id="IStusDAO" class="com.undergrowth.bean.StusDAO"></bean>
	
	<!-- 使用RmiServiceExporter將IStusDAO的物件匯出為RMI服務物件 -->
	<bean class="org.springframework.remoting.rmi.RmiServiceExporter" 
	p:service-ref="IStusDAO"
	p:serviceName="IStusDAO"
	p:registryPort="1199"
	p:serviceInterface="com.undergrowth.bean.IStusDAO"
	/>
	
	<!-- 使用RmiProxyFactoryBean從遠端服務處獲取服務的物件 並將其封裝成stusDao物件 -->
	<bean id="stusDao" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"
		p:serviceUrl="rmi://localhost:1199/IStusDAO"
		p:serviceInterface="com.undergrowth.bean.IStusDAO"
	/>
	
</beans>

服務介面實現類 StusDAO.java
package com.undergrowth.bean;

import java.sql.Timestamp;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 * A data access object (DAO) providing persistence and search support for Stus
 * entities. Transaction control of the save(), update() and delete() operations
 * can directly support Spring container-managed transactions or they can be
 * augmented to handle user-managed Spring transactions. Each of these methods
 * provides additional information for how to configure it for the desired type
 * of transaction control.
 * 
 * @see com.undergrowth.bean.Stus
 * @author MyEclipse Persistence Tools
 */
@Transactional
@Repository
public class StusDAO implements IStusDAO {
	private static final Log logger = LogFactory.getLog(StusDAO.class);
	// property constants
	public static final String STU_NAME = "stuName";
	public static final String STU_AGE = "stuAge";

	private EntityManager entityManager;

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	private EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * Perform an initial save of a previously unsaved Stus entity. All
	 * subsequent persist actions of this entity should use the #update()
	 * method. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#persist(Object)
	 * EntityManager#persist} operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * StusDAO.save(entity);
	 * txManager.commit(txn);
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Stus entity to persist
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void save(Stus entity) {
		logger.info("saving Stus instance");
		try {
			getEntityManager().persist(entity);
			logger.info("save successful");
		} catch (RuntimeException re) {
			logger.error("save failed", re);
			throw re;
		}
	}

	/**
	 * Delete a persistent Stus entity. This operation must be performed within
	 * the a database transaction context for the entity's data to be
	 * permanently deleted from the persistence store, i.e., database. This
	 * method uses the {@link javax.persistence.EntityManager#remove(Object)
	 * EntityManager#delete} operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * StusDAO.delete(entity);
	 * txManager.commit(txn);
	 * entity = null;
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Stus entity to delete
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void delete(Stus entity) {
		logger.info("deleting Stus instance");
		try {
			entity = getEntityManager().getReference(Stus.class,
					entity.getStuId());
			getEntityManager().remove(entity);
			logger.info("delete successful");
		} catch (RuntimeException re) {
			logger.error("delete failed", re);
			throw re;
		}
	}

	/**
	 * Persist a previously saved Stus entity and return it or a copy of it to
	 * the sender. A copy of the Stus entity parameter is returned when the JPA
	 * persistence mechanism has not previously been tracking the updated
	 * entity. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge}
	 * operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * entity = StusDAO.update(entity);
	 * txManager.commit(txn);
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Stus entity to update
	 * @return Stus the persisted Stus entity instance, may not be the same
	 * @throws RuntimeException
	 *             if the operation fails
	 */
	public Stus update(Stus entity) {
		logger.info("updating Stus instance");
		try {
			Stus result = getEntityManager().merge(entity);
			logger.info("update successful");
			return result;
		} catch (RuntimeException re) {
			logger.error("update failed", re);
			throw re;
		}
	}

	public Stus findById(String id) {
		logger.info("finding Stus instance with id: " + id);
		try {
			Stus instance = getEntityManager().find(Stus.class, id);
			return instance;
		} catch (RuntimeException re) {
			logger.error("find failed", re);
			throw re;
		}
	}

	/**
	 * Find all Stus entities with a specific property value.
	 * 
	 * @param propertyName
	 *            the name of the Stus property to query
	 * @param value
	 *            the property value to match
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            number of results to return.
	 * @return List<Stus> found by query
	 */
	@SuppressWarnings("unchecked")
	public List<Stus> findByProperty(String propertyName, final Object value,
			final int... rowStartIdxAndCount) {
		logger.info("finding Stus instance with property: " + propertyName
				+ ", value: " + value);
		try {
			final String queryString = "select model from Stus model where model."
					+ propertyName + "= :propertyValue";
			Query query = getEntityManager().createQuery(queryString);
			query.setParameter("propertyValue", value);
			if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
				int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult(rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					int rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults(rowCount);
					}
				}
			}
			return query.getResultList();
		} catch (RuntimeException re) {
			logger.error("find by property name failed", re);
			throw re;
		}
	}

	public List<Stus> findByStuName(Object stuName, int... rowStartIdxAndCount) {
		return findByProperty(STU_NAME, stuName, rowStartIdxAndCount);
	}

	public List<Stus> findByStuAge(Object stuAge, int... rowStartIdxAndCount) {
		return findByProperty(STU_AGE, stuAge, rowStartIdxAndCount);
	}

	/**
	 * Find all Stus entities.
	 * 
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Stus> all Stus entities
	 */
	@SuppressWarnings("unchecked")
	public List<Stus> findAll(final int... rowStartIdxAndCount) {
		logger.info("finding all Stus instances");
		try {
			final String queryString = "select model from Stus model";
			Query query = getEntityManager().createQuery(queryString);
			if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
				int rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
				if (rowStartIdx > 0) {
					query.setFirstResult(rowStartIdx);
				}

				if (rowStartIdxAndCount.length > 1) {
					int rowCount = Math.max(0, rowStartIdxAndCount[1]);
					if (rowCount > 0) {
						query.setMaxResults(rowCount);
					}
				}
			}
			return query.getResultList();
		} catch (RuntimeException re) {
			logger.error("find all failed", re);
			throw re;
		}
	}

	public static IStusDAO getFromApplicationContext(ApplicationContext ctx) {
		return (IStusDAO) ctx.getBean("StusDAO");
	}
}

上面的 @Transactional 用於spring的jpa的事務管理  

服務介面 IStusDAO.java

package com.undergrowth.bean;

import java.sql.Timestamp;
import java.util.List;

/**
 * Interface for StusDAO.
 * 
 * @author MyEclipse Persistence Tools
 */

public interface IStusDAO {
	/**
	 * Perform an initial save of a previously unsaved Stus entity. All
	 * subsequent persist actions of this entity should use the #update()
	 * method. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#persist(Object)
	 * EntityManager#persist} operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * IStusDAO.save(entity);
	 * txManager.commit(txn);
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Stus entity to persist
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void save(Stus entity);

	/**
	 * Delete a persistent Stus entity. This operation must be performed within
	 * the a database transaction context for the entity's data to be
	 * permanently deleted from the persistence store, i.e., database. This
	 * method uses the {@link javax.persistence.EntityManager#remove(Object)
	 * EntityManager#delete} operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * IStusDAO.delete(entity);
	 * txManager.commit(txn);
	 * entity = null;
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Stus entity to delete
	 * @throws RuntimeException
	 *             when the operation fails
	 */
	public void delete(Stus entity);

	/**
	 * Persist a previously saved Stus entity and return it or a copy of it to
	 * the sender. A copy of the Stus entity parameter is returned when the JPA
	 * persistence mechanism has not previously been tracking the updated
	 * entity. This operation must be performed within the a database
	 * transaction context for the entity's data to be permanently saved to the
	 * persistence store, i.e., database. This method uses the
	 * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge}
	 * operation.
	 * <p>
	 * User-managed Spring transaction example:
	 * 
	 * <pre>
	 * TransactionStatus txn = txManager
	 * 		.getTransaction(new DefaultTransactionDefinition());
	 * entity = IStusDAO.update(entity);
	 * txManager.commit(txn);
	 * </pre>
	 * 
	 * @see <a href =
	 *      "http://www.myeclipseide.com/documentation/quickstarts/jpaspring#containermanaged">Spring
	 *      container-managed transaction examples</a>
	 * @param entity
	 *            Stus entity to update
	 * @return Stus the persisted Stus entity instance, may not be the same
	 * @throws RuntimeException
	 *             if the operation fails
	 */
	public Stus update(Stus entity);

	public Stus findById(String id);

	/**
	 * Find all Stus entities with a specific property value.
	 * 
	 * @param propertyName
	 *            the name of the Stus property to query
	 * @param value
	 *            the property value to match
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Stus> found by query
	 */
	public List<Stus> findByProperty(String propertyName, Object value,
			int... rowStartIdxAndCount);

	public List<Stus> findByStuName(Object stuName, int... rowStartIdxAndCount);

	public List<Stus> findByStuAge(Object stuAge, int... rowStartIdxAndCount);

	/**
	 * Find all Stus entities.
	 * 
	 * @param rowStartIdxAndCount
	 *            Optional int varargs. rowStartIdxAndCount[0] specifies the the
	 *            row index in the query result-set to begin collecting the
	 *            results. rowStartIdxAndCount[1] specifies the the maximum
	 *            count of results to return.
	 * @return List<Stus> all Stus entities
	 */
	public List<Stus> findAll(int... rowStartIdxAndCount);
}

客戶端實現 SpringRmiClient.java
package com.undergrowth.junit;

import static org.junit.Assert.*;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.undergrowth.bean.IStusDAO;
import com.undergrowth.bean.Stus;

/*
 * spring支援的rmi很簡單  
 * 不用服務介面繼承Remote
 * 不用服務實現類繼承UnicastRemoteObject
 * 也不用我們自己註冊rmi的遠端服務實現類
 * 
 * 伺服器端 你需要做的僅僅是 寫好你需要提供遠端服務的實現類
 * 然後將其交給RmiServiceExporter類 RmiServiceExporter會將實現類釋出為RMI服務
 * 
 * 客戶端 也很簡單
 * 只需要使用RmiProxyFactoryBean從伺服器端的URL從獲取服務物件  並進行封裝給你定義的id
 * 然後從spring容器中獲取RmiProxyFactoryBean封裝的id即可
 * 
 * 此測試程式碼中 伺服器和客戶端都在一個專案中 也可換成多個專案 在不同的電腦中
 * 只需要在伺服器的RmiServiceExporter中加入 p:registryHost="ip地址" 即可
 * 客戶端將localhost換成ip地址即可
 */
public class SpringRmiClient {

	@Test
	public void testRmiClient() {
		ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
		//獲取封裝的遠端服務物件
		IStusDAO iStusDAO=(IStusDAO) ac.getBean("stusDao");
		//呼叫遠端服務物件的方法 返回列表結果
		List<Stus> list=iStusDAO.findAll();
		for (Stus stus : list) {
			System.out.println(stus);
		}
		//新增
		Stus stus=new Stus(UUID.randomUUID().toString(), "spring rmi客戶端", 78.9, new Timestamp(Calendar.getInstance().getTimeInMillis()));
		iStusDAO.save(stus);
		
		list=iStusDAO.findAll();
		for (Stus stus2 : list) {
			System.out.println(stus2);
		}
		
		
		//如果不加下面的while語句  那麼 執行完此測試方法後 在cmd中 就看不到rmi的繫結埠資訊 
		//因為spring在測試完成後 會清理相關的rmi資訊
		//while(true){}
	}

}

控制檯輸出
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[EL Info]: 2014-05-03 12:44:50.747--ServerSession(17038506)--EclipseLink, version: Eclipse Persistence Services - 2.5.0.v20130507-3faac2b
[EL Info]: connection: 2014-05-03 12:44:51.655--ServerSession(17038506)--file:/D:/learnsoftware/java/AndroidDevelop/myeclipse_2013_code/SpringRmiAndJpa/bin/_SpringRmiAndJpa login successful
Stus [stuId=014ED6D01D9B4C6E882E3624032FA9CD, stuName=sz, stuAge=21.0, stuBirthday=2014-04-19 15:07:33.0]
Stus [stuId=946E460A4FA14D8496EF601E754FD314, stuName=gz, stuAge=20.0, stuBirthday=2014-04-19 15:07:33.0]
Stus [stuId=014ED6D01D9B4C6E882E3624032FA9CD, stuName=sz, stuAge=21.0, stuBirthday=2014-04-19 15:07:33.0]
Stus [stuId=946E460A4FA14D8496EF601E754FD314, stuName=gz, stuAge=20.0, stuBirthday=2014-04-19 15:07:33.0]
Stus [stuId=96d4f969-6d0a-4f6d-940f-e97b73080314, stuName=spring rmi客戶端, stuAge=78.9, stuBirthday=2014-05-03 12:44:52.591]


對比以上兩種方式 會發現spring的rmi比自己寫的rmi要簡單、方便的多 不過前提是你對spring要有一定的瞭解

其實使用自定義的rmi方式時 遇到了很多我稱之為詭異的現象 也記錄一下吧

其實當我寫好遠端服務介面和遠端服務實現類時 我嘗試著使用junit類進行測試 如下 

package com.undergrowth.junit;

import static org.junit.Assert.*;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.List;

import org.junit.Test;

import com.undergrowth.bean.Stus;
import com.undergrowth.rmi.IStusDAO;
import com.undergrowth.rmi.StusDAO;

public class RmiJunit {

	@Test
	public void testServer() {
		
		try {
			IStusDAO istusDAO=new StusDAO();
			//System.setProperty("java.rmi.server.hostname", "192.168.38.172");
			//建立訪問rmi的遠端埠
			//啟動rmiregister程式
			//Runtime.getRuntime().exec("rmiregistry 4567");
			LocateRegistry.createRegistry(4567);
			//註冊遠端服務物件
			Naming.rebind("rmi://192.168.38.172:4567/IStusDAO", istusDAO);
			System.out.println("註冊遠端服務物件成功");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	@Test
	public void testClient() {
		
		try {
			IStusDAO iStusDAO=(IStusDAO) Naming.lookup("rmi://192.168.38.172:4567/IStusDAO");
			/*List<Stus> listStus=iStusDAO.findAll();
			for (Stus stus : listStus) {
				System.out.println(stus);
			}*/
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	@Test
	public void testCon(){
		IStusDAO iStusDAO;
		try {
			iStusDAO = new StusDAO();
			List<Stus> listStus=iStusDAO.findAll();
			for (Stus stus : listStus) {
				System.out.println(stus);
			}
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}


先執行testServer沒有問題  控制檯打印出 

註冊遠端服務物件成功
即表示伺服器端註冊rmi服務是沒有問題的 看起來是這樣 是麼?

其實不是 接著看 然後執行testClient 發現報錯了 

java.rmi.ConnectException: Connection refused to host: 192.168.38.172; nested exception is: 
	java.net.ConnectException: Connection refused: connect
	at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:601)
	at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:198)
	at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:184)
	at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:322)
	at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
	at java.rmi.Naming.lookup(Naming.java:84)
	at com.undergrowth.junit.RmiJunit.testClient(RmiJunit.java:42)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.net.ConnectException: Connection refused: connect
	at java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
	at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
	at java.net.Socket.connect(Socket.java:529)
	at java.net.Socket.connect(Socket.java:478)
	at java.net.Socket.<init>(Socket.java:375)
	at java.net.Socket.<init>(Socket.java:189)
	at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:22)
	at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:128)
	at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:595)
	... 29 more

上網一搜 好多 經典的錯誤 但是我都排查了 說ip錯誤的 不是  說伺服器和客戶端的rmi的url要一致 也不是  

很明顯  是客戶端無法連線上伺服器的rmi埠 於是 


發現cmd下面居然沒有 4567的埠監聽

這讓我好鬱悶 接著尋尋覓覓 還是沒找到原因  將testServer和testClient換成兩個main函式後  發現就正常了  好吧 我發現我找到原因了 

在testServer裡面加上一句 如下

@Test
	public void testServer() {
		
		try {
			IStusDAO istusDAO=new StusDAO();
			//System.setProperty("java.rmi.server.hostname", "192.168.38.172");
			//建立訪問rmi的遠端埠
			//啟動rmiregister程式
			//Runtime.getRuntime().exec("rmiregistry 4567");
			LocateRegistry.createRegistry(4567);
			//註冊遠端服務物件
			Naming.rebind("rmi://192.168.38.172:4567/IStusDAO", istusDAO);
			System.out.println("註冊遠端服務物件成功");
			while(true){}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

因為在與testServer執行完後 如果沒有加while(true){} 這句的話  註冊的rmi這個程序就關掉了  程序都關了 rmi的埠監聽也就不存在了 客戶端還怎麼連的上呢 鬱悶死了 不過 好歹解決了

當然 如果使用  

Runtime.getRuntime().exec("rmiregistry 4567");
的話 則會在後臺建立一個rmiregistry的程序 這樣 rmi的服務就不依賴於當前的程序了  但是  在Naming.rebind的時候 則會報錯 如下
@Test
	public void testServer() {
		
		try {
			IStusDAO istusDAO=new StusDAO();
			//System.setProperty("java.rmi.server.hostname", "192.168.38.172");
			//建立訪問rmi的遠端埠
			//啟動rmiregister程式
			Runtime.getRuntime().exec("rmiregistry 5678");
			//LocateRegistry.createRegistry(5678);
			//註冊遠端服務物件
			Naming.rebind("rmi://192.168.38.172:5678/IStusDAO", istusDAO);
			System.out.println("註冊遠端服務物件成功");
			//while(true){}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

錯誤
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
	java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
	java.lang.ClassNotFoundException: com.undergrowth.rmi.IStusDAO
	at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:400)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:248)
	at sun.rmi.transport.Transport$1.run(Transport.java:159)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)
	at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
	at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
	at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:359)
	at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
	at java.rmi.Naming.rebind(Naming.java:160)
	at com.undergrowth.junit.RmiJunit.testServer(RmiJunit.java:29)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
	java.lang.ClassNotFoundException: com.undergrowth.rmi.IStusDAO
	at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
	at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:390)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:248)
	at sun.rmi.transport.Transport$1.run(Transport.java:159)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.ClassNotFoundException: com.undergrowth.rmi.IStusDAO
	at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:249)
	at sun.rmi.server.LoaderHandler.loadProxyInterfaces(LoaderHandler.java:709)
	at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:653)
	at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:590)
	at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:628)
	at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:294)
	at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStream.java:238)
	at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1528)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1490)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1729)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1326)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
	... 12 more

原因在於  使用rmiregistry的程序的時候  在註冊istusDAO物件的時候  使用的是相對路徑查詢istusDAO的類 而rmiregistry程序根本不知道istusDAO的類在哪裡 

有解決辦法 即 進入到istusDAO的所在目錄 在執行測試方法 即可