Voici comment j'ai implémenté toutes les fonctionnalités qui créent des beans d'interfaces annotées 'WebService' et qui supporte Autowiring dans l'implémentation proxy. (déclaration de paquet et les instructions d'importation sont omis dans le code ci-dessous) Tout d'abord, j'ai créé WebService
et WebServiceOperation
annotation.
WebService Annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebService {
String service();
String namespace();
}
WebService Opération Annotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebServiceOperation {
String operation();
}
L'étape suivante consiste à analyser toutes les interfaces WebService
annotés de paquets spécifiés. Spring fournit ClassPathScanningCandidateComponentProvider
pour l'analyse des paquets mais ne détecte pas les interfaces. S'il vous plaît voir this question et it's answer pour plus de détails. J'ai donc étendu ClassPathScanningCandidateComponentProvider
et surpasser la méthode isCandidateComponent
.
ClassPathScanner
public class ClassPathScanner extends ClassPathScanningCandidateComponentProvider {
public ClassPathScanner(final boolean useDefaultFilters) {
super(useDefaultFilters);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isIndependent();
}
}
A ce stade, j'ai créé l'annotation EnableWebServices
pour activer les services Web et de fournir des packages de services Web qui contiennent WebService
interfaces annotés.
EnableWebServices Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({
WebServiceProxyConfig.class,
WebServiceProxyBeansRegistrar.class
})
public @interface EnableWebServices {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}
Cette annotation peut être appliquée à une Configuration
classe annotée avec des paquets pour analyser les interfaces, comme ci-dessous.
@EnableWebServices({
"a.b.c.webservices",
"x.y.z.webservices"
})
Il est temps de penser à la création de proxy dynamique qui invoquera service Web réel à partir des informations données dans WebService
et WebServiceOperation
annotations. Java fournit un mécanisme pour créer un proxy dynamique qui nécessite de fournir l'implémentation de l'interface InvocationHandler
et de fournir la logique dans sa méthode invoke
. Supposons qu'un bean de type "TheWebServiceCaller" contienne toute la méchante logique pour appeler un service Web. Je viens de l'injecter et d'appeler sa méthode call
avec un TheWebServiceInfo
(extrait des annotations WebService
et WebServiceOperation
) et de demander l'objet.
TheWebServiceInfo (Supposons que tous les champs ont accesseurs)
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;
}
}
implementaion de InvocationHandler
est passé à Proxy.newProxyInstance
(ainsi que d'autres informations) pour créer proxy objets. J'ai besoin d'objets proxy séparés pour chaque interface annotée WebService
. Je vais maintenant créer une fabrique pour créer des instances de proxy et le nom est 'WebServiceProxyBeanFactory'. Les instances créées par cette fabrique deviennent des beans correspondant aux interfaces annotées WebService
.
Un peu plus tard, j'exposerai «WebServiceProxy» et WebServiceProxyBeanFactory
comme des beans. Dans 'WebServiceProxyBeanFactory', je vais injecter WebServiceProxy
et l'utiliser. Veuillez noter que createWebServiceProxyBean
utilise des génériques. C'est important.
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);
}
}
Si vous vous souvenez, plus tôt, je WebServiceProxyConfig
ont importé dans EnableWebServices
annotations. est utilisé pour exposer WebServiceProxy
et WebServiceProxyBeanFactory
comme des haricots.
WebServiceProxyConfig
@Configuration
public class WebServiceProxyConfig {
@Bean
public WebServiceProxy webServiceProxy() {
return new WebServiceProxy();
}
@Bean(name = "webServiceProxyBeanFactory")
public WebServiceProxyBeanFactory webServiceProxyBeanFactory() {
return new WebServiceProxyBeanFactory();
}
}
Maintenant, tout est en place. Il est temps d'écrire un crochet pour commencer à analyser les paquets de services Web et enregistrer les proxies dynamiques en tant que beans. Je vais fournir la mise en œuvre de 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();
}
}
}
Dans cette classe, j'extrait tous les paquets fournis dans l'annotation EnableWebServices
. Pour chaque paquet extrait, j'ai utilisé ClassPathScanner
pour numériser. (Ici, la logique peut être affinée pour filtrer uniquement les interfaces annotées WebService
). Pour chaque interface détectée, j'ai enregistré une définition de bean. S'il vous plaît noter que j'ai utilisé webServiceProxyBeanFactory
et a appelé son createWebServiceProxyBean
avec classLoader et le type d'interface. Cette méthode d'usine, lorsqu'elle est invoquée au printemps plus tard, retournera un bean du même type que celui de l'interface, de sorte que le bean avec le type correct est enregistré. Ce bean peut être injecté n'importe où avec le type d'interface. De plus, WebServiceProxy
peut injecter et utiliser n'importe quel autre haricot. Donc autowiring fonctionnera aussi comme prévu.
J'ai exposé 'WebServiceProxy' (implémente 'InvocationHandler') comme bean et l'ai essayé dans' BeanFactoryPostProcessor.postProcessBeanFactory() '. Ici 'beanFactory.getBean (WebServiceProxy.class)' renvoie l'instance de 'WebServiceProxy' mais ses champs annotés 'Autowired' sont null. J'ai besoin d'obtenir l'instance 'WebServiceProxy' ici pour la passer à 'Proxy.newProxyInstance()'. –
Bien sûr, il n'y a pas de champs autowired pendant que BFPP traite beanFactory. Les champs sont autowired au stade de BeanPostProcessor.postProcessBeforeInitialization et il est déclenché après BFPP. –
Vous n'avez pas besoin de créer de proxy à l'étape de BFPP. Si vous pensez vraiment que vous voulez créer des proxies autour de certains beans à l'étape d'initialisation, vous devez créer votre BeanPostProcessor, pas BeanFactoryPostProcessor, et créer des proxies dans postProcessAfterInitialization –