복붙노트

[SPRING] Spring MessageSource는 다중 클래스 경로를 지원합니까?

SPRING

Spring MessageSource는 다중 클래스 경로를 지원합니까?

스프링 프레임 워크를 사용하여 웹 기반 응용 프로그램을위한 플러그인 시스템을 설계하고 있습니다. 플러그인은 classpath에있는 jar 파일입니다. 그래서 나는 jsp 같은 소스를 얻을 수있다. 아래를 보라.

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] pages = resolver.getResources("classpath*:jsp/*jsp");

여태까지는 그런대로 잘됐다. 하지만 messageSource에 문제가 있습니다. ReloadableResourceBundleMessageSource # setBasename은 "classpath * :"를 통해 여러 클래스 경로를 지원하지 않는 것 같습니다. "classpath :"를 사용하면 하나의 플러그인에서만 messageSource를 얻을 수 있습니다.

누구든지 모든 플러그인에서 messageSources를 등록하는 방법을 알고 있습니까? MessageSource와 같은 구현이 존재합니까?

해결법

  1. ==============================

    1.여기서 문제는 여러 클래스 패스 나 클래스 로더가 아니라 코드가 주어진 경로에 대해로드하고로드 할 리소스의 수를 의미합니다.

    여기서 문제는 여러 클래스 패스 나 클래스 로더가 아니라 코드가 주어진 경로에 대해로드하고로드 할 리소스의 수를 의미합니다.

    classpath * 구문은 스프링 메커니즘으로, 코드가 주어진 경로에 대해 여러 리소스를로드 할 수있게합니다. 매우 편리합니다. 그러나 ResourceBundleMessageSource는 표준 java.util.ResourceBundle을 사용하여 리소스를로드합니다.이 방법은 주어진 경로에 대한 첫 번째 리소스를로드하고 다른 모든 리소스를 무시하는 훨씬 간단한 덤버 메커니즘입니다.

    나는 너에게 쉬운 문제가 정말로 없다. ResourceBundleMessageSource를 파기하고 PathMatchingResourcePatternResolver를 사용하여 다양한 자원을 찾고 MessageSource 인터페이스를 통해 노출시키는 MessageSource의 사용자 정의 구현 (대부분 AbstractMessageSource를 서브 클래 싱하여)을 작성해야한다고 생각합니다. ResourceBundle은별로 도움이되지 않습니다.

  2. ==============================

    2.@ seralex-vi basenames / WEB-INF / messages의 해결책이 작동하지 않았습니다.

    @ seralex-vi basenames / WEB-INF / messages의 해결책이 작동하지 않았습니다.

    메소드 새로 고침을 덮어 ​​씁니다. 클래스 이름 ReloadableResourceBundleMessageSource의 Properties는 두 가지 기본 이름 (classpath * 및 / WEB-INF /)을 모두 수행합니다.

    public class SmReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
    
    private static final String PROPERTIES_SUFFIX = ".properties";
    
    private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    
    @Override
    protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
        if (filename.startsWith(PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)) {
            return refreshClassPathProperties(filename, propHolder);
        } else {
            return super.refreshProperties(filename, propHolder);
        }
    }
    
    private PropertiesHolder refreshClassPathProperties(String filename, PropertiesHolder propHolder) {
        Properties properties = new Properties();
        long lastModified = -1;
        try {
          Resource[] resources = resolver.getResources(filename + PROPERTIES_SUFFIX);
          for (Resource resource : resources) {
            String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, "");
            PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder);
            properties.putAll(holder.getProperties());
            if (lastModified < resource.lastModified())
              lastModified = resource.lastModified();
          }
        } catch (IOException ignored) { 
        }
        return new PropertiesHolder(properties, lastModified);
    }
    

    spring-context.xml에는 classpath * : 접두어가 있어야합니다.

    <bean id="messageSource" class="SmReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>/WEB-INF/i18n/enums</value>
                <value>/WEB-INF/i18n/messages</value>
                <value>classpath*:/META-INF/messages-common</value>
                <value>classpath*:/META-INF/enums</value>
            </list>
        </property>
    </bean>
    
  3. ==============================

    3.근본적으로 각각의 관련 basename을 명시 적으로 지정할 수 있습니다.

    근본적으로 각각의 관련 basename을 명시 적으로 지정할 수 있습니다.

     <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <value>classpath:com/your/package/source1</value>
                    <value>classpath:com/your/second/package/source2</value>
                    <value>classpath:com/your/third/package/source3/value>
                    <value>classpath:com/your/fourth/package/source4</value>
                </list>
            </property>
        </bean>
    
  4. ==============================

    4.다른 방법으로는 아래 예제처럼 ReloadableResourceBundleMessageSource 클래스에서 refreshProperties 메서드를 재정의 할 수 있습니다.

    다른 방법으로는 아래 예제처럼 ReloadableResourceBundleMessageSource 클래스에서 refreshProperties 메서드를 재정의 할 수 있습니다.

    public class MultipleMessageSource extends ReloadableResourceBundleMessageSource {
      private static final String PROPERTIES_SUFFIX = ".properties";
      private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    
      @Override
      protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
        Properties properties = new Properties();
        long lastModified = -1;
        try {
          Resource[] resources = resolver.getResources(filename + PROPERTIES_SUFFIX);
          for (Resource resource : resources) {
            String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, "");
            PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder);
            properties.putAll(holder.getProperties());
            if (lastModified < resource.lastModified())
              lastModified = resource.lastModified();
          }
        } catch (IOException ignored) { }
        return new PropertiesHolder(properties, lastModified);
      }
    }
    

    ReloadableResourceBundleMessageSource와 같은 스프링 컨텍스트 구성과 함께 사용하십시오.

      <bean id="messageSource" class="common.utils.MultipleMessageSource">
        <property name="basenames">
          <list>
            <value>classpath:/messages/validation</value>
            <value>classpath:/messages/messages</value>
          </list>
        </property>
        <property name="fileEncodings" value="UTF-8"/>
        <property name="defaultEncoding" value="UTF-8"/>
      </bean>
    

    이 트릭을해야한다고 생각합니다.

  5. ==============================

    5.Java 구성 및 계층 적 메시지 소스를 활용하여 아주 간단한 플러그인 시스템을 구축 할 수 있습니다. 플러그 가능한 항아리에 다음과 같은 클래스를 추가하십시오.

    Java 구성 및 계층 적 메시지 소스를 활용하여 아주 간단한 플러그인 시스템을 구축 할 수 있습니다. 플러그 가능한 항아리에 다음과 같은 클래스를 추가하십시오.

    @Configuration
    public class MyPluginConfig {
        @Bean
        @Qualifier("external")
        public HierarchicalMessageSource mypluginMessageSource() {
            ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
            messageSource.setBasenames("classpath:my-plugin-messages");
            return messageSource;
        }
    }
    

    및 해당 my-plugin-messages.properties 파일을 참조하십시오.

    기본 응용 프로그램에서 Java config 클래스에 다음과 같이 입력하십시오.

    @Configuration
    public class MainConfig {
        @Autowired(required = false)
        @Qualifier("external")
        private List<HierarchicalMessageSource> externalMessageSources = Collections.emptyList();
    
        @Bean
        public MessageSource messageSource() {
            ReloadableResourceBundleMessageSource rootMessageSource = new ReloadableResourceBundleMessageSource();
            rootMessageSource.setBasenames("classpath:messages");
    
            if (externalMessageSources.isEmpty()) {
                // No external message sources found, just main message source will be used
                return rootMessageSource;
            }
            else {
                // Wiring detected external message sources, putting main message source as "last resort"
                int count = externalMessageSources.size();
    
                for (int i = 0; i < count; i++) {
                    HierarchicalMessageSource current = externalMessageSources.get(i);
                    current.setParentMessageSource( i == count - 1 ? rootMessageSource : externalMessageSources.get(i + 1) );
                }
                return externalMessageSources.get(0);
            }
        }
    }
    

    플러그인의 순서가 적절하다면 @Order 주석을 각 플러그 가능한 메시지 소스 빈에 넣기 만하면됩니다.

  6. ==============================

    6.ReloadableResourceBundleMessageSource :: calculateFilenamesForLocale을 재정의하는 것이 더 나을 수도 있습니다. 그런 다음 ReloadableResourceBundleMessageSource :: getProperties는 cachedProperties에서 PropertiesHolder를 가져올 수 있습니다.

    ReloadableResourceBundleMessageSource :: calculateFilenamesForLocale을 재정의하는 것이 더 나을 수도 있습니다. 그런 다음 ReloadableResourceBundleMessageSource :: getProperties는 cachedProperties에서 PropertiesHolder를 가져올 수 있습니다.

  7. from https://stackoverflow.com/questions/3888832/does-spring-messagesource-support-multiple-class-path by cc-by-sa and MIT license