`
former
  • 浏览: 96528 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

xml rpc with sping beans

阅读更多

最近在处理web services时用到apache xmlrpc,这里简单总结一下。

1.选用xml rpc

web services有很多种实现,这里选用xml rpc的是因为公司各个项目都支持xml rpc的调用,其他的web services实现并非完全支持;xml rpc与平台无关;xml rpc可以保证项目与项目之间的无侵入性。

2.spring bean

由于目前的业务组件都由spring container去管理、增强,所以理想的实现是在xml rpc的server端作为服务的beans都由spring container提供。但是 xml rpc server端的默认实现中,服务beans都由classloader去加载,代码如下:

//org.apache.xmlrpc.server.PropertyHandlerMapping
public void load(ClassLoader pClassLoader, Map pMap) throws XmlRpcException {
        for (Iterator iter = pMap.entrySet().iterator();  iter.hasNext();  ) {
            Map.Entry entry = (Map.Entry) iter.next();
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            Class c = newHandlerClass(pClassLoader, value);
            registerPublicMethods(key, c);
        }
}
//org.apache.xmlrpc.metadata.Util
    /**
     * Creates a new instance of <code>pClass</code>.
     */
    public static Object newInstance(Class pClass) throws XmlRpcException {
        try {
            return pClass.newInstance();
        } catch (InstantiationException e) {
            throw new XmlRpcException("Failed to instantiate class " + pClass.getName(), e);
        } catch (IllegalAccessException e) {
            throw new XmlRpcException("Illegal access when instantiating class " + pClass.getName(), e);
        }
    }

 

 这样取得一个bean的实例并不能满足我们的要求,例如我们取的bean不能通过依赖注入得到相关属性;不能通过spring中设置的aop切面做动态的增强…………

有鉴于此,我们有必要自己实现一个xml rpc server的factorybean,并将其交由spring container管理。代码如下:

/**
 * <li>xmlrpc server 工厂</li>
 * 
 * @author yangpeng 2008-8-1 上午09:18:34
 */
public class XmlRpcServletServerFactoryBean extends ApplicationObjectSupport
		implements FactoryBean, InitializingBean {

	private XmlRpcServletServer server;
	/** XmlRpcServletServer的属性集合 */
	private Map<String, String> serverProperties;
	/** 是否在父BeanFactory中寻找xml rpc services */
	private boolean detectServersInAncestorContexts = false;
	private AbstractReflectiveHandlerMapping.AuthenticationHandler authenticationHandler;
	private RequestProcessorFactoryFactory requestProcessorFactoryFactory;
	private TypeConverterFactory typeConverterFactory;
	protected Log log = LogFactory.getLog(XmlRpcServletServerFactoryBean.class);

	public Object getObject() throws Exception {
		return server;
	}
	public Class<?> getObjectType() {
		return XmlRpcServletServer.class;
	}
	public boolean isSingleton() {
		return true;
	}
	public void afterPropertiesSet() throws Exception {
		server = new XmlRpcServletServer();
		initServerProperties();
		server.setHandlerMapping(newXmlRpcHandlerMapping());
	}
	protected void initServerProperties() {
		if (null != serverProperties) {
			Set<String> keys = serverProperties.keySet();
			for (String key : keys) {
				String value = serverProperties.get(key);
				try {
					if (!ReflectionUtil.setProperty(this, key, value)
							&& !ReflectionUtil.setProperty(server, key, value)
							&& !ReflectionUtil.setProperty(server.getConfig(),
									key, value)) {
						throw new BeanInitializationException("key:" + key
								+ ";value:" + value + " is wrong property!");
					}
				}
				catch (IllegalAccessException e) {
					log.error(e);
					throw new BeanInitializationException("key:" + key
							+ ";value:" + value + " is wrong property!");
				}
				catch (InvocationTargetException e) {
					log.error(e);
					throw new BeanInitializationException("key:" + key
							+ ";value:" + value + " is wrong property!");
				}
			}
		}
	}
	protected XmlRpcHandlerMapping newXmlRpcHandlerMapping()
			throws XmlRpcException {
		SpringPropertyHandlerMapping mapping = new SpringPropertyHandlerMapping();
		mapping.setAuthenticationHandler(authenticationHandler);
		if (requestProcessorFactoryFactory != null) {
			mapping
					.setRequestProcessorFactoryFactory(requestProcessorFactoryFactory);
		}
		if (typeConverterFactory != null) {
			mapping.setTypeConverterFactory(typeConverterFactory);
		}
		else {
			mapping.setTypeConverterFactory(server.getTypeConverterFactory());
		}
		mapping.setVoidMethodEnabled(server.getConfig()
				.isEnabledForExtensions());
		mapping.addHandler(detectServersInAncestorContexts,
				getApplicationContext());
		return mapping;
	}
//省略getter、setter
}

 

 SpringPropertyHandlerMapping继承于PropertyHandlerMapping,重载addHandler方法,将ApplicationContext作为参数传入,addHandler中的实现类似于spring2.5 MVC中查找声明@controller的Controller类的实现。为了能获得这样的效果,我们先要定义两个Annotation:

/**
 * <li>xml rpc service 注解</li>
 * 
 * @author yangpeng 2008-8-1 下午03:37:34
 */
@Target( { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface XmlRpcService {

	/**
	 * value为空则xml rpc service的名称默认使用sping bean的id。否则使用value
	 */
	String value() default "";
	/**
	 * 是否使用方法注解
	 * 
	 * @return false:默认服务组件中的所有公共方法都作为xml rpc的服务方法 <br>
	 *         true:在服务组件
	 */
	boolean useMethodAnnotation() default false;
}

 

/**
 * <li>标注此方法会作为xmlrpc server的响应方法</li>
 * 
 * @author yangpeng 2008-8-1 下午03:33:12
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlRpcMethod {

	String value() default "";
}

 接着来看看SpringPropertyHandlerMapping,在这里它是核型

/**
 * <li>注册spring bean的HandlerMapping</li>
 * 
 * @author yangpeng 2008-8-1 上午10:42:21
 */
public class SpringPropertyHandlerMapping extends PropertyHandlerMapping {

	public void addHandler(boolean detectServersInAncestorContexts,
			final ApplicationContext context) throws XmlRpcException {
		Assert.notNull(context, "context must not be null!");
		String[] beanNames = (detectServersInAncestorContexts ? BeanFactoryUtils
				.beanNamesForTypeIncludingAncestors(context, Object.class)
				: context.getBeanNamesForType(Object.class));
		for (String beanName : beanNames) {
			registerPublicMethods(beanName, context);
		}
	}
	@SuppressWarnings( { "unchecked", "unchecked" })
	protected void registerPublicMethods(String beanName,
			final ApplicationContext context) throws XmlRpcException {
		Class<?> serviceType = context.getType(beanName);
		XmlRpcService service = AnnotationUtils.findAnnotation(serviceType,
				XmlRpcService.class);
		if (service == null
				&& context instanceof ConfigurableApplicationContext
				&& context.containsBeanDefinition(beanName)) {
			ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context;
			BeanDefinition bd = cac.getBeanFactory().getMergedBeanDefinition(
					beanName);
			if (bd instanceof AbstractBeanDefinition) {
				AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
				if (abd.hasBeanClass()) {
					Class<?> beanClass = abd.getBeanClass();
					serviceType = beanClass;// 得到被代理对象
					service = AnnotationUtils.findAnnotation(beanClass,
							XmlRpcService.class);
				}
			}
		}
		if (service != null) {
			Map map = new HashMap();
			Method[] methods = serviceType.getMethods();
			for (Method method : methods) {
				if (!isHandlerMethod(service.useMethodAnnotation(), method)) {
					continue;
				}
				String serviceName = StringUtils.isEmpty(service.value()) ? beanName
						: service.value();
				String name = serviceName + "." + method.getName();
				Method[] mArray;
				Method[] oldMArray = (Method[]) map.get(name);
				if (oldMArray == null) {
					mArray = new Method[] { method };
				}
				else {
					mArray = new Method[oldMArray.length + 1];
					System.arraycopy(oldMArray, 0, mArray, 0, oldMArray.length);
					mArray[oldMArray.length] = method;
				}
				map.put(name, mArray);
			}
			for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
				Map.Entry entry = (Map.Entry) iter.next();
				String name = (String) entry.getKey();
				Method[] mArray = (Method[]) entry.getValue();
				handlerMap.put(name, newXmlRpcHandler(
						context.getBean(beanName), mArray));
			}
		}
	}
	protected XmlRpcHandler newXmlRpcHandler(final Object bean,
			final Method[] pMethods) throws XmlRpcException {
		String[][] sig = getSignature(pMethods);
		String help = getMethodHelp(bean.getClass(), pMethods);
		if (sig == null || help == null) {
			return new SpringXmlRpcHandler(this, getTypeConverterFactory(),
					bean, pMethods);
		}
		return new SpringReflectiveXmlRpcMetaDataHandler(this,
				getTypeConverterFactory(), bean, pMethods, sig, help);
	}
	protected boolean isHandlerMethod(boolean useMethodAnnotation, Method method) {
		if (useMethodAnnotation) {
			XmlRpcMethod xmlRpcMethod = AnnotationUtils.getAnnotation(method,
					XmlRpcMethod.class);
			if (null == xmlRpcMethod) {
				return false;
			}
		}
		return super.isHandlerMethod(method);
	}
}

 简单解释一下。SpringPropertyHandlerMapping遍历spring container中所有注册的beans,查找使用了@XmlRpcService注解的bean(无论其是否被代理)。对于这样的bean认为提供xml rpc服务,然后查找其声明的xml rpc服务方法。默认情况下,其所有的public、非static、非Object类方法的方法都会被当作xml rpc的服务方法(有点拗口)。如果在XmlRpcService注解中声明useMethodAnnotation为true,则其method除了要满足以上条件外,还要必须声明XmlRpcMethod注解才会被认为是xml rpc的服务方法。

SpringXmlRpcHandler与SpringReflectiveXmlRpcMetaDataHandler都非常简单,类似于默认实现的ReflectiveXmlRpcHandler、ReflectiveXmlRpcMetaDataHandler,这里就不多展开演示了。

3.例子

server1:

使用spring自带的petclinic,为业务实现一个xml rpc的faced:

package org.springframework.samples.petclinic.xmlrpcfaced;

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.PetType;

import xmlrpc.annotation.XmlRpcService;

@XmlRpcService
public class PetFaced {

	@Autowired
	Clinic clinic;

	public String getPetTypesName() {
		Collection<PetType> types = clinic.getPetTypes();
		String typesName = "";
		for (PetType petType : types) {
			typesName += petType.getName() + ",";
		}
		return typesName;
	}
}

  springcontext:

	<bean id="rpcServer"
		class="xmlrpc.server.XmlRpcServletServerFactoryBean">
		<property name="serverProperties">
			<map>
				<entry key="enabledForExtensions" value="true"></entry>
			</map>
		</property>
	</bean>

 最后实现一个测试filter:

public class SpringXmlRpcFilter extends OncePerRequestFilter {

	public static final String DEFAULT_XML_RPC_SERVIER_NAME = "xmlRpcServer";
	private String servierName = DEFAULT_XML_RPC_SERVIER_NAME;
	private XmlRpcServletServer server;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		server.execute(request, response);
		// filterChain.doFilter(request, response);
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.web.filter.GenericFilterBean#initFilterBean()
	 */
	@Override
	protected void initFilterBean() throws ServletException {
		WebApplicationContext wac = WebApplicationContextUtils
				.getRequiredWebApplicationContext(getServletContext());
		server = (XmlRpcServletServer) wac.getBean(servierName,
				XmlRpcServletServer.class);
	}
	public String getServierName() {
		return servierName;
	}
	public void setServierName(String servierName) {
		this.servierName = servierName;
	}
}

 server2:

使用被spring container代理过的service、具有事务属性的bean作为xml rpc的service

@XmlRpcService(useMethodAnnotation = true)
@Transactional
public class HibernateClinic implements Clinic {
	@Autowired
	private SessionFactory sessionFactory;
	@XmlRpcMethod
	@Transactional(readOnly = true)
	public String getTypeName(int id) throws DataAccessException {
		return String.valueOf(sessionFactory.getCurrentSession().createQuery(
				"select name from PetType type where id = ?").setInteger(0, id)
				.uniqueResult());
	}
//其他方法省略
}

 其他地方都一样,只是省略了faced。

个人总结

xml rpc的明显局限是对于复杂、用户自定义java bean的支持很弱,只支持一些基本的类型。

性能方面我没有做过测试,不能乱讲。

目前3.1的发布版本的客户端代码的默认实现有线程不安全的bug,这个问题在后来的2007年10月份的版本中才被修复,但是提供给大家下载的版本为8月份的版本,一个有问题的版本,大家注意了!

那个filter有个小问题不知道大家发现了没有,就是如果使用filterChain.doFilter(request, response);这句代码程序会报异常,具体原因我没仔细查,建议大家写个servlet做代替,文档中也是用servlet去做的。

后记

补充一个xml rpc client的包装类,使得客户端也可以通过spring容器管理,注入到需要的service中。详见附件。

分享到:
评论
5 楼 crazyox 2009-02-25  
很好, 很强大
4 楼 former 2008-08-04  
xufei547 写道
实在是太强了!!
学习ing

强在哪里呢?
不要这样说。。。
能让你有所收获即可。。。
3 楼 xufei547 2008-08-04  
实在是太强了!!
学习ing
2 楼 luopan68 2008-08-04  
不错,受益了
1 楼 former 2008-08-03  
<p>做一个简单的改进:使用@Resource注解。</p>
<pre name='code' class='java'>public class XmlRpcServletServerFactoryBean implements FactoryBean,
InitializingBean {

@Resource
private ApplicationContext context;
//........
}</pre>
<p>取消对ApplicationObjectSupport.java的继承。</p>

相关推荐

    sping3 spring-beans 类图 chm

    sping3 spring-beans 类图 chm

    Sping视频2 Sping视频2Sping视频2Sping视频2

    Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2Sping视频2

    spring中xml文件不提示

    在探讨“spring中xml文件不提示”的问题时,我们首先需要理解Spring框架的基本概念以及XML配置文件在其中扮演的角色。Spring框架是一个开源的轻量级Java应用框架,旨在简化企业级应用开发。它通过依赖注入(DI)和...

    Sping的jar包

    它的配置可以通过XML或.properties文件进行,方便调整日志行为。 在实际开发中,Spring的jar包通常会被引入到项目的类路径中,通过Maven或Gradle等构建工具管理依赖。开发人员可以利用Spring提供的注解驱动开发,...

    Sping入门小例子

    例如,XML配置文件(如`beans.xml`)可能会这样写: ```xml &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=...

    mybatis+sping mvc mybatis+sping mvc

    mybatis+sping mvc mybatis+sping mvcmybatis+sping mvc mybatis+sping mvcmybatis+sping mvc mybatis+sping mvcmybatis+sping mvc mybatis+sping mvcmybatis+sping mvc mybatis+sping mvc

    sping配置jar包

    1. **spring-beans-2.5.6.jar**:这是 Spring 的核心库之一,主要用于 Bean 的生命周期管理和依赖注入。Bean 是 Spring 中的基本单元,代表应用程序中的对象。这个 jar 包包含了 Bean 定义、配置元数据解析以及 Bean...

    sping配置数据源

    &lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop=...

    sping 技术的主要讲解

    sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术sping 技术

    spingmvc+hibernate+sping

    sping+spingmvc+hibernate 在学习的时候毕业前夕自己根据资料和以前自己写的项目整合的套,对包哪些进行了选择,主要对事物aop的配置进行了配置,个人感觉不错,共享下,另外还对spingmvc,mybatis的整合项目也有,在...

    springmvc+sping+mybatis完整实例

    3. `src/main/resources`:资源文件夹,存放配置文件,如Spring的`applicationContext.xml`和`springmvc-dispatcher-servlet.xml`,以及MyBatis的`mybatis-config.xml`和Mapper接口对应的XML映射文件。 4. `src/main...

    Sping定时器的使用

    标题中的“Spring定时器的使用”指的是Spring框架中的任务调度功能,主要由Spring的`TaskExecution`和`TaskScheduling`模块提供。Spring通过`@Scheduled`注解和`TaskScheduler`接口,允许开发者轻松地实现定时任务,...

    Sping最新资源包

    通过XML配置或者基于注解的方式,开发者可以声明对象及其依赖,由IoC容器负责创建和装配这些对象。这种方式降低了对象间的耦合性,使得代码更易于测试和维护。 AOP则提供了在不修改原有代码的情况下,插入额外功能...

    sping 3 经典教程

    《Spring 3经典教程》是IT领域内关于Spring框架的一部权威著作,由Gary Mak、Josh Long和Daniel Rubio三位专家联合编写。此书专为希望深入掌握Spring 3框架的开发者设计,通过一系列编码实例(coding recipes)来...

    sping配置用户密码加密解密

    - **ConfigLoad.txt**:这可能是关于配置加载逻辑的文件,Spring支持通过XML、Java配置或@Configuration注解的方式来加载和管理配置。在这个场景下,它可能涉及到如何加载和使用上述的加密解密配置和数据库连接池...

    sping MVC 简单小例子

    最后,"HelloWorld"项目中的其他文件可能包括配置文件(如`servlet-context.xml`或`web.xml`)、测试类以及必要的依赖库。配置文件会定义DispatcherServlet、Controller的映射、过滤器的注册等。 总结,这个"Spring...

    Sping翻转控制器

    ### Sping翻转控制器知识点详解 #### 一、Spring IoC 容器概述 ##### 1.1 控制反转(Inversion of Control, IoC)原理 - **定义**:控制反转是一种设计模式,用于降低代码之间的耦合度。在Spring框架中,IoC容器...

    sping的jar包

    XML配置和基于注解的配置都是Spring中定义Bean的方式。 2. **依赖注入(Dependency Injection, DI)**:Spring通过DI实现了对象之间的松耦合。DI允许Spring框架在运行时将依赖关系传递给Bean,而不是由Bean自己去...

Global site tag (gtag.js) - Google Analytics