Spring限定符和属性占位符

28

有没有人知道我是否应该能够在Qualifier中使用属性占位符作为表达式?我似乎无法使其工作。

我正在使用spring 3.0.4。

@Controller
public class MyController {
   @Autowired
   @Qualifier("${service.class}")
   Service service;
}

@Service
@Qualifier("ServiceA")
ServiceA implements Service {
   public void print() {
       System.out.println("printing ServiceA.print()");
   } 
}

@Service
@Qualifier("ServiceB")
ServiceB implements Service {
   public void print() {
      System.out.println("printing ServiceB.print()");
   } 
}

XML:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="file:/etc/config.properties"/>
</bean>

config.properties:

config.properties
service.class=serviceB

如果你可以在XML中选择如何连接bean,那么在属性文件和@Qualifier中这样做的意义是什么? - matt b
你看过spring-3配置文件中的profile beans吗?这似乎是你要找的东西。因为spring-3有@Value注解来支持属性解析,所以你应该能够做到你所要求的事情。但我不确定。 - Kevin
@matt 只是想要减少使用 XML 和属性文件的频率,因为不同的部署会使用不同的实现。 - Langali
6个回答

41

这样做是有效的。如果只使用默认的Spring Bean名称,则可以省略服务名称。例如,serviceA与ServiceA等。

@Controller
class MyController {
@Autowired(required=false)
@Qualifier("Service")
Service service;

public static void main(String[] args) {
   ApplicationContext context = new ClassPathXmlApplicationContext("app-ctx.xml", MyController.class);
   for(String s:context.getBeanDefinitionNames()){
       System.out.println(s);
       for(String t:context.getAliases(s)){
           System.out.println("\t" + t);
       }
   }
   context.getBean(MyController.class).service.print();
  }
}

public interface Service {
    void print();
}

@Service(value="ServiceA")
public class ServiceA implements example.Service {
    public void print() {
        System.out.println("printing ServiceA.print()");
    } 
}

@Service(value="ServiceB")
public class ServiceB implements example.Service {
    public void print() {
        System.out.println("printing ServiceB.print()");
    } 
}

XML:
<beans>
    <alias name="${service.class}" alias="Service"/>
    <context:property-placeholder location="example/app.properties"/>
    <context:component-scan base-package="example"/>
<beans>

属性:

service.class=ServiceB

2
不知道为什么这个没有被接受。这绝对是实现 OP 想要的最整洁的方法。+1 - lost
你为什么在@Autowired注解上添加了required="false" - stephen.hanson
我刚试了一下,没有使用 required="false"。也可以正常工作。实际上非常不错。唯一缺少的是:<alias name="${service.class}" alias="Service"/>,加上这个就行了。谢谢! - Ondrej Burkert
1
我想知道是否有一种方法可以不使用XML,只使用注释来完成这个任务? - Danny Yeshurun
这个不需要使用XML:https://dev59.com/questions/4l4d5IYBdhLWcg3wJvwp#28988342 - mkczyk
无需使用XML的工作解决方案:https://dev59.com/VWsz5IYBdhLWcg3wj4ra#47074088 - mkczyk

22

这个解决方案不需要使用XML,只需使用属性文件。

您的类已经改进:

MyController.java

@Controller
public class MyController {
    @Autowired
    public MyController(@Qualifier("MyServiceAlias") MyService myService) {
        myService.print();
    }
}

ServiceA.java:

@Service("serviceA")
public class ServiceA implements MyService {
    @Override
    public void print() {
        System.out.println("printing ServiceA.print()");
    }
}

ServiceB.java:

@Service("serviceB")
public class ServiceB implements MyService {
    @Override
    public void print() {
        System.out.println("printing ServiceB.print()");
    }
}

application.properties(你可以在这里更改将要被加载的类):

service.class=serviceA

还有一个重要的配置文件 AppConfig.java:

@Configuration
public class AppConfig {

    @Autowired
    private ApplicationContext context;

    @Bean
    public MyService MyServiceAlias(@Value("${service.class}") String qualifier) {
        return (MyService) context.getBean(qualifier);
    }
}

其他解释:

  • @Qualifier 只用于自动装配的字段上。对于服务(Service),如果要指定 bean 名称,请使用 @Service 注解。
  • 如果您想要标准的 bean 名称,则不需要在 @Service 中使用指定名称。例如,ServiceA 的标准 bean 名称为 serviceA(不是 ServiceA——请注意第一个大写字母),因此 @Service("serviceA") 是多余的(@Service 即可)。
  • 我基于这个答案创建了 AppConfigSpring Bean Alias in JavaConfig
  • 与这个解决方案Spring Qualifier and property placeholder相比更好,因为您不需要 XML。
  • 已在 Spring Boot 1.5.7 上测试通过。


4
我猜答案是否定的,这仅基于一些 javadoc 页面上的说明。例如,请参阅@Value的文档:

http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Value.html

请注意,他们特别提到在注释中使用表达式。作为比较,@Qualifier 的文档如下:

http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Qualifier.html

这段文字并没有提到表达式。显然这不是一个明确的答案(但Spring通常在文档方面做得很好)。另外,如果@Qualifier注释中支持表达式,我会期望它们与@Value注释的工作方式相同(只是因为Spring是一个非常一致的框架)。
Spring 3.1有新的配置文件bean功能,似乎可以实现你试图做的事情。这里有一个相关的介绍:

http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/


2
作为一种解决方法,您可以在config.properties中根据Spring服务的名称设置所需的服务实现。
@Controller
public class MyController {
  //add a String which will hold the name of the service to implement
  @Value("${service.class}")
  private String serviceToImplement;

  Service service;

  // now autowire spring service bean based on int name using setter
  @Autowired
  public void setService(ApplicationContext context) {
    service = (Service) context.getBean(serviceToImplement);
   }
}

@Service
 @Qualifier("ServiceA")
 ServiceA implements Service {
  public void print() {
   System.out.println("printing ServiceA.print()");
  } 
}

 @Service
 @Qualifier("ServiceB")
 ServiceB implements Service {
   public void print() {
    System.out.println("printing ServiceB.print()");
   } 
}

config.properties

service.class=serviceB

0
只需使用@ConditionalOnProperty。 如果您不需要在一个上下文中同时使用这两个服务,那么您的代码可以是这样的:
@Service
@ConditionalOnProperty(value="storage.type", havingValue = "serviceA")
ServiceA implements Service {
   public void print() {
       System.out.println("printing ServiceA.print()");
   } 
}

@Service
@ConditionalOnProperty(value="service.class", havingValue = "serviceB")
ServiceB implements Service {
   public void print() {
      System.out.println("printing ServiceB.print()");
   } 
}

@Controller
public class MyController {
   @Autowired
   Service service;
}

-5

或许试试这个:

@Controller
public class MyController {

   private String serviceId;

   @Value("${serviceId}")
   public void setServiceId(String serviceId) {
      this.serviceId = serviceId;
   }

   @Autowired
   @Qualifier(serviceId)
   Service service;
}

1
@Aaron http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html注释由 '@' 符号后跟注释类型和括号中的元素值对列表组成。这些值必须是编译时常量,因此使用 String 实例的解决方法将不起作用,会导致编译错误... - beagle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接