命名空间支持
要实现命名空间支持,需要继承自NamespaceHandlerSupport。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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注册工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
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文件来验证配置是否正确。
测试
创建接口
1
2
3
4
5
6
7
8
9
10
|
package com.codestd.spring.cxf.ws; import javax.jws.WebService; /** * @author jaune(Wang Chengwei) * @since 1.0.0 */ @WebService public interface HelloService { public String syHi(String name); } |
实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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; } } |
测试用例
1
2
3
4
5
6
7
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations={ "classpath:applicationContext.xml" }) public class InitializationTest { @Test public void test(){ } } |
在处理类中有一段代码是将有@Endpoint注解的类都打印出来,所以如果类名被打印出来就表示配置正常了。
运行测试用例
控制台能够看到
class com.codestd.spring.cxf.ws.HelloServiceImpl
通过以上内容的介绍本次扩展基本上就实现了。