最近做系统重构,计划将多个系统的公共部分抽取出来作为一项公共服务,为以后项目维护和横向扩展奠定基础。
常用的服务发布方式有RMI / HTTPInvoker / Hessian / Burlap,关于这几类java远程服务的性能比较和优缺点大家可参考:http://www.cnblogs.com/jifeng/archive/2011/07/20/2111183.html ,本文着重讲解怎样使用自定的注解标签,结合SPRING将服务方便的发布出去。
一、 Maven配置
<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>
二、自定义Annotation注解标签(RemoteService)
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
}
三、 定义Annotation标签解释器
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;
}
/****** 参考附件 ******/
}
四、重写Spring框架AnnotationConfigUtils
请注意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();
}
}
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"
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/
ContextConfiguration)初始化Spring容器,测试具体的逻辑前,先将项目发布到Jetty容器中。
@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
源码地址:https://git.oschina.net/damivip/spring-remoting-annotation.git
来源:oschina
链接:https://my.oschina.net/u/853141/blog/354055