Skip to content

Commit ac5a606

Browse files
committed
Add final Configuration class without cglib proxy
As part of performance start-up time improvement, Spring 5.2 brings the option to disable the cglib proxy, allowing henceforth configuration classes to be final. Mind that inner-class bean method call are no longer possible since no proxy will ensure the unique instance (if bean scope defaulted to singleton). Reference: spring-projects/spring-boot#9068
1 parent 8a2c07e commit ac5a606

File tree

6 files changed

+98
-3
lines changed

6 files changed

+98
-3
lines changed

readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ configuration updates.
2727
[This video](https://www.youtube.com/watch?time_continue=367&v=GlV5sXdXPu4&feature=emb_logo&themeRefresh=1) helped me discover this great library.
2828

2929
### Other useful notes
30-
* [Bean post processor](src/main/java/com/ftm/vcp/beanpostprocessor/notes_bean_post_processor.md)
30+
* [Bean post processor](src/main/java/com/ftm/vcp/beanpostprocessor/notes_bean_post_processor.md)
31+
* [Bean configuration full vs lite mode](src/main/java/com/ftm/vcp/beanmode/notes_full_vs_line_mode.md)

src/main/java/com/ftm/vcp/beanmode/config/BeanFullModeConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
77

8+
/**
9+
* @see org.springframework.context.annotation.ConfigurationClassUtils
10+
*/
811
@Configuration
912
public class BeanFullModeConfig {
1013

src/main/java/com/ftm/vcp/beanmode/config/BeanLiteModeConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import com.ftm.vcp.beanmode.model.Person;
55
import org.springframework.context.annotation.Bean;
66

7+
/**
8+
* @see org.springframework.context.annotation.ConfigurationClassUtils
9+
*/
710
public class BeanLiteModeConfig {
811

912
@Bean("john")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.ftm.vcp.beanmode.config;
2+
3+
import com.ftm.vcp.beanmode.model.Name;
4+
import com.ftm.vcp.beanmode.model.Person;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.stereotype.Component;
8+
9+
/**
10+
* @see org.springframework.context.annotation.ConfigurationClassUtils
11+
*/
12+
@Configuration(proxyBeanMethods = false) // This could easily be replaced by @Component
13+
public final class BeanWithoutProxyConfig {
14+
15+
@Bean("john")
16+
Person getPerson(Name name) {
17+
return new Person(name);
18+
}
19+
20+
@Bean
21+
Name getName() {
22+
return new Name("John");
23+
}
24+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
## Bean configuration's lite vs full mode
2+
3+
`@Configuration` leverages cglib's proxy to ensure that the underlying beans declared in the annotated class are unique
4+
(if theis scope is defaulted to singleton). That is the reason why classes stereotyped with `@Configuration` aren't allowed
5+
to be `final`.
6+
7+
That said, **full mode** is when `@Configuration` along with `cglib proxy` are used.
8+
9+
Conversely, beans in **lite mode** can be defined in any other stereotype (including none and `@Configuration(proxyBeanMethods = false)`)
10+
that can incidentally be a final class.
11+
12+
⚠️ Mind that bean method calls inside the declaring class do will create as many new instances as calls.
13+
14+
```java
15+
import org.springframework.context.annotation.Bean;
16+
17+
class LiteModeConfig {
18+
19+
@Bean
20+
Foo foo() {
21+
return new Foo(foo1()); // This will create a new instance of foo1
22+
}
23+
24+
@Bean
25+
Foo foo1() {
26+
return new Foo1();
27+
}
28+
}
29+
```
30+
31+
thus, the correct way yo handle with this would be to leverage parameter injection:
32+
33+
```java
34+
import org.springframework.context.annotation.Configuration;
35+
36+
@Configuration(proxyBeanMethods = false)
37+
final class LiteModeConfig {
38+
39+
@Bean
40+
Foo foo(Foo1 foo1) {
41+
return new Foo(foo1); // using unique instance of foo1
42+
}
43+
44+
@Bean
45+
Foo foo1() {
46+
return new Foo1();
47+
}
48+
}
49+
```

src/test/java/com/ftm/vcp/beanmode/BeanModeTest.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ftm.vcp.beanmode;
22

33
import com.ftm.vcp.beanmode.config.BeanFullModeConfig;
4+
import com.ftm.vcp.beanmode.config.BeanWithoutProxyConfig;
45
import com.ftm.vcp.beanmode.config.BeanLiteModeConfig;
56
import com.ftm.vcp.beanmode.model.Name;
67
import com.ftm.vcp.beanmode.model.Person;
@@ -15,7 +16,7 @@
1516
class BeanModeTest {
1617

1718
@Test
18-
void should_load_new_instances_when_not_using_proper_configuration_annotation() {
19+
void should_load_new_instances_when_using_lite_mode() {
1920
// Given
2021
final var applicationContext = new AnnotationConfigApplicationContext(BeanLiteModeConfig.class);
2122

@@ -28,7 +29,7 @@ void should_load_new_instances_when_not_using_proper_configuration_annotation()
2829
}
2930

3031
@Test
31-
void should_let_spring_create_singleton_beans() {
32+
void should_let_spring_create_singleton_beans_when_using_full_mode() {
3233
// Given
3334
final var applicationContext = new AnnotationConfigApplicationContext(BeanFullModeConfig.class);
3435

@@ -39,4 +40,18 @@ void should_let_spring_create_singleton_beans() {
3940
// Then
4041
then(person.name()).isSameAs(name);
4142
}
43+
44+
@Test
45+
void should_still_let_spring_create_singleton_beans_when_using_full_mode_without_cglib_proxy() {
46+
// Given
47+
final var applicationContext = new AnnotationConfigApplicationContext(BeanWithoutProxyConfig.class);
48+
49+
// When
50+
final var person = applicationContext.getBean(Person.class);
51+
final var name = applicationContext.getBean(Name.class);
52+
53+
// Then
54+
then(person.name()).isSameAs(name);
55+
}
56+
4257
}

0 commit comments

Comments
 (0)