だ作業Autowiredを取得するだけではなく、単純なオブジェクトを、それを作成する必要があります。 (パッケージ宣言およびインポート文は以下のコードでは省略されている)すべての まず私はWebService
とWebServiceOperation
注釈を作成しました。
Webサービス注釈
@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
メソッドをオーバーロードしました。この時点で
ClassPathScanner
public class ClassPathScanner extends ClassPathScanningCandidateComponentProvider {
public ClassPathScanner(final boolean useDefaultFilters) {
super(useDefaultFilters);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isIndependent();
}
}
私は、Webサービスを有効にしてWebService
注釈付きインタフェースが含まれているWebサービスのパッケージを提供するために、EnableWebServices
注釈を作成しました。
EnableWebServices注釈
@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(すべてのフィールドは、ゲッターとセッターを持っていると仮定)
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
のImplementaionがプロキシを作成する(いくつかの他の情報とともに)Proxy.newProxyInstance
に渡されます。オブジェクト。私は、それぞれのWebService
注釈付きインターフェイスのための分離プロキシオブジェクトが必要です。プロキシインスタンス作成のためのファクトリを作成し、名前は 'WebServiceProxyBeanFactory'です。このファクトリによって作成されたインスタンスは、対応するWebService
注釈付きインターフェイスのBeanになります。
少し後で、私はBeanとして 'WebServiceProxy'とWebServiceProxyBeanFactory
を公開します。 '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
はclassLoaderとインタフェースのタイプと呼ばれています。このファクトリメソッドは、後で春に呼び出されると、インタフェースと同じ型のBeanを返すので、正しい型のBeanが登録されます。このBeanは、インタフェース型であればどこにでも挿入できます。さらに、WebServiceProxy
は、他のBeanを注入して使用することができます。 autowiringも期待どおりに動作します。
私はBeanFactoryPostProcessor.postProcessBeanFactory()で 'WebServiceProxy'( 'InvocationHandler'を実装)をBeanとして公開しました。ここで 'beanFactory.getBean(WebServiceProxy.class)'は 'WebServiceProxy'のインスタンスを返しますが、 'Autowired'の注釈付きフィールドはnullです。私は 'WebServiceProxy'インスタンスをここで 'Proxy.newProxyInstance()'に渡す必要があります。 –
もちろん、BFPPがbeanFactoryを処理している間は自動フィールドはありません。フィールドはBeanPostProcessor.postProcessBeforeInitializationの段階でautowiredされ、BFPPの後に起動されます。 –
BFPPの段階でプロキシを作成する必要はありません。初期化段階でいくつかのBeanの周りにプロキシを作成したいと思うなら、BeanFactoryPostProcessorではなくBeanPostProcessorを作成し、postProcessAfterInitializationでプロキシを作成する必要があります –