SPRING註解發佈RMI/HTTPInvoker/Hessian/Burlap服務

最近作系統重構,計劃將多個系統的公共部分抽取出來做爲一項公共服務,爲之後項目維護和橫向擴展奠基基礎。 html

經常使用的服務發佈方式有RMI / HTTPInvoker / Hessian / Burlap,關於這幾類java遠程服務的性能比較和優缺點你們可參考:http://www.cnblogs.com/jifeng/archive/2011/07/20/2111183.html ,本文着重講解怎樣使用自定的註解標籤,結合SPRING將服務方便的發佈出去。 java

1、 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>


2、自定義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服務發佈的端口。 git

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,指定發佈服務的類型 web

package org.springframework.remoting;

public enum ServiceType {
    HTTP, BURLAP, HESSIAN, RMI
}

3、 定義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;
    }

    /******      參考附件     ******/
}


4、重寫Spring框架AnnotationConfigUtils

請注意package和Class Name必須一致 spring

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;
	}

	/*******    參考附件    *******/

}


5、發佈服務

        服務接口

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>

      其餘類型的服務發佈請參考附件。 mvc

6、測試服務

本次測試使用Junit4結合Spring容器測試服務。 app

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();
    }
}

服務接口測試 eclipse

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()));
    }
}

測試結果: webapp

syetem start sucess.
2014-12-09 10:43:34


源碼地址:https://git.oschina.net/damivip/spring-remoting-annotation.git

相關文章
相關標籤/搜索