RMI簡單例項與Spring整合RMI
阿新 • • 發佈:2019-02-09
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的所在目錄 在執行測試方法 即可