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的所在目录 在运行测试方法 即可