2017-01-06 1 views
-1

У меня есть класс контроллера, который вызывается первым в моем приложении. Там я планировал получить значение из Карты из класса Service.Когда запускать карту в службе, вызванной контроллером весной с впрыском зависимостей?

Вот контроллер:

@Controller 
public class AppController { 

    public Service doSomethingWithTheMap(String key) { 
     return ServiceImpl.getMapValueFor(key).exec(); 
    } 
} 

я получаю вопросы, потому что во время инициализации, а во надетой ценностей на карте Службы, чтобы быть более точным, я требовать BeanFactory, поскольку значения в карте Служащих реализации. Выполнение этого в статическом блоке приведет к тому, что BeanFactory будет нулевым, потому что он не вводится, но я бы догадался.

Таким образом, в результате этого вызова initMap() я чувствую себя немного ... должно быть лучшее решение.

Любые подсказки кто-нибудь?

Должен признать, что я новичок в весне и, может быть, я все испортил. FYI Карта пришла мне в голову после бесконечных, если еще проверяет, какая служба вызывается на основе ввода String. Поэтому я заменил его на карту и простой один вкладыш в контроллере.

ServiceImpl.getMapValueFor(key).exec(); 

Вот класс обслуживания:

@Service 
public class ServiceImpl { 

private static Map<String, Service> map; 

private static ApplicationContext context; 

@Autowired 
public void setApplicationContext(ApplicationContext factory) { 
    this.context = factory; 
} 

public static Service getMapValueFor(String key) { 
    if (map == null) { 
     initMap(); 
    } 
    return map.get(key); 
} 

private static void initMap() { 
    /* 
    * FIXME: We can not init the map in a static block or directly 
    * initialize it since the factory is not injected until execution of a 
    * static block and will be null. 
    */ 
    BeanFactory factory = context; 
    map = new HashMap<String, Service>(); 
    map.put("key", factory.getBean(SomeService.class)); 

} 
} 
+1

Взгляните на: http://stackoverflow.com/questions/1192823/spring-wire-a-static- класс – davesbrain

+0

Спасибо @Dave. Понимаю. Похоже, моя проблема - это порядок инициализации. Пробовал это теперь, используя способ конструктора, инициализирующий (или проводку) класс, содержащий карту, но все же, NullPointer, потому что контекст/фабрика не существует при переносе на карту. – init5

ответ

0

Первое, что я хочу сказать, что у вас есть ошибка, потому что вы используете HashMap без каких-либо синхронизации! - Не волнуйтесь, многие (если не большинство) разработчики java совершили ту же ошибку.

Если вы не упростили код, ваша служба больше похожа на команду, чем на сервис; Услуга - одноэлементный. Невозможно, чтобы службы имели методы без аргументов, но я бы сказал, что это необычно. Вы уверены, что не должны использовать прототипы вместо синглтонов?

Обычно количество услуг ограничено, и если у вас есть несколько сервисов того же типа, вы должны использовать @Qualifier при их автообновлении. В любом случае этот код выглядит изворотливым для меня, поэтому, возможно, вам стоит попытаться объяснить проблему на более высоком уровне, потому что может быть лучший способ, чем ваш текущий код.

+0

Ваша формулировка «команда» точно верна. Я просто упростил свой код для демонстрационных целей. Я назвал это Службой здесь, потому что я пришел вокруг аннотаций Сервиса и Компонента весны. Однако то, что я точно хочу сделать, это вызов класса на основе значения String, полученного в моем контроллере. У меня будет около 200 пар отношений String to Class. – init5

+0

Тогда комментарий ApplicationContextAware - это то, что вам нужно, дайте мне 10 минут, чтобы поднять пример. –

+0

Откуда вы получаете ключ от пользователя, это часть ввода пользователем или? –

0

Ваш класс обслуживания ServiceImpl должен реализовать интерфейс org.springframework.context.ApplicationContextAware, чтобы получить экземпляр контекста приложения Spring.

+0

Спасибо. Не знал об этом. Наверное, я преувеличиваю здесь, и сейчас я полностью работаю против весны. – init5

+0

@ init5, Spring вызовет ваш setApplicationContext, только если ваш класс, имеющий этот метод, реализует 'ApplicationContextAware'. Попробуйте это и сообщите мне, если вы столкнетесь с какими-либо другими проблемами. – VHS

+0

все еще NPE во время initMap(). Проверка для подхода провайдера/простого компонента – init5

0

Это очень простое решение, оно использует тот факт, что имя @Bean - это имя метода, который его создает, вам, вероятно, понадобится лучшая стратегия. Идея заключается в том, чтобы скрыть getBean внутри класса поставщика, который затем может быть Autowired (и проверенного)

import org.springframework.beans.BeansException; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 
import org.springframework.context.annotation.AnnotationConfigApplicationContext; 
import org.springframework.context.annotation.Bean; 

public class Main { 

    public static void main(String[] args) { 

     ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); 
     CallableProvider provider = ctx.getBean(CallableProvider.class); 
     System.out.println(provider.getCommand("aCommand").call()); 
     System.out.println(provider.getCommand("bCommand").call()); 
    } 

    public static class Config { 
     @Bean 
     public ACommand aCommand() { 
      return new ACommand(); 
     } 
     @Bean 
     public BCommand bCommand() { 
      return new BCommand(); 
     } 

     @Bean 
     public CallableProvider callableProvider() { 
      return new CallableProvider(); 
     } 
    } 

    public static class CallableProvider implements ApplicationContextAware { 
     private ApplicationContext context; 

     @Override 
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
      this.context = applicationContext; 
     } 

     public Command getCommand(String name) { 
      return context.getBean(name, Command.class); 
     } 
    } 

    public static class ACommand implements Command { 
     // autowire stuff 
     @Override 
     public String call() { 
      return "A"; 
     } 
    } 

    public static class BCommand implements Command { 
     // autowire stuff 
     @Override 
     public String call() { 
      return "B"; 
     } 
    } 

    public interface Command { 
     String call(); 
    } 
} 
+0

ОК, я понял. Поэтому в основном я бы определил все свои команды с аннотацией Bean и предоставил провайдеру ApplicationContextAware для работы проводки. Я проверю это и дам вам знать. И, конечно, мне нужно инициализировать мою карту где-то, потому что мой пользовательский ввод отличается от имен bean hehe. – init5

+0

Если вы планируете использовать карту в контроллере, к ней обращаются несколько потоков. Как правило, для этого используется ConcurrentHashMap, поскольку он является потокобезопасным, если вы используете HashMap, вы должны синхронизировать инициализацию и каждый доступ. –

+0

Отлично, это сработало. Также с картой. Ваша идея использования равенства имен и методов bean-компонентов привела меня к [этому] (https://github.com/in1t5/spring-annotating). Я удалил Map и определения bean-компонентов в классе конфигурации. Вместо этого я прямо называю bean-компоненты, когда они сканируются Spring с помощью '@Component (« A »)'. Весна говорит: _Создание общего экземпляра одноэлементного bean-компонента A'_. Впоследствии я могу напрямую вызвать 'ctx.getBean (« A », Command.class) .exec()' в контроллере. Что ты думаешь? Или я как-то неправильно использую аннотацию? – init5

Смежные вопросы