基于Spring可扩展Schema自定义配置(2)

来源:互联网 发布:王保保 知乎 编辑:程序博客网 时间:2024/06/08 13:58

本章主要实现配置支持,注解扫描等功能。为本次教程的核心

命名空间支持

要实现命名空间支持,需要继承自NamespaceHandlerSupport

package com.codestd.spring.cxf.config.schema;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;import com.codestd.spring.cxf.config.EndpointBeanProcessor;/** * 处理命名空间 * @author jaune(Wang Chengwei) * @since 1.0.0 */public class WebServiceAnnotationNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        // TODO Auto-generated method stub        this.registerBeanDefinitionParser("annotation-endpoint", new AnnotationBeanDefinitionParser(EndpointBeanProcessor.class));    }}

通过registerBeanDefinitionParser方法讲配置支持添加到Spring中。annotation-endpoint是配置支持的元素。AnnotationBeanDefinitionParser是处理配置的类。EndpointBeanProcessor是处理@Endpoint注解的Bean的类,后面会有详细的讲述。

处理配置

需要实现BeanDefinitionParser

package com.codestd.spring.cxf.config.schema;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.support.RootBeanDefinition;import org.springframework.beans.factory.xml.BeanDefinitionParser;import org.springframework.beans.factory.xml.ParserContext;import org.springframework.util.StringUtils;import org.w3c.dom.Element;/** * @author jaune(Wang Chengwei) * @since 1.0.0 */public class AnnotationBeanDefinitionParser implements BeanDefinitionParser {    private final Class<?> beanClass;    public AnnotationBeanDefinitionParser(Class<?> beanClass) {        this.beanClass = beanClass;    }    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        RootBeanDefinition beanDefinition = new RootBeanDefinition();        beanDefinition.setBeanClass(beanClass);        beanDefinition.setLazyInit(false);        String id = element.getAttribute("id");        if(id == null || id.length() == 0 ){            String name = element.getAttribute("name");            if(!StringUtils.isEmpty(name)) id = name;            else id = beanClass.getName();        }        if (parserContext.getRegistry().containsBeanDefinition(id))  {            throw new IllegalStateException("Duplicate spring bean id " + id);        }        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);        String annotationPackage = element.getAttribute("package");        if(!StringUtils.isEmpty(annotationPackage))            beanDefinition.getPropertyValues().add("annotationPackage", annotationPackage);        return beanDefinition;    }}

BeanDefinitionParser的应用参见Spring官方文档。

Bean注册工具类

package com.codestd.spring.cxf.config;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.ConfigurableApplicationContext;/** * Registry Bean. Must inject the spring ApplicationContext. * @author jaune(Wang Chengwei) * @since 1.0.0 */public class BeanRegistry implements ApplicationContextAware{    private ApplicationContext applicationContext;    private ConfigurableApplicationContext configurableApplicationContext;    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;        if(applicationContext instanceof ConfigurableApplicationContext){            this.configurableApplicationContext = (ConfigurableApplicationContext)this.applicationContext;        }    }    public BeanRegistry(){    }    public BeanRegistry(ApplicationContext applicationContext){        this.setApplicationContext(applicationContext);    }    public BeanDefinition register(Class<?> clazz){        if(configurableApplicationContext == null)return null;        BeanDefinitionRegistry beanDefinitonRegistry =                 (BeanDefinitionRegistry)configurableApplicationContext.getBeanFactory();        BeanDefinitionBuilder beanDefinitionBuilder = this.createBuilder(clazz);        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();        beanDefinitonRegistry.registerBeanDefinition(clazz.getName(),beanDefinition);        return beanDefinition;    }    private BeanDefinitionBuilder createBuilder(Class<?> clazz){        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);        return beanDefinitionBuilder;    }}

处理@Endpoint

package com.codestd.spring.cxf.config;import org.springframework.beans.BeansException;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;import org.springframework.core.type.filter.AnnotationTypeFilter;import org.springframework.util.StringUtils;import com.codestd.spring.cxf.annotation.Endpoint;/** * @author jaune(WangChengwei) * @since 1.0.0 */public class EndpointBeanProcessor implements     BeanFactoryPostProcessor, DisposableBean, BeanPostProcessor, ApplicationContextAware{    private final String COMMA_SPLIT_PATTERN = ",";    private ApplicationContext applicationContext;    private String annotationPackage;    private String[] annotationPackages;    private BeanRegistry beanRegistry;    public void setAnnotationPackage(String annotationPackage) {        this.annotationPackage = annotationPackage;        if(!StringUtils.isEmpty(this.annotationPackage))            this.annotationPackages = this.annotationPackage.split(this.COMMA_SPLIT_PATTERN);    }    @Override    public void setApplicationContext(ApplicationContext applicationContext)            throws BeansException {        this.applicationContext = applicationContext;        this.beanRegistry = new BeanRegistry(this.applicationContext);    }    @Override    public Object postProcessAfterInitialization(Object bean, String beanName)            throws BeansException {        if(!this.isMatchPackage(bean))return bean;        Endpoint endpoint = bean.getClass().getAnnotation(Endpoint.class);        if(endpoint != null){            System.out.println(bean.getClass());        }        return bean;    }    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName)            throws BeansException {        return bean;    }    @Override    public void destroy() throws Exception {    }    /**     * 包是否匹配     * @param bean     * @return     */    private boolean isMatchPackage(Object bean){        if (annotationPackages == null || annotationPackages.length == 0) {            return true;        }        String beanClassName = bean.getClass().getName();        for (String pkg : annotationPackages) {            if (beanClassName.startsWith(pkg)) {                return true;            }        }        return false;    }    /**     * 扫描{@link com.codestd.spring.cxf.annotation.Endpoint}注解     */    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        if (annotationPackage == null || annotationPackage.length() == 0) {            return;        }        if (beanFactory instanceof BeanDefinitionRegistry) {            BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry)beanFactory;            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry,true);            AnnotationTypeFilter filter = new AnnotationTypeFilter(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文件来验证配置是否正确。

测试

创建接口

package com.codestd.spring.cxf.ws;import javax.jws.WebService;/** * @author jaune(Wang Chengwei) * @since 1.0.0 */@WebServicepublic interface HelloService {    public String syHi(String name);}

实现类

package com.codestd.spring.cxf.ws;import javax.jws.WebService;import com.codestd.spring.cxf.annotation.Endpoint;/** * @author jaune(Wang Chengwei) * @since 1.0.0 */@Endpoint(address="HelloService", id = "HelloServiceEndpoint")@WebService(endpointInterface="com.codestd.spring.cxf.ws.HelloService")public class HelloServiceImpl implements HelloService{    @Override    public String syHi(String name) {        return "Hello "+name;    }}

测试用例

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations={"classpath:applicationContext.xml"})public class InitializationTest {    @Test    public void test(){    }}

在处理类中有一段代码是将有@Endpoint注解的类都打印出来,所以如果类名被打印出来就表示配置正常了。
运行测试用例
控制台能够看到

class com.codestd.spring.cxf.ws.HelloServiceImpl

本次扩展基本实现。

本此教程的内容可以在Spring官方文档第42章中找到。

Note
本次并没有实现自动发布WebService的功能,这块儿功能比较复杂暂时还没有实现,如果实现会在后面的博文中补充。

1 0
原创粉丝点击