【问题标题】:Spring Qualifier and property placeholderSpring 限定符和属性占位符
【发布时间】:2011-12-10 09:30:07
【问题描述】:

有谁知道我是否应该能够将属性占位符用作限定符中的表达式?我似乎无法正常工作。

我正在使用 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 中执行此操作有什么意义?
  • 你看过spring-3 profile beans吗?看起来这可能就是您正在寻找的东西。看起来您应该能够按照您的要求进行操作,因为 spring-3 具有支持解析属性的 @Value 注释。但我只是不确定。
  • @matt 只是想尽量减少 XML 的使用。和属性文件,因为不同的部署会使用不同的实现。

标签: java spring dependency-injection


【解决方案1】:

我大胆猜测答案是否定的,只是基于几个 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】:

    不妨试一试:

    @Controller
    public class MyController {
    
       private String serviceId;
    
       @Value("${serviceId}")
       public void setServiceId(String serviceId) {
          this.serviceId = serviceId;
       }
    
       @Autowired
       @Qualifier(serviceId)
       Service service;
    }
    

    【讨论】:

    • @Aaron docs.oracle.com/javase/1.5.0/docs/guide/language/… 注释由一个 at 符号 (@) 后跟一个注释类型和一个带括号的元素-值对列表组成。这些值必须是编译时常量所以使用实例字符串的解决方法在编译错误过程中将不起作用....
    【解决方案3】:

    这行得通。如果您只使用默认的 spring bean 名称,则可以省略服务名称。 serviceA vs 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
    

    【讨论】:

    • 不知道为什么这没有被接受。这绝对是执行 OP 想要的最简洁的方法。 +1
    • 您在@Autowired 注释上添加required="false" 有什么原因吗?
    • 我只是试了一下,没有使用required="false"。也可以。它实际上非常整洁。我唯一缺少的是:&lt;alias name="${service.class}" alias="Service"/&gt;,这就成功了。谢谢!
    • 不知道有没有办法不用xml,只需要注释?
    【解决方案4】:

    此解决方案在没有 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 用于将自动装配的字段。对于服务,要指定 bean 名称,请使用 @Service
    • 如果您想要标准 bean 名称,则不需要使用 @Service 指定名称。例如,ServiceA 的标准 bean 名称是 serviceA(不是 ServiceA - 见大的第一个字母),所以 @Service("serviceA") 是多余的(@Service 就足够了)。
    • 我基于AppConfig 这个答案:Spring Bean Alias in JavaConfig
    • 这个解决方案比这个Spring Qualifier and property placeholder 更好,因为你不需要XML。
    • 在 Spring Boot 1.5.7 上测试。

    【讨论】:

      【解决方案5】:

      作为一种解决方法,您可以根据 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
      

      【讨论】:

        猜你喜欢
        • 2014-07-16
        • 1970-01-01
        • 2011-09-22
        • 1970-01-01
        • 2016-02-22
        • 2014-03-23
        • 1970-01-01
        • 2013-05-05
        • 2013-12-14
        相关资源
        最近更新 更多