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
通过以上内容的介绍本次扩展基本上就实现了。