Spring自定义配置Schema可扩展(二)
命名空间支持
要实现命名空间支持,需要继承自NamespaceHandlerSupport。
packagecom.codestd.spring.cxf.config.schema; importorg.springframework.beans.factory.xml.NamespaceHandlerSupport; importcom.codestd.spring.cxf.config.EndpointBeanProcessor; /** *处理命名空间 *@authorjaune(WangChengwei) *@since1.0.0 */ publicclassWebServiceAnnotationNamespaceHandlerextendsNamespaceHandlerSupport{ @Override publicvoidinit(){ //TODOAuto-generatedmethodstub this.registerBeanDefinitionParser("annotation-endpoint",newAnnotationBeanDefinitionParser(EndpointBeanProcessor.class)); } }
通过registerBeanDefinitionParser方法讲配置支持添加到Spring中。annotation-endpoint是配置支持的元素。AnnotationBeanDefinitionParser是处理配置的类。EndpointBeanProcessor是处理@Endpoint注解的Bean的类,后面会有详细的讲述。
处理配置
需要实现BeanDefinitionParser
packagecom.codestd.spring.cxf.config.schema; importorg.springframework.beans.factory.config.BeanDefinition; importorg.springframework.beans.factory.support.RootBeanDefinition; importorg.springframework.beans.factory.xml.BeanDefinitionParser; importorg.springframework.beans.factory.xml.ParserContext; importorg.springframework.util.StringUtils; importorg.w3c.dom.Element; /** *@authorjaune(WangChengwei) *@since1.0.0 */ publicclassAnnotationBeanDefinitionParserimplementsBeanDefinitionParser{ privatefinalClass<?>beanClass; publicAnnotationBeanDefinitionParser(Class<?>beanClass){ this.beanClass=beanClass; } @Override publicBeanDefinitionparse(Elementelement,ParserContextparserContext){ RootBeanDefinitionbeanDefinition=newRootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); Stringid=element.getAttribute("id"); if(id==null||id.length()==0){ Stringname=element.getAttribute("name"); if(!StringUtils.isEmpty(name))id=name; elseid=beanClass.getName(); } if(parserContext.getRegistry().containsBeanDefinition(id)){ thrownewIllegalStateException("Duplicatespringbeanid"+id); } parserContext.getRegistry().registerBeanDefinition(id,beanDefinition); StringannotationPackage=element.getAttribute("package"); if(!StringUtils.isEmpty(annotationPackage)) beanDefinition.getPropertyValues().add("annotationPackage",annotationPackage); returnbeanDefinition; } }
BeanDefinitionParser的应用参见Spring官方文档。
Bean注册工具类
packagecom.codestd.spring.cxf.config; importorg.springframework.beans.BeansException; importorg.springframework.beans.factory.config.BeanDefinition; importorg.springframework.beans.factory.support.BeanDefinitionBuilder; importorg.springframework.beans.factory.support.BeanDefinitionRegistry; importorg.springframework.context.ApplicationContext; importorg.springframework.context.ApplicationContextAware; importorg.springframework.context.ConfigurableApplicationContext; /** *RegistryBean.MustinjectthespringApplicationContext. *@authorjaune(WangChengwei) *@since1.0.0 */ publicclassBeanRegistryimplementsApplicationContextAware{ privateApplicationContextapplicationContext; privateConfigurableApplicationContextconfigurableApplicationContext; @Override publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{ this.applicationContext=applicationContext; if(applicationContextinstanceofConfigurableApplicationContext){ this.configurableApplicationContext=(ConfigurableApplicationContext)this.applicationContext; } } publicBeanRegistry(){ } publicBeanRegistry(ApplicationContextapplicationContext){ this.setApplicationContext(applicationContext); } publicBeanDefinitionregister(Class<?>clazz){ if(configurableApplicationContext==null)returnnull; BeanDefinitionRegistrybeanDefinitonRegistry= (BeanDefinitionRegistry)configurableApplicationContext.getBeanFactory(); BeanDefinitionBuilderbeanDefinitionBuilder=this.createBuilder(clazz); BeanDefinitionbeanDefinition=beanDefinitionBuilder.getRawBeanDefinition(); beanDefinitonRegistry.registerBeanDefinition(clazz.getName(),beanDefinition); returnbeanDefinition; } privateBeanDefinitionBuildercreateBuilder(Class<?>clazz){ BeanDefinitionBuilderbeanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(clazz); returnbeanDefinitionBuilder; } }
处理@Endpoint
packagecom.codestd.spring.cxf.config; importorg.springframework.beans.BeansException; importorg.springframework.beans.factory.DisposableBean; importorg.springframework.beans.factory.config.BeanFactoryPostProcessor; importorg.springframework.beans.factory.config.BeanPostProcessor; importorg.springframework.beans.factory.config.ConfigurableListableBeanFactory; importorg.springframework.beans.factory.support.BeanDefinitionRegistry; importorg.springframework.context.ApplicationContext; importorg.springframework.context.ApplicationContextAware; importorg.springframework.context.annotation.ClassPathBeanDefinitionScanner; importorg.springframework.core.type.filter.AnnotationTypeFilter; importorg.springframework.util.StringUtils; importcom.codestd.spring.cxf.annotation.Endpoint; /** *@authorjaune(WangChengwei) *@since1.0.0 */ publicclassEndpointBeanProcessorimplements BeanFactoryPostProcessor,DisposableBean,BeanPostProcessor,ApplicationContextAware{ privatefinalStringCOMMA_SPLIT_PATTERN=","; privateApplicationContextapplicationContext; privateStringannotationPackage; privateString[]annotationPackages; privateBeanRegistrybeanRegistry; publicvoidsetAnnotationPackage(StringannotationPackage){ this.annotationPackage=annotationPackage; if(!StringUtils.isEmpty(this.annotationPackage)) this.annotationPackages=this.annotationPackage.split(this.COMMA_SPLIT_PATTERN); } @Override publicvoidsetApplicationContext(ApplicationContextapplicationContext) throwsBeansException{ this.applicationContext=applicationContext; this.beanRegistry=newBeanRegistry(this.applicationContext); } @Override publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName) throwsBeansException{ if(!this.isMatchPackage(bean))returnbean; Endpointendpoint=bean.getClass().getAnnotation(Endpoint.class); if(endpoint!=null){ System.out.println(bean.getClass()); } returnbean; } @Override publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName) throwsBeansException{ returnbean; } @Override publicvoiddestroy()throwsException{ } /** *包是否匹配 *@parambean *@return */ privatebooleanisMatchPackage(Objectbean){ if(annotationPackages==null||annotationPackages.length==0){ returntrue; } StringbeanClassName=bean.getClass().getName(); for(Stringpkg:annotationPackages){ if(beanClassName.startsWith(pkg)){ returntrue; } } returnfalse; } /** *扫描{@linkcom.codestd.spring.cxf.annotation.Endpoint}注解 */ @Override publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{ if(annotationPackage==null||annotationPackage.length()==0){ return; } if(beanFactoryinstanceofBeanDefinitionRegistry){ BeanDefinitionRegistrybeanDefinitionRegistry=(BeanDefinitionRegistry)beanFactory; ClassPathBeanDefinitionScannerscanner=newClassPathBeanDefinitionScanner(beanDefinitionRegistry,true); AnnotationTypeFilterfilter=newAnnotationTypeFilter(Endpoint.class); scanner.addIncludeFilter(filter); scanner.scan(annotationPackages); } } }
这里已经实现了注解的扫描。然后需要在postProcessAfterInitialization方法中写业务处理代码。AfterInitialization表示Bean已经创建并且注入属性。
postProcessBeforeInitialization主要是为了在Bean实例化时注入属性。
让Spring识别扩展
首先在classpath的META-INF下创建spring.handlers,内容如下
http\://www.codestd.com/schema/std/ws=com.codestd.spring.cxf.config.schema.WebServiceAnnotationNamespaceHandler
在这个文件中指明了哪个命名空间需要哪个类来处理。
然后再创建spring.schemas
http\://www.codestd.com/schema/std/ws/stdws-1.0.xsd=META-INF/schema/stdws-1.0.xsd
指明了Sechma文件的位置,Spring会使用这里制定的xsd文件来验证配置是否正确。
测试
创建接口
packagecom.codestd.spring.cxf.ws; importjavax.jws.WebService; /** *@authorjaune(WangChengwei) *@since1.0.0 */ @WebService publicinterfaceHelloService{ publicStringsyHi(Stringname); }
实现类
packagecom.codestd.spring.cxf.ws; importjavax.jws.WebService; importcom.codestd.spring.cxf.annotation.Endpoint; /** *@authorjaune(WangChengwei) *@since1.0.0 */ @Endpoint(address="HelloService",id="HelloServiceEndpoint") @WebService(endpointInterface="com.codestd.spring.cxf.ws.HelloService") publicclassHelloServiceImplimplementsHelloService{ @Override publicStringsyHi(Stringname){ return"Hello"+name; } }
测试用例
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:applicationContext.xml"}) publicclassInitializationTest{ @Test publicvoidtest(){ } }
在处理类中有一段代码是将有@Endpoint注解的类都打印出来,所以如果类名被打印出来就表示配置正常了。
运行测试用例
控制台能够看到
classcom.codestd.spring.cxf.ws.HelloServiceImpl
通过以上内容的介绍本次扩展基本上就实现了。