2016-09-15 12 views
3

私が取り組んでいる春のプロジェクトでは、Webサービスを呼び出すための機能レイヤーがあります。各ウェブサービスオペレーションについて、メソッドは、コードがほぼ同じであるが、いくつかの異なるオペレーション固有の情報(例えば、サービス名、オペレーション名、名前空間など)を用いて作成される。自動配線機能を持つ動的プロキシBean

私は、このレイヤーをインターフェースと注釈付きのメソッドに置き換えています。例えば、以下のコードは、Webサービス( "foo")の操作 "fetchBar"に対して提供されています。

package a.b.c.webservices; 

@WebService(service="foo", namespace="...") 
public interface FooWebService { 

    @WebServiceOperation(operation="fetchBar") 
    BarRespons fetchBar(BarRequest request) throws WebServiceException; 
} 

は今、私はいくつかのメカニズムで、春には、私はいくつかの指定されたパッケージ(複数可)から動的プロキシBeanを作成することができ、私は、Webサービスを呼び出すために、次のコードを使用することができ、欲しいです。私はそれをInvocationHandlerの実装を提供することにより、java.lang.reflect.Proxy.newProxyInstanceを使用して動的豆のインスタンスを作成して、これを達成するために

package a.b.c.business; 

import a.b.c.webservices.FooWebService; 

public class FooBusiness { 

    @Autowired 
    FooWebService fooWebService; 


    public Bar getBar() { 

     Bar bar = null;    

     BarRequest request; 

     //create request 
     BarResponse response = fooWebService.fetchBar(request); 
     //extrac bar from response 

     return bar; 
    } 
} 

。しかし、オートワイヤリングは、invocationHandlerの提供された実装とそれ以上の依存関係では機能しません。

私はこれを達成するために次の方法を試しました。

  • BeanFactoryPostProcessor.postProcessBeanFactoryConfigurableListableBeanFactory.registerSingletonメソッドを使用して登録豆を実装しました。
  • ImportBeanDefinitionRegistrar.registerBeanDefinitionsを実装し、BeanDefinitionRegistry.registerBeanDefinitionを使用しようとしましたが、私はAutowiringをサポートする正しいBean定義を提供する方法を混乱させています。

何が欠けているか教えてください。私が正しい方向に行かないなら、私を案内してください。

答えて

4

だ作業Autowiredを取得するだけではなく、単純なオブジェクトを、それを作成する必要があります。 (パッケージ宣言およびインポート文は以下のコードでは省略されている)すべての まず私はWebServiceWebServiceOperation注釈を作成しました。

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" 
}) 

それはWebServiceWebServiceOperation注釈に与えられた情報から、実際のWebサービスを呼び出す動的プロキシの作成について考える時間です。 Javaは、InvocationHandlerインターフェイスの実装を提供し、そのロジックをinvokeメソッドで提供する必要がある動的プロキシを作成するためのメカニズムを提供します。私はこの実装を次のように指定しました。WebServiceProxy

タイプ 'TheWebServiceCaller'のBeanには、Webサービスを呼び出すためのすべての不愉快なロジックが含まれているとします。私はちょうどそれを注入し、TheWebServiceInfoWebServiceWebServiceOperationの注釈から抽出された)の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は、WebServiceProxyWebServiceProxyBeanFactoryを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も期待どおりに動作します。

1

InvocationHandlerはBeanですか?あなたは、Beanとして私は「Webサービス」注釈付きインタフェースの豆を作成しても、プロキシ実装の内部でオートワイヤリングをサポートしているすべての機能を実装する方法をここで

+0

私はBeanFactoryPostProcessor.postProcessBeanFactory()で 'WebServiceProxy'( 'InvocationHandler'を実装)をBeanとして公開しました。ここで 'beanFactory.getBean(WebServiceProxy.class)'は 'WebServiceProxy'のインスタンスを返しますが、 'Autowired'の注釈付きフィールドはnullです。私は 'WebServiceProxy'インスタンスをここで 'Proxy.newProxyInstance()'に渡す必要があります。 –

+1

もちろん、BFPPがbeanFactoryを処理している間は自動フィールドはありません。フィールドはBeanPostProcessor.postProcessBeforeInitializationの段階でautowiredされ、BFPPの後に起動されます。 –

+0

BFPPの段階でプロキシを作成する必要はありません。初期化段階でいくつかのBeanの周りにプロキシを作成したいと思うなら、BeanFactoryPostProcessorではなくBeanPostProcessorを作成し、postProcessAfterInitializationでプロキシを作成する必要があります –

関連する問題