我们在使用spring rmi实现服务间远程调用时,每一个普通的service要想导出成远程服务供其他服务调用,都需要在spring的配置文件中配置一个org.springframework.remoting.rmi.RmiServiceExporter类的bean。一般都采用下面这种配置方法:
<bean name="serviceARmiExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service" ref="serviceA" />
<property name="serviceName" value="serviceA" />
<property name="serviceInterface" value="com.springrmi.service.IServiceA" />
<property name="registryPort" value="${rmi.port}" />
</bean>
<bean name="serviceBRmiExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service" ref="serviceB" />
<property name="serviceName" value="serviceB" />
<property name="serviceInterface" value="com.springrmi.service.IServiceB" />
<property name="registryPort" value="${rmi.port}" />
</bean>
。。。。。。。其他的远程服务配置
可以看到上面这样的配置太过臃肿,对于看过spring源码的人来说,一般都了解spring是先针对某个类来注册一个BeanDefinition,然后通过BeanDefinition来创建Bean实例,所以我们可以通过程序来批量创建这些RmiServiceExporter类的BeanDefinition,然后将这些BeanDefinition注册到spring容器中,最后由spring容器创建RmiServiceExporter实例供其他服务调用。
接下来我们来看看spring容器主要初始化方法,AbstractApplicationContext的refresh()方法。
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
从这个方法中可以看到,我们可以从三个地方可以介入到spring BeanDefinition的注册过程中,
1、继承某一个AbstractApplicationContext的子类,覆盖postProcessBeanFactory方法,在这个方法中批量注册RmiServiceExporter的BeanDefinition到容器中。
2、实现一个BeanFactoryPostProcessor接口,在该接口的postProcessBeanFactory方法中批量注册RmiServiceExporter BeanDefinition。
3、finishRefresh方法中,会触发ContextRefreshedEvent事件,我们可以针对该事件写一个ApplicationListener,也可以批量注册RmiServiceExporter BeanDefinition。
下面通过第一种方式实现下批量注册RmiServiceExporter BeanDefinition,其他两种方法大同小异。
(1)首先定义一个接口用于标识业务类需要导出成远程服务。
public interface RmiServiceExporterAutoRegistrar {
// String getServiceInterface();
}
(2)编写两个普通的业务接口和实现类
public interface IUserService {
User getOneUser();
String addUser();
String updateUser();
}
public interface IGroupService {
String addGroup();
String updateGroup();
String deleteGroup();
}
@Service(value = "userService")
public class UserService implements IUserService,
RmiServiceExporterAutoRegistrar {
private static final Log LOG = LogFactory.getLog(UserService.class);
@Override
public User getOneUser() {
User user = new User();
user.setAge(Integer.valueOf(1000));
user.setEmail("sunwukong@qq.com");
user.setNickname("孙悟空");
user.setPassword("123456");
user.setUsername("sunwukong");
return user;
}
@Override
public String addUser() {
LOG.info("新增用户");
return "已成功新增用户!";
}
@Override
public String updateUser() {
LOG.info("修改用户");
return "已成功修改用户!";
}
}
@Service(value = "groupService")
public class GroupService implements IGroupService,
RmiServiceExporterAutoRegistrar {
private static final Log LOG = LogFactory.getLog(GroupService.class);
@Override
public String addGroup() {
LOG.info("新增用户组!");
return "已成功新增用户组!";
}
@Override
public String updateGroup() {
LOG.info("修改用户组!");
return "已成功修改用户组!";
}
@Override
public String deleteGroup() {
LOG.info("删除用户组!");
return "已成功删除用户组!";
}
}
业务实现类一定要实现RmiServiceExporterAutoRegistrar,并且放在implements关键字后面的第二个接口。
(3)覆盖AbstractApplicationContext的postProcessBeanFactory方法。
public class MyAnnotationConfigApplicationContext extends
AnnotationConfigApplicationContext {
public MyAnnotationConfigApplicationContext() {
super();
}
public MyAnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
super(annotatedClasses);
}
public MyAnnotationConfigApplicationContext(
DefaultListableBeanFactory beanFactory) {
super(beanFactory);
}
public MyAnnotationConfigApplicationContext(String... basePackages) {
super(basePackages);
}
@Override
protected void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) {
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
String[] autoRegisterBeanNameArr = listableBeanFactory
.getBeanNamesForType(RmiServiceExporterAutoRegistrar.class);
BeanDefinitionBuilder beanDefinitionBuilder = null;
Class<?> tempBeanClass = null;
for (String tempBeanName : autoRegisterBeanNameArr) {
logger.info("auto registrar bean name>>>>>>>>>>>>>"
+ tempBeanName);
AbstractBeanDefinition tempBeanDefinition = (AbstractBeanDefinition) listableBeanFactory
.getBeanDefinition(tempBeanName);
try {
tempBeanClass = ClassUtils.forName(
tempBeanDefinition.getBeanClassName(),
ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (LinkageError e) {
e.printStackTrace();
}
beanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(RmiServiceExporter.class);
beanDefinitionBuilder.addPropertyReference("service", tempBeanName);
beanDefinitionBuilder.addPropertyValue("serviceName", tempBeanName);
Class<?>[] interfaces = tempBeanClass.getInterfaces();
String interfaceName = interfaces[0].getName();
beanDefinitionBuilder.addPropertyValue("serviceInterface",
interfaceName);
beanDefinitionBuilder.addPropertyValue("registryPort",
System.getProperty("RMI_PORT", "1099"));
listableBeanFactory.registerBeanDefinition(tempBeanName
+ "Exporter", beanDefinitionBuilder.getBeanDefinition());
}
}
}
(4)启动rmi服务端和客户端
//spring rmi服务端启动程序
public class RmiServiceStarter {
public static void main(String[] args) {
MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext(
"org.yanfeilin.helloworld.spring.annotations.service");
}
}
//spring rmi客户端调用程序
@Configuration
public class SpringRmiClient {
private static final Log LOG = LogFactory.getLog(SpringRmiClient.class);
@Bean(name = "userService")
public RmiProxyFactoryBean userService() {
RmiProxyFactoryBean userServiceProxy = new RmiProxyFactoryBean();
userServiceProxy.setServiceUrl("rmi://localhost:1099/userService");
userServiceProxy.setServiceInterface(IUserService.class);
userServiceProxy.setRefreshStubOnConnectFailure(true);
userServiceProxy.setLookupStubOnStartup(true);
return userServiceProxy;
}
@Bean(name = "groupService")
public RmiProxyFactoryBean groupService() {
RmiProxyFactoryBean groupServiceProxy = new RmiProxyFactoryBean();
groupServiceProxy.setServiceUrl("rmi://localhost:1099/groupService");
groupServiceProxy.setServiceInterface(IGroupService.class);
groupServiceProxy.setRefreshStubOnConnectFailure(true);
groupServiceProxy.setLookupStubOnStartup(true);
return groupServiceProxy;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext clientApplicationContext = new AnnotationConfigApplicationContext(
SpringRmiClient.class);
IUserService userService = (IUserService) clientApplicationContext
.getBean("userService");
LOG.info(userService.addUser());
LOG.info(userService.updateUser());
IGroupService groupService = (IGroupService) clientApplicationContext
.getBean("groupService");
LOG.info(groupService.addGroup());
LOG.info(groupService.updateGroup());
LOG.info(groupService.deleteGroup());
}
}
以上就是实现简化spring rmi服务端配置的方法,我们在使用spring其他组件(比如jms服务提供者配置)的时候,同样可以用这样的思路简化某些臃肿的配置。