在Spring中隐式合格的自动装配

时间:2016-08-22 08:55:28

标签: java spring

我有一个带有一些独立bean X的项目,它在一堆服务中自动装配。服务彼此使用,最后用于单入口点(控制器)。现在有了新的要求:实现X的几个版本,并根据入口点的参数(枚举XType)决定使用哪个版本。如果不改变服务就可以做到这一点。

我对解决方案的想法是创建自定义范围UsesX并实现<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:config="http://www.hp.com/maas/1.0.0/UpgraderConfig" targetNamespace="http://www.hp.com/maas/1.0.0/UpgraderConfig" elementFormDefault="qualified" version="1.0.0"> <xs:simpleType name="stringWithoutWhiteSpace"> <xs:annotation> <xs:documentation>One or more occurrences of any character which is not whitespace (i.e. not space, tab, newline, return)</xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> <xs:minLength value="1"/> <xs:pattern value="\S+"/> </xs:restriction> </xs:simpleType> <xs:complexType name="UpgraderConfigContainer"> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="Upgrader" type="config:UpgraderConfig"/> </xs:sequence> </xs:complexType> <xs:complexType name="UpgraderConfig"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="className" type="config:stringWithoutWhiteSpace"/> </xs:sequence> </xs:complexType> <xs:element name="UpgraderConfigs" type="config:UpgraderConfigContainer"> <xs:unique name="uniqueUpgraderName"> <xs:selector xpath="config:Upgrader"/> <xs:field xpath="config:name"/> </xs:unique> </xs:element> </xs:schema> ,它将使用UsesX将每个BeanFactoryPostProcessor转换为每个XType的单例集。此外,它还会为此bean添加限定符,以便在控制器中为X和基于参数的选择创建工厂方法。但是如何在不改变类的情况下隐式地将这个限定符添加到服务中的BeanDefinition

UPD

好的,例如,我想在@Autowired请求时使用db url "jdbc:mysql://Adb",在A时使用"jdbc:mysql://Bdb"

B

此外,还需要在XML配置中配置enum DatabaseType {A, B} @Controller @RequestMapping(/) class MyController { @Autowired ServiceProvider provider; // some way to get service by DatabaseType void foo(@RequestParam DatabaseType dbType) { ServiceA a = provider.getA(dbType); a.bar(); ServiceB b = provider.getB(dbType); b.baz(); } } @Service class ServiceA { // Don't want to get information about different databases in services @Autowired ServiceB b; @Autowired ServiceC c; @Autowired DaoFoo dao; //... } @Service class ServiceB { @Autowired ServiceC c; @Autowired DaoFoo daoFoo; @Autowired DaoBar daoBar; //... } @Service class ServiceC { @Autowired DaoBar daoBar; //... } @Repository class DaoFoo { DaoFoo(String dbURL) {/*...*/} } @Repository class DaoBar { DaoFoo(String dbURL) {/*...*/} } "jdbc:mysql://Adb"

4 个答案:

答案 0 :(得分:3)

我想结束你的要求,以便我能说明你是否正确。

  1. 您有一组您不想修改的@Service
  2. 目前,此服务只使用X类型的一种实现。
  3. 要在服务中使用的X实现的选择将由XType枚举定义,而{1}}枚举可以从请求中获得。
  4. 您希望X类型bean可以从xml配置。
  5. OP:如果其中一项服务被称为w / o X,应该使用哪种XType实施?

    因此,如果我的理解是正确的,那么Proxy类型似乎需要X。 在此Proxy中,您需要隐式地获取此XType(f.ex.到ThreadLocal var)。 使用@Autowired时,bean首先按类型标识。因此,您需要使用现有的X实现进行代理,并将当前实现和新实现提取到不同类型。

    因此,您可能最终得到以下内容:

    interface newX {
        void save();
    }
    
    @Repository
    class DaoFoo implements newX {
        public void save() {...};
    }
    
    @Repository
    class DaoBar implements newX {
        public void save() {...};
    }
    
    class XImpl implements X, newX {
        public final ThreadLocal<XType> currentXType = new ThreadLo...;
        Map<XType, newX> mapping = ....
        public void save() {mapping.get(currentXType.get()).save();};
    }
    

答案 1 :(得分:1)

您的示例有点混乱,因为您的Service被命名为A和B,但您也使用A和B作为DatabaseType。但我想我明白你想要什么。

我认为您无法使用Autowired执行此操作,但您可以将Service设为@Scope("prototype")并从上下文中检索它们。上下文应该在您第一次请求时实例化Service,然后在提供相同的输入时重用相同的bean。

@Configuration
public class ServiceProvider{
    ...
    @Bean
    @Scope("prototype")
    public ServiceA serviceA(DatabaseType dbType) {
        ...
    }

    @Bean
    @Scope("prototype")
    public ServiceB serviceB(DatabaseType dbType) {
        ...
    }
}

@Controller
@RequestMapping(/)
class MyController {
    @Autowired
    ConfigurableApplicationContext context
    void foo(@RequestParam DatabaseType dbType) {
        AutowireCapableBeanFactory beanFactory = context.getBeanFactory();
        ServiceA serviceA = (ServiceA)context.getBean("serviceA", dbType);
        ...
    }
}

答案 2 :(得分:1)

创建一个服务界面,如:

interface ServiceInterface{
    public boolean isTheOne(String type); // or some suitable name.
}

然后您的所有服务都需要实现此接口,然后在控制器

   @Controller
   @RequestMapping(/)
   class MyController {
   @Autowired 
   Set<ServiceInterface> provider; 

   void foo(@RequestParam DatabaseType dbType) {
     ServiceInterface service = provider.stream().filter(s -> s.isTheOne(String dbType));
     service.bar();
    }
   }

答案 3 :(得分:0)

您可以通过开发Qualifier将XType保持为自定义@Interface内的枚举器。 请在下面找到一个示例,该示例提到了基于不同类型数据类型的bean的条件接线:

@Target({ElementType.FIELD, 
         ElementType.METHOD,
         ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public static @interface DBHost{

public static enum DatabaseType {      
A,
B
}
}


@Autowired
@DBHost(DBHost.DatabaseType.A)
ServiceBean serviceInstanceA;

查找限定符注释here

的其他用法