Spring Прототип scoped bean в одноэлементном
Я пытаюсь внедрить bean-компонент prototype
в bean-компонент singleton
так, чтобы каждый новый вызов метода синглтон-компонента имел новый экземпляр прототипа bean-компонента.
Рассмотрим синглтон-бин, как показано ниже:
@Component
public class SingletonBean {
@Autowired
private PrototypeBean prototypeBean;
public void doSomething() {
prototypeBean.setX(1);
prototypeBean.display();
}
}
Я ожидаю, что каждый раз, когда вызывается метод doSomething()
, используется новый экземпляр PrototypeBean
.
Ниже приведен пример прототипа:
@Component
@Scope(value="prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
Integer x;
void setX(Integer x) {
this.x = x;
}
void display() {
System.out.println(x);
}
}
То, что, похоже, происходит, заключается в том, что spring слишком тяготеет к передаче нового экземпляра PrototypeBean в методе doSomething()
. Таким образом, две строки кода в методе doSomething()
создают новый экземпляр prototypeBean в каждой строке.
И так во 2-й строке - prototypeBean.display()
печатает NULL.
Чего не хватает в конфигурации для этой инъекции?
Ответы
Ответ 1
Из Spring документации:
Вам не нужно использовать <aop:scoped-proxy/>
в сочетании с beans, которые определены как одиночные или прототипы. Если вы попытаетесь создать ограниченный прокси-сервер для singleton bean, BeanCreationException.
Кажется, документация немного изменилась для версии 3.2 документации, где вы можете найти это предложение:
Вам не нужно использовать <aop:scoped-proxy/>
в сочетании с beans, которые определены как одиночные или прототипы.
Кажется, что не предполагается, что вы используете прокси-прототип bean, так как каждый раз, когда он запрашивается BeanFactory
, он создаст новый экземпляр.
Чтобы ваш прототип bean имел вид factory, вы можете использовать ObjectFactory
следующим образом:
@Component
public class SingletonBean {
@Autowired
private ObjectFactory<PrototypeBean> prototypeFactory;
public void doSomething() {
PrototypeBean prototypeBean = prototypeFactory.getObject();
prototypeBean.setX(1);
prototypeBean.display();
}
}
и ваш прототип bean будет объявлен следующим образом:
@Component
@Scope(value="prototype")
public class PrototypeBean {
// ...
}
Ответ 2
Синглтон-бин создается только один раз, поэтому внедренный bean-объект-прототип также создается один раз при создании экземпляра синглтон-компонента. Один и тот же экземпляр bean-компонента-прототипа будет использоваться для каждого запроса.
Если новый экземпляр компонента-прототипа будет создан для каждого запроса во время выполнения, можно использовать приведенный ниже метод Injection
Пример
public class Singleton {
private Prototype prototype;
public Singleton(Prototype prototype) {
this.prototype = prototype;
}
public void doSomething() {
prototype.foo();
}
public void doSomethingElse() {
prototype.bar();
}
}
public abstract class Singleton {
protected abstract Prototype createPrototype();
public void doSomething() {
createPrototype().foo();
}
public void doSomethingElse() {
createPrototype().bar();
}
}
<bean id="prototype" class="ch.frankel.blog.Prototype" scope="prototype" />
<bean id="singleton" class="sample.MySingleton">
<lookup-method name="createPrototype" bean="prototype" />
</bean>
Ответ 3
Правильный способ его достижения - использовать метод поиска методом поиска и везде, где вы использовали beans использовать метод вызова метода поиска (подробный ответ)
Ответ 4
Spring прокладывает ваш beans довольно простым способом. Я работаю в большом коммерческом приложении, и я вставлял следующие фрагменты кода для проверки порядка загрузки.
1) Все ваши структуры oneton bean изначально загружаются Spring (пока Spring знает об этом через аннотации и/или xml). Это случается только однажды. Вы можете проверить это путем записи или печати в статическом блоке:
static {
log.info("#### classNameHere loaded"); //or println if no log setup
}
2) Spring создает все экземпляры singleton, о которых он знает (но не прототипы! экземпляры Prototype будут созданы, если они ссылаются внутри singleton bean - там, естественно, загружаются структуры классов). Вы можете проверить это, добавив этот метод в каждый класс:
@PostConstruct
public void methodHitAfterClassInstantiation() {
LOGGER.info("#### instance of classNameHere");
}
Итак, в вашем примере структуры классов SingletonBean загружаются при запуске Spring. Создается новый экземпляр SingletonBean.
И поскольку PrototypeBean Autowired внутри SingletonBean, его структура классов загружается и создается экземпляр. Теперь, если был еще один bean, скажем AnotherSingletonBean, с Autwired PrototypeBean внутри
из него будет создан РАЗЛИЧНЫЙ экземпляр PrototypeBean (нет необходимости снова загружать структуру класса). Таким образом, существует только 1 SingletonBean, а внутри него PrototypeBean, который
всегда будет указывать на тот же bean. Из-за этого синглтоны всегда должны быть безгражданными, так как все ваши другие beans, которые используют синглтон, будут указывать на
тот же объект. Но вы можете сохранить состояние в прототипе bean, потому что везде, где вы создаете новую ссылку, вы укажете на другой объект bean.
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes-prototype