SPRING注解发布RMI/HTTPInvoker/Hessian/Burlap服务

最近做系统重构,计划将多个系统的公共部分抽取出来作为一项公共服务,为以后项目维护和横向扩展奠定基础。

常用的服务发布方式有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





郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。