这是我如何实现创建“WebService”注释接口的所有功能的方法,并且还支持代理实现内部的自动装配。 (以下代码中省略了包声明和导入语句)
首先我创建了WebService 和WebServiceOperation 注释。
WebService 注释
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebService {
String service();
String namespace();
}
WebService操作注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebServiceOperation {
String operation();
}
下一步是从指定的包中扫描所有WebService 注释的接口。 Spring 提供ClassPathScanningCandidateComponentProvider 用于包扫描,但它不检测接口。请参阅this question 和it's answer 了解更多详情。所以我扩展了ClassPathScanningCandidateComponentProvider 并覆盖了isCandidateComponent 方法。
类路径扫描器
public class ClassPathScanner extends ClassPathScanningCandidateComponentProvider {
public ClassPathScanner(final boolean useDefaultFilters) {
super(useDefaultFilters);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isIndependent();
}
}
此时我创建了EnableWebServices注解来启用Web服务并提供包含WebService注解接口的Web服务包。
启用WebServices注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({
WebServiceProxyConfig.class,
WebServiceProxyBeansRegistrar.class
})
public @interface EnableWebServices {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}
这个注解可以应用到一些Configuration注解的类,带有包来扫描接口,如下所示。
@EnableWebServices({
"a.b.c.webservices",
"x.y.z.webservices"
})
是时候考虑创建动态代理了,它将根据WebService 和WebServiceOperation 注释中给出的信息调用实际的Web 服务。 Java 提供了一种创建动态代理的机制,需要提供InvocationHandler 接口的实现,并在其invoke 方法中提供逻辑。我将此实现命名为WebServiceProxy
假设“TheWebServiceCaller”类型的 bean 包含调用 Web 服务的所有讨厌的逻辑。我只是注入它并使用TheWebServiceInfo(从WebService 和WebServiceOperation 注释中提取)调用它的call 方法并请求对象。
TheWebServiceInfo(假设所有字段都有getter和setter)
public class TheWebServiceInfo {
private String service;
private String namespace;
private String operation;
}
WebServiceProxy
public class WebServiceProxy implements InvocationHandler {
@Autowired
private TheWebServiceCaller caller;
@Override
public Object invoke(Object target, Method method, Object[] args) throws Exception {
Object request = (null != args && args.length > 0) ? args[0] : null;
WebService webService = method.getDeclaringClass().getAnnotation(WebService.class);
WebServiceOperation webServiceOperation = method.getAnnotation(WebServiceOperation.class);
TheWebServiceInfo theInfo = createTheWebServiceInfo(webService, webServiceOperation);
return caller.call(theInfo, request);
}
private TheWebServiceInfo createTheWebServiceInfo(WebService webService, WebServiceOperation webServiceOperation) {
TheWebServiceInfo theInfo = new TheWebServiceInfo();
theInfo.setService(webService.service());
theInfo.setNamespace(webService.namespace());
theInfo.setOperation(webServiceOperation.operation());
return theInfo;
}
}
InvocationHandler 的实现被传递给Proxy.newProxyInstance(连同其他一些信息)以创建代理对象。我需要为每个 WebService 带注释的接口提供单独的代理对象。我现在将创建一个工厂来代理实例创建,名称为“WebServiceProxyBeanFactory”。这个工厂创建的实例将成为对应WebService注解接口的bean。
稍后,我会将“WebServiceProxy”和WebServiceProxyBeanFactory 公开为bean。在“WebServiceProxyBeanFactory”中,我将注入WebServiceProxy 并使用它。请注意createWebServiceProxyBean 使用泛型。这很重要。
WebServiceProxyBeanFactory
public class WebServiceProxyBeanFactory {
@Autowired
WebServiceProxy webServiceProxy;
@SuppressWarnings("unchecked")
public <WS> WS createWebServiceProxyBean(ClassLoader classLoader, Class<WS> clazz) {
return (WS) Proxy.newProxyInstance(classLoader, new Class[] {clazz}, webServiceProxy);
}
}
如果你还记得,我之前在EnableWebServices 注释中导入了WebServiceProxyConfig。 WebServiceProxyConfig 用于将WebServiceProxy 和WebServiceProxyBeanFactory 暴露为bean。
WebServiceProxyConfig
@Configuration
public class WebServiceProxyConfig {
@Bean
public WebServiceProxy webServiceProxy() {
return new WebServiceProxy();
}
@Bean(name = "webServiceProxyBeanFactory")
public WebServiceProxyBeanFactory webServiceProxyBeanFactory() {
return new WebServiceProxyBeanFactory();
}
}
现在一切就绪。是时候编写一个钩子来开始扫描 Web 服务包并将动态代理注册为 bean。我将提供ImportBeanDefinitionRegistrar 的实现。
WebServiceProxyBeansRegistrar
@Configuration
public class WebServiceProxyBeansRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {
private ClassPathScanner classpathScanner;
private ClassLoader classLoader;
public WebServiceProxyBeansRegistrar() {
classpathScanner = new ClassPathScanner(false);
classpathScanner.addIncludeFilter(new AnnotationTypeFilter(WebService.class));
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
String[] basePackages = getBasePackages(importingClassMetadata);
if (ArrayUtils.isNotEmpty(basePackages)) {
for (String basePackage : basePackages) {
createWebServicProxies(basePackage, registry);
}
}
}
private String[] getBasePackages(AnnotationMetadata importingClassMetadata) {
String[] basePackages = null;
MultiValueMap<String, Object> allAnnotationAttributes =
importingClassMetadata.getAllAnnotationAttributes(EnableWebServices.class.getName());
if (MapUtils.isNotEmpty(allAnnotationAttributes)) {
basePackages = (String[]) allAnnotationAttributes.getFirst("basePackages");
}
return basePackages;
}
private void createWebServicProxies(String basePackage, BeanDefinitionRegistry registry) {
try {
for (BeanDefinition beanDefinition : classpathScanner.findCandidateComponents(basePackage)) {
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
WebService webService = clazz.getAnnotation(WebService.class);
String beanName = StringUtils.isNotEmpty(webService.bean())
? webService.bean() : ClassUtils.getShortNameAsProperty(clazz);
GenericBeanDefinition proxyBeanDefinition = new GenericBeanDefinition();
proxyBeanDefinition.setBeanClass(clazz);
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue(classLoader);
args.addGenericArgumentValue(clazz);
proxyBeanDefinition.setConstructorArgumentValues(args);
proxyBeanDefinition.setFactoryBeanName("webServiceProxyBeanFactory");
proxyBeanDefinition.setFactoryMethodName("createWebServiceProxyBean");
registry.registerBeanDefinition(beanName, proxyBeanDefinition);
}
} catch (Exception e) {
System.out.println("Exception while createing proxy");
e.printStackTrace();
}
}
}
在这个类中,我提取了EnableWebServices注解中提供的所有包。对于每个提取的包,我使用ClassPathScanner 进行扫描。 (这里的逻辑可以被细化为只过滤WebService注解的接口)。对于每个检测到的接口,我都注册了一个 bean 定义。请注意,我使用了webServiceProxyBeanFactory,并使用类加载器和接口类型调用了它的createWebServiceProxyBean。这个工厂方法,后面被spring调用的时候,会返回和接口相同类型的bean,所以注册了正确类型的bean。这个 bean 可以在任何接口类型的地方注入。此外,WebServiceProxy 可以注入和使用任何其他 bean。所以自动装配也将按预期工作。