Spring beans обычно создаются при старте контекста с использованием того или иного механизма инициализации. Но если надо, бин можно создать и после инициализации, когда контекcт уже запущен и работает.
Подготовка
Начнём с пустого maven проекта, в который добавим Junit, Hamcrest, EasyMock и Spring:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javaee.version>7.0</javaee.version>
<org.springframework.version>4.1.7.RELEASE</org.springframework.version>
<junit.version>4.12</junit.version>
<hamcrest.version>1.3</hamcrest.version>
<easymock.version>3.3.1</easymock.version>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${easymock.version}</version>
</dependency>
</dependencies>
|
Нам понадобятся два бина. Один мы будем внедрять:
1
2
3
4
5
6
7
8
9
10
11
|
public interface TargetService {
String getTarget();
}
@Service
public class TargetServiceImpl implements TargetService {
@Override
public final String getTarget() {
return "World";
}
}
|
Второй будем конструировать вручную:
1
2
3
4
5
6
7
8
|
public class GreeterService {
@Inject
private TargetService target;
public final String greet() {
return "Hello " + target.getTarget() + "!";
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class GreeterServiceTest {
@Rule
public EasyMockRule em = new EasyMockRule(this);
@Mock
private TargetService targetService;
@TestSubject
private GreeterService testedObject = new GreeterService();
@Test
public void testGreet() {
expect(targetService.getTarget()).andStubReturn("TEST");
replay(targetService);
assertThat(testedObject.greet(), is("Hello TEST!"));
}
}
|
Обратите внимание, что у второго бина нет никакой аннотации, заставившей бы Spring его сконструировать.
Создание бина
Чтобы создать бин, нужно попопросить фабрику бинов создать его:
1
2
3
4
|
AutowireCapableBeanFactory bf = context.getAutowireCapableBeanFactory();
GreeterService greeterBean = bf.createBean(GreeterService.class);
System.out.println(greeterBean.greet());
|
context это экземпляр ApplicationContext. В моём примере я его создаю по месту, но можно его получить через интерфейс ApplicationContextAware или объявить его зависимостью через @Inject.
Вызов createBean() создаёт экземпляр класса и проводит все необходимые инициализационные работы: связывает зависимости, вызывает методы *Aware интерфейсов, вызывает метод инициализации итд. Однако этот бин существует только в виде объекта, который вернула createBean() и он не может быть запрошен из контекста и его жизненный цикл управляется вами, а не Srping’ом.
Регистрация бина
Чтобы создать «настоящий» бин, которым будет управлять Spring и который можно запросить из контекста, необходимо не инстанциировать класс напрямую, а описать будущий бин и попросить Spring построить его по этому описанию:
1
2
3
4
5
6
7
8
9
10
|
GenericBeanDefinition gbd = new GenericBeanDefinition();
gbd.setBeanClass(GreeterService.class);
gbd.setAutowireCandidate(true);
gbd.setScope("singleton");
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) bf;
registry.registerBeanDefinition("greeter", gbd);
GreeterService greeter = (GreeterService) context.getBean("greeter");
System.out.println(greeter.greet());
|
В объекте BeanDefintion можно описать все аспекты создания бина, которые доступны при инициализации с использованием xml или groovy. Описание бина регистрируется в контексте и дальше Spring управляет его жизненным циклом и доступом к бину.
Код примера доступен на github.