簡體   English   中英

Spring 3中的Custom Autowire候選bean

[英]Custom Autowire candidate beans in Spring 3

假設我有一個服務接口ServiceInterface以及實現它的幾個組件的以下結構: ProductAServiceProductBService我還有一個RequestContext bean,它具有一個限定屬性,表示我們當前正在處理ProductA或ProductB。 然后,如何將自動裝配或其他注釋自動注入正確的實現(ProductAService或ProductBService)到需要它的某個服務(下面的ServiceThatNeedsServiceInterface )。

public interface ServiceInterface {
  void someMethod();
}

@Component(name="ProductAService")
public class ProductAService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, A Service"); 
  }
}

@Component(name="ProductBService")
public class ProductBService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, B Service"); 
  }
}

@Component
public class ServiceThatNeedsServiceInterface {

  // What to do here???
  @Autowired
  ServiceInterface service;

  public void useService() {
    service.someMethod();
  }
}

@Component
@Scope( value = WebApplicationContext.SCOPE_REQUEST )
public class RequestContext {
  String getSomeQualifierProperty();
}

Spring Source在版本1.1.4中創建ServiceLocatorFactoryBean時引用了您的問題。 要使用它,您需要添加類似下面的界面:

public interface ServiceLocator {
    //ServiceInterface service name is the one 
      //set by @Component
    public ServiceInterface lookup(String serviceName);
}

您需要將以下代碼段添加到applicationContext.xml中

<bean id="serviceLocatorFactoryBean"
    class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
    <property name="serviceLocatorInterface"
              value="org.haim.springframwork.stackoverflow.ServiceLocator" />
</bean>

現在您的ServiceThatNeedsServiceInterface看起來類似於下面的那個:

@Component
public class ServiceThatNeedsServiceInterface {
    // What to do here???
    //  @Autowired
    //  ServiceInterface service;

    /*
     * ServiceLocator lookup returns the desired implementation
     * (ProductAService or ProductBService) 
     */ 
 @Autowired
     private ServiceLocator serviceLocatorFactoryBean;

     //Let’s assume we got this from the web request 
     public RequestContext context;

     public void useService() {
        ServiceInterface service =  
        serviceLocatorFactoryBean.lookup(context.getQualifier());
        service.someMethod();         
      }
}

ServiceLocatorFactoryBean將根據RequestContext限定符返回所需的服務。 除了spring注釋,您的代碼不依賴於Spring。 我為上面的內容執行了以下單元測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/applicationContext.xml" })
public class ServiceThatNeedsServiceInterfaceTest {

    @Autowired
    ServiceThatNeedsServiceInterface serviceThatNeedsServiceInterface;

    @Test
    public void testUseService() {
    //As we are not running from a web container
    //so we set the context directly to the service 
        RequestContext context = new RequestContext();
        context.setQualifier("ProductAService");
        serviceThatNeedsServiceInterface.context = context;
        serviceThatNeedsServiceInterface.useService();

        context.setQualifier("ProductBService");
        serviceThatNeedsServiceInterface.context = context;
        serviceThatNeedsServiceInterface.useService();
    }

}

控制台將顯示
你好,一個服務
您好,B服務

一句警告。 API文檔說明了這一點
“這樣的服務定位器......通常用於原型bean,即用於為每次調用返回一個新實例的工廠方法......對於單例bean,最好使用直接setter或構造函數注入目標bean。”

我不明白為什么這可能會導致問題。 在我的代碼中,它在對serviceThatNeedsServiceInterface.useService()的兩個序列調用中返回相同的服務;

您可以在GitHub中找到我的示例的源代碼

我能想到的唯一方法就是創建像FactoryBean這樣的東西來返回基於RequestContext屬性的相應實現。 這是我一起拍的具有你想要的行為的東西:

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.http.HttpServletRequest;

public class InjectionQualifiedByProperty {

    @Controller
    @Scope(WebApplicationContext.SCOPE_REQUEST)
    public static class DynamicallyInjectedController {
        @Autowired
        @Qualifier("picker")
        Dependency dependency;

        @RequestMapping(value = "/sayHi", method = RequestMethod.GET)
        @ResponseBody
        public String sayHi() {
            return dependency.sayHi();
        }
    }

    public interface Dependency {
        String sayHi();
    }

    @Configuration
    public static class Beans {
        @Bean
        @Scope(WebApplicationContext.SCOPE_REQUEST)
        @Qualifier("picker")
        FactoryBean<Dependency> dependencyPicker(final RequestContext requestContext,
                                                 final BobDependency bob, final FredDependency fred) {
            return new FactoryBean<Dependency>() {
                @Override
                public Dependency getObject() throws Exception {
                    if ("bob".equals(requestContext.getQualifierProperty())) {
                        return bob;
                    } else {
                        return fred;
                    }
                }

                @Override
                public Class<?> getObjectType() {
                    return Dependency.class;
                }

                @Override
                public boolean isSingleton() {
                    return false;
                }
            };
        }
    }

    @Component
    public static class BobDependency implements Dependency {
        @Override
        public String sayHi() {
            return "Hi, I'm Bob";
        }
    }

    @Component
    public static class FredDependency implements Dependency {
        @Override
        public String sayHi() {
            return "I'm not Bob";
        }
    }

    @Component
    @Scope(WebApplicationContext.SCOPE_REQUEST)
    public static class RequestContext {
        @Autowired HttpServletRequest request;

        String getQualifierProperty() {
            return request.getParameter("which");
        }
    }
}

在Github上使用了這個代碼。 您可以使用以下命令克隆並運行它:

git clone git://github.com/zzantozz/testbed tmp
cd tmp/spring-mvc
mvn jetty:run

然后訪問http://localhost:8080/dynamicallyInjected以查看一個依賴項的結果, http://localhost:8080/dynamicallyInjected?which=bob查看另一個。

我想,你錯過了注釋,告訴春天,你有一個自定義服務。 所以你的解決方案是在類名之前添加這個注釋:

@Service("ProductAService")
public class ProductAService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, A Service"); 
  }
}

@Service("ProductBService")
public class ProductBService implements ServiceInterface {
  @Override public void someMethod() { 
    System.out.println("Hello, B Service"); 
  }
}

然后你可以自動連接它,但是為了使用特定的服務,你必須添加注釋Qualifier(),如下所示:

  @Autowired
  @Qualifier("ProductBService") // or ProductAService
  ServiceInterface service;

或者你可能只需要添加一個注釋限定符(“你的bean的名字”):)

這可能對您有所幫助:

使用

AutowireCapeableBeanFactory.autowireBean(Object existingBean)  

要么

AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)

AutowireCapeableBeanFactory.autowireBean(Object existingBean) AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean,int autowireMode,boolean dependencyCheck)

我不認為你可以用注釋做到這一點,原因是你需要一個在運行時動態的bean(可能是服務或B服務),所以@Autowire將在任何地方使用bean之前連接。 一種解決方案是在需要時從上下文中獲取bean。

    @Component
public class ServiceThatNeedsServiceInterface {


  ServiceInterface service;

  public void useService() {
     if(something is something){
        service = applicationContext.getBean("Abean", ServiceInterface.class);
     }else{
        service = applicationContext.getBean("Bbean", ServiceInterface.class);
     }
    service.someMethod();
  }
}

你可以在類中的某個地方放置else邏輯作為一個單獨的函數:

public void useService() {
        service = findService();
        service.someMethod();
      }

public ServiceInterface findService() {
         if(something is something){
            return applicationContext.getBean("Abean", ServiceInterface.class);
         }else{
            return applicationContext.getBean("Bbean", ServiceInterface.class);
         }

      }

這是動態的,這可能是你想要的。

您可以將@Qualifier注釋與別名結合使用。 請參閱此處基於屬性加載bean的示例。 您可以修改此方法並更改requestcontext中的屬性/別名...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM