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的所在目录 在运行测试方法 即可
posted on 2014-05-03 13:18 liangxinzhi 阅读(255) 评论(0) 编辑 收藏 举报