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 pClass.
     */
    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管理。代码如下:

/**
 * 
  • xmlrpc server 工厂
  • * * @author yangpeng 2008-8-1 上午09:18:34 */ public class XmlRpcServletServerFactoryBean extends ApplicationObjectSupport implements FactoryBean, InitializingBean { private XmlRpcServletServer server; /** XmlRpcServletServer的属性集合 */ private Map 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 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:

    /**
     * 
  • xml rpc service 注解
  • * * @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的服务方法
    * true:在服务组件 */ boolean useMethodAnnotation() default false; }

     

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

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

    /**
     * 
  • 注册spring bean的HandlerMapping
  •  *  * @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 types = clinic.getPetTypes();
    		String typesName = "";
    		for (PetType petType : types) {
    			typesName += petType.getName() + ",";
    		}
    		return typesName;
    	}
    }

      springcontext:

    	
    		
    			
    				
    			
    		
    	

     最后实现一个测试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中。详见附件。

    • xmlrpc_with_spring_beans.rar (6.5 KB)
    • 下载次数: 153
    • xml_rpc_spring_client.rar (2 KB)
    • 下载次数: 94

    你可能感兴趣的:(XML,Spring,Bean,Servlet,Web)