最近做系统重构,计划将多个系统的公共部分抽取出来作为一项公共服务,为以后项目维护和横向扩展奠定基础。
常用的服务发布方式有RMI / HTTPInvoker / Hessian / Burlap,关于这几类java远程服务的性能比较和优缺点大家可参考:http://www.cnblogs.com/jifeng/archive/2011/07/20/2111183.html ,本文着重讲解怎样使用自定的注解标签,结合SPRING将服务方便的发布出去。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>8.1.3.v20120416</version>
</dependency>
package org.springframework.remoting;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RemoteService {
ServiceType serviceType() default ServiceType.HTTP;
Class<?> serviceInterface();
}
RemoteService辅助标签RmiServiceProperty,在发布RMI服务时,用来指定RMI服务发布的端口。
package org.springframework.remoting;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
import java.rmi.registry.Registry;
/**
* Created by Administrator on 2014/12/8.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RmiServiceProperty {
int registryPort() default Registry.REGISTRY_PORT;
}
ServiceType,指定发布服务的类型
package org.springframework.remoting;
public enum ServiceType {
HTTP, BURLAP, HESSIAN, RMI
}
public class ServiceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements PriorityOrdered {
private int order = Ordered.LOWEST_PRECEDENCE - 1;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
RemoteService service = AnnotationUtils.findAnnotation(bean.getClass(), RemoteService.class);
Object resultBean = bean;
if (null != service) {
if (ServiceType.HTTP == service.serviceType()) {
if(!beanName.startsWith("/")){
throw new FatalBeanException("Exception initializing HttpInvokerService for "+beanName+",beanName should bean start with \"/\".");
}
HttpInvokerServiceExporter httpInvokerServiceExporter = new HttpInvokerServiceExporter();
httpInvokerServiceExporter.setServiceInterface(service.serviceInterface());
httpInvokerServiceExporter.setService(bean);
httpInvokerServiceExporter.afterPropertiesSet();
resultBean = httpInvokerServiceExporter;
} else if (ServiceType.HESSIAN == service.serviceType()) {
if(!beanName.startsWith("/")){
throw new FatalBeanException("Exception initializing HessianService for "+beanName+",beanName should bean start with \"/\".");
}
HessianServiceExporter hessianServiceExporter = new HessianServiceExporter();
hessianServiceExporter.setServiceInterface(service.serviceInterface());
hessianServiceExporter.setService(bean);
hessianServiceExporter.afterPropertiesSet();
resultBean = hessianServiceExporter;
} else if (ServiceType.BURLAP == service.serviceType()) {
if(!beanName.startsWith("/")){
throw new FatalBeanException("Exception initializing BurlapService for "+beanName+",beanName should bean start with \"/\".");
}
BurlapServiceExporter burlapServiceExporter = new BurlapServiceExporter();
burlapServiceExporter.setServiceInterface(service.serviceInterface());
burlapServiceExporter.setService(bean);
burlapServiceExporter.afterPropertiesSet();
resultBean = burlapServiceExporter;
} else if (ServiceType.RMI == service.serviceType()) {
RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
rmiServiceExporter.setServiceInterface(service.serviceInterface());
rmiServiceExporter.setService(bean);
RmiServiceProperty rmiServiceProperty = bean.getClass().getAnnotation(RmiServiceProperty.class);
if(rmiServiceProperty!=null){
rmiServiceExporter.setRegistryPort(rmiServiceProperty.registryPort());
}
String serviceName = beanName;
if(serviceName.startsWith("/")){
serviceName = serviceName.substring(1);
}
rmiServiceExporter.setServiceName(serviceName);
try {
rmiServiceExporter.afterPropertiesSet();
} catch (RemoteException remoteException) {
throw new FatalBeanException("Exception initializing RmiServiceExporter", remoteException);
}
resultBean = rmiServiceExporter;
}
}
return resultBean;
}
/****** 参考附件 ******/
}
请注意package和Class Name必须一致
package org.springframework.context.annotation;
public class AnnotationConfigUtils {
/****** 参考附件 ******/
/**
* Register all relevant annotation post processors in the given registry.
* @param registry the registry to operate on
* @param source the configuration source element (already extracted)
* that this registration was triggered from. May be <code>null</code>.
* @return a Set of BeanDefinitionHolders, containing all bean definitions
* that have actually been registered by this call
*/
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, Object source) {
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
if (!registry.containsBeanDefinition(SERVICE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
def.setSource(source);
def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefs.add(registerPostProcessor(registry, def, SERVICE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
/******* 参考附件 *******/
return beanDefs;
}
/******* 参考附件 *******/
}
public interface HttpDateService {
public Date getDate();
}
@RemoteService(serviceInterface = HttpDateService.class, serviceType = ServiceType.HTTP)
public class HttpDateServiceImpl implements HttpDateService {
@Override
public Date getDate() {
return new Date();
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config />
<!-- id的内容对应下面服务地址 -->
<bean id="/remote/HttpDateService.service" class="me.bbvip.springremoting.http.impl.HttpDateServiceImpl"/>
</beans>
本次测试使用Junit4结合Spring容器测试服务。
Client Spring配置
<?xml version="1.0" encoding="ISO-8859-1"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!--httpInvoker client,serviceUrl对应上面HttpDateServiceImpl的ID --> <bean id="httpDateService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> <property name="serviceUrl" value="http://localhost:8080/remoting/remote/HttpDateService.service" /> <property name="serviceInterface" value="me.bbvip.springremoting.http.HttpDateService" /> </bean> </beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations ={"classpath:DateServiceTest-context.xml"})
public class ServerRunner {
private static Server server;
@BeforeClass
public static void startWebapp() throws Exception {
server = new Server();
Connector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/remoting");
webAppContext.setWar("src/main/webapp");
server.setHandler(webAppContext);
server.start();
System.out.println("syetem start sucess.");
}
@AfterClass
public static void stopWebapp() throws Exception {
server.stop();
}
}
服务接口测试
public class HttpDateServiceTest extends ServerRunner {
@Resource(name = "httpDateService")
private HttpDateService httpDateService;
@Test
public void getGetDate() {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(httpDateService.getDate()));
}
}
测试结果:
syetem start sucess. 2014-12-09 10:43:34
SPRING注解发布RMI/HTTPInvoker/Hessian/Burlap服务
原文:http://my.oschina.net/damihui/blog/354055