使用自定义的spring注解标签发布RMI/HTTPInvoker/Hessian/Burlap远程服务。
1.依赖jar的pom.xml配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.slimsmart.spring.rpc.annotation.demo</groupId> <artifactId>spring-rpc-annotation-demo</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.0.9.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>4.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-webapp</artifactId> <version>8.1.3.v20120416</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.5</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclipse-plugin</artifactId> <version>2.9</version> <configuration> <downloadSources>true</downloadSources> <downloadJavadocs>true</downloadJavadocs> </configuration> </plugin> </plugins> </build> </project>2.改造spring,使其支持注解发布不同服务
1)定义远程发布服务注解
RemoteService.java
package org.springframework.remoting.annotation; 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; /** * 远程发布注解RemoteService */ @Target({ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface RemoteService { ServiceType serviceType() default ServiceType.HTTP; Class<?> serviceInterface(); }2)RmiServiceProperty.java
RemoteService辅助标签,在发布RMI服务时,用来指定RMI服务发布的端口。
package org.springframework.remoting.annotation; 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 java.rmi.registry.Registry; import org.springframework.stereotype.Component; /** * RemoteService辅助标签,在发布RMI服务时,用来指定RMI服务发布的端口。 */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface RmiServiceProperty { int registryPort() default Registry.REGISTRY_PORT; }3)定义发布服务的类型枚举
package org.springframework.remoting.annotation; /** * ServiceType,指定发布服务的类型 */ public enum ServiceType { HTTP, BURLAP, HESSIAN, RMI }4)注解解析实现
package org.springframework.factory.annotation; import java.rmi.RemoteException; import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.remoting.annotation.RemoteService; import org.springframework.remoting.annotation.RmiServiceProperty; import org.springframework.remoting.annotation.ServiceType; import org.springframework.remoting.caucho.BurlapServiceExporter; import org.springframework.remoting.caucho.HessianServiceExporter; import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter; import org.springframework.remoting.rmi.RmiServiceExporter; /** * 解析RemoteService注解标签,发布服务 */ @SuppressWarnings("deprecation") 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) { //HttpInvoker 服务发布 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; //Hessian 服务发布 } 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; //Burlap 服务发布 } 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; //RIM 服务发布 } 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; } @Override public int getOrder() { return order; } }5.重写spring注解配置工具类AnnotationConfigUtils.java 需与spring的在同一包目录
/* * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.annotation; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotationMetadata; import org.springframework.factory.annotation.ServiceAnnotationBeanPostProcessor; import org.springframework.util.ClassUtils; /** * Utility class that allows for convenient registration of common * {@link org.springframework.beans.factory.config.BeanPostProcessor} and * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor} * definitions for annotation-based configuration. Also registers a common * {@link org.springframework.beans.factory.support.AutowireCandidateResolver}. * * @author Mark Fisher * @author Juergen Hoeller * @author Chris Beams * @author Phillip Webb * @since 2.5 * @see ContextAnnotationAutowireCandidateResolver * @see CommonAnnotationBeanPostProcessor * @see ConfigurationClassPostProcessor * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor * @see org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor * @see org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor */ //重写Spring框架 AnnotationConfigUtils,与spring相同包目录 public class AnnotationConfigUtils { /** * The bean name of the internally managed Configuration annotation processor. */ public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.factory.annotation.internalConfigurationAnnotationProcessor"; /*** * 定义注解解析实现 */ public static final String SERVICE_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.remoting.ServiceAnnotationBeanPostProcessor"; /** * The bean name of the internally managed BeanNameGenerator for use when processing * {@link Configuration} classes. Set by {@link AnnotationConfigApplicationContext} * and {@code AnnotationConfigWebApplicationContext} during bootstrap in order to make * any custom name generation strategy available to the underlying * {@link ConfigurationClassPostProcessor}. * @since 3.1.1 */ public static final String CONFIGURATION_BEAN_NAME_GENERATOR = "org.springframework.context.annotation.internalConfigurationBeanNameGenerator"; /** * The bean name of the internally managed Autowired annotation processor. */ public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"; /** * The bean name of the internally managed Required annotation processor. */ public static final String REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalRequiredAnnotationProcessor"; /** * The bean name of the internally managed JSR-250 annotation processor. */ public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalCommonAnnotationProcessor"; /** * The bean name of the internally managed JPA annotation processor. */ public static final String PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalPersistenceAnnotationProcessor"; private static final String PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME = "org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"; /** * The bean name of the internally managed Scheduled annotation processor. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalScheduledAnnotationProcessor"; /** * The bean name of the internally managed Async annotation processor. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalAsyncAnnotationProcessor"; /** * The bean name of the internally managed AspectJ async execution aspect. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String ASYNC_EXECUTION_ASPECT_BEAN_NAME = "org.springframework.scheduling.config.internalAsyncExecutionAspect"; /** * The class name of the AspectJ async execution aspect. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME = "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect"; /** * The name of the AspectJ async execution aspect @{@code Configuration} class. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; /** * The bean name of the internally managed cache advisor. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String CACHE_ADVISOR_BEAN_NAME = "org.springframework.cache.config.internalCacheAdvisor"; /** * The bean name of the internally managed cache aspect. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String CACHE_ASPECT_BEAN_NAME = "org.springframework.cache.config.internalCacheAspect"; /** * The class name of the AspectJ caching aspect. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String CACHE_ASPECT_CLASS_NAME = "org.springframework.cache.aspectj.AnnotationCacheAspect"; /** * The name of the AspectJ caching aspect @{@code Configuration} class. * <p>ATTENTION:</p> This constant is meant for internal use only. The value is stable * but don't rely on the presence of this constant declaration; rather copy the value. */ public static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJCachingConfiguration"; private static final boolean jsr250Present = ClassUtils.isPresent("javax.annotation.Resource", AnnotationConfigUtils.class.getClassLoader()); private static final boolean jpaPresent = ClassUtils.isPresent("javax.persistence.EntityManagerFactory", AnnotationConfigUtils.class.getClassLoader()) && ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader()); /** * Register all relevant annotation post processors in the given registry. * @param registry the registry to operate on */ public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } /** * 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}. * @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) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4); /** * 非ServiceAnnotationBeanPostProcessor类处理 */ 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)); } if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } return beanDefs; } private static BeanDefinitionHolder registerPostProcessor( BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) { definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(beanName, definition); return new BeanDefinitionHolder(definition, beanName); } private static DefaultListableBeanFactory unwrapDefaultListableBeanFactory(BeanDefinitionRegistry registry) { if (registry instanceof DefaultListableBeanFactory) { return (DefaultListableBeanFactory) registry; } else if (registry instanceof GenericApplicationContext) { return ((GenericApplicationContext) registry).getDefaultListableBeanFactory(); } else { return null; } } public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { processCommonDefinitionAnnotations(abd, abd.getMetadata()); } static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { if (metadata.isAnnotated(Lazy.class.getName())) { abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value")); } else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) { abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value")); } if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } if (metadata.isAnnotated(DependsOn.class.getName())) { abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value")); } if (abd instanceof AbstractBeanDefinition) { AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd; if (metadata.isAnnotated(Role.class.getName())) { absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue()); } if (metadata.isAnnotated(Description.class.getName())) { absBd.setDescription(attributesFor(metadata, Description.class).getString("value")); } } } static BeanDefinitionHolder applyScopedProxyMode( ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode(); if (scopedProxyMode.equals(ScopedProxyMode.NO)) { return definition; } boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); } static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, Class<?> annotationClass) { return attributesFor(metadata, annotationClass.getName()); } static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, String annotationClassName) { return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName, false)); } static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata, Class<?> containerClass, Class<?> annotationClass) { return attributesForRepeatable(metadata, containerClass.getName(), annotationClass.getName()); } @SuppressWarnings("unchecked") static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata, String containerClassName, String annotationClassName) { Set<AnnotationAttributes> result = new LinkedHashSet<AnnotationAttributes>(); addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false)); Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false); if (container != null && container.containsKey("value")) { for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) { addAttributesIfNotNull(result, containedAttributes); } } return Collections.unmodifiableSet(result); } private static void addAttributesIfNotNull(Set<AnnotationAttributes> result, Map<String, Object> attributes) { if (attributes != null) { result.add(AnnotationAttributes.fromMap(attributes)); } } }
3.测试类
先使用jetty启动web服务,再通过客户端调用
ServerRunner.java
package cn.slimsmart.spring.rpc.annotation.demo; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * 使用jetty作为服务端,运行测试前,先启动startWebapp */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:applicationContext-test.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); //设置webapp端口 server.addConnector(connector); WebAppContext webAppContext = new WebAppContext(); //设置工程名称 webAppContext.setContextPath("/remoting"); //这是webapp目录 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(); } }UserServiceTest.java
package cn.slimsmart.spring.rpc.annotation.demo; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; /** * UserService测试类 */ public class UserServiceTest extends ServerRunner{ @Autowired private UserService userService; @Test public void getUser() { System.out.println(userService.getUser("slim")); } }参考: http://my.oschina.net/damihui/blog/354055