Статья содержит код, который можно найти на GitHub project.
Иногда сталкиваешься с такой ситуацией, когда нужно использовать какой-нибудь сервис, представляющий собой spring bean, в экземпляре, который был создан в коде не spring контейнером, и соответственно, заинжектить обычным способом данный сервис не получится. В таком случае, можно получить нужный бин через контекст.
Например, сервис, который хотим использовать:
@Service public class StringProcessorService {
public String toUpperCase(String str) { return str.toUpperCase(); } }
И класс, который использует данный сервис через контекст:
public void setApplicationContext(ApplicationContext applicationContext) { SpringUtility.applicationContext = applicationContext; }
/** * @param clazz bean class * @param <T> bean type * @return a class bean from the application context */ public static <T> T getBean(final Class<T> clazz) { return SpringUtility.applicationContext.getBean(clazz); } }
Убедимся, что метод экземпляра работает корректно, запустив следующий тест:
Периодически приходится отвечать на вопросы про область видимости bean в Spring Framework. В данном примере разбирается область видимости(scope) prototype, который не часто встречается в коде. Статья содержит код, который можно найти на GitHub project.
Создадим два бина с разной областью видимости.
@Component public class SingletonCounter {
private int count = 0;
public void increment() { count++; }
public int getCount() { return count; } }
Данный бин имеет область видимости singleton, так как по умолчанию, все бины создаются с данной областью видимости. Данный тип видимости можно задать явно, используя аннотацию ConfigurableBeanFactory.SCOPE_SINGLETON.
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class PrototypeCounter {
private final StringProcessor stringProcessor; private int count = 0;
public PrototypeCounter(StringProcessor stringProcessor) { this.stringProcessor = stringProcessor; }
public void increment() { count++; }
public int getCount() { return count; }
public String process(String str) { return stringProcessor.process(str); } }
Данный бин имеет область видимости prototype, которая задается соответствующей аннотацией ConfigurableBeanFactory.SCOPE_PROTOTYPE.
Теперь используем данные бины в классах сервисах, через внедрение с помощью конструктора:
@Service public class PrototypeIncrementService {
private final PrototypeCounter prototypeCounter; private final SingletonCounter singletonCounter;
Из теста видно, что бины с областью видимости singleton создаются в единственном экземпляре, и если у них есть состояние, в данном случае поле private int count, при вызове метода для инкремента, будет инкрементироваться именно оно, где бы мы не использовали сервис с внедренным счетчиком.
Однако, если мы рассмотрим бины с областью видимости prototype, можно увидеть, что каждый раз, при внедрении данных бинов, создается новый экземпляр со своим состоянием. Похожего поведения, можно добиться если написать в программе явный вызов создания объекта через оператор new. Однако, в таком случае, мы потеряем возможность внедрять зависимости и друге бины в наши экземпляры, поскольку контейнер не управляет ими.
Выводы:
Singleton bean
Prototype bean
Создается только один экземпляр. Он используется везде.
Каждый раз при обращению к бину, создается новый экземпляр.
Уничтожается spring при закрытии контекста. Вызывается метод destroy
Уничтожается сборщиком мусора. Spring не управляет удалением экземпляра и обработкой закрытия его ресурсов.
@Component public class UpperCaseProcessor implements Processor {
@Override public String process(String str) { return str.toUpperCase(); } }
В таком случае при инжектировании в spring bean коллекции, типизированной созданным интерфейсом, все бины, имплементирующие данный интерфейс, будут добавлены в эту коллекцию автоматически.
@SpringBootApplication public class ListBeanApplication implements ApplicationRunner {
private final List<Processor> processorList;
@Value("${stringValue}") private String value;
public ListBeanApplication(List<Processor> processorList) { this.processorList = processorList; }
Добавление бинов также можно организовать в ассоциативный массив. Для этого определяем следующий типизированный интерфейс
Map<String, Processor> processorMap;
По умолчанию, если не задано название бина, будет использоваться название класса. Таким образом все бины, имплементирующие интерфейс Processor будут добавлены в эту коллекцию.