Scoped proxy isn't created for #Bean-defined beans (Spring 4.0.5) - spring

I have the following config:
<context:component-scan base-package="my.spring.mvc" scoped-proxy="targetClass"/>
I was expected that all my scoped beans would be wrapped, but it doesn't affect those that were created in my #Configuration class via #Bean. To be more concrete:
#Component
#Scope("request")
public class Bean1 {}
works fine, but:
// <- no annotations here
public class Bean1
+
#Configuration
public class BeansProducer {
#Bean
#Scope(value="request")
public Bean1 bean1() {
return new Bean1();
}
}
doesn't inject scoped bean (I get Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean)
Is it possible to make #Bean-beans to be scoped in accordance with the component-scan's setting as it happens in case of regular #Components?

As far as I am aware - it is not possible. I see two options:
Annotate add proxyMode = ScopedProxyMode.TARGET_CLASS to your #Scope annotation.
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Bean1 bean1() {
return new Bean1();
}
If you don't want to repeat all the time #Scope annotation with all the details you can create your custom annotation:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
#interface RequestScoped {
}
And annotate your bean like:
#Bean
#RequestScoped
public Bean1 bean1() {
return new Bean1();
}

Related

Spring Boot: use autowired constructor with class from configuration file

I have a Spring Boot 2.3 application with a controller:
#RestController
public class StatusController {
private final ServerStatusCheck serverStatusCheck;
private final ServerStatusMapper serverStatusMapper;
#Autowired
public StatusController(AService aService, ServerStatusMapper serverStatusMapper) {
this.serverStatusCheck = aService;
this.serverStatusMapper = serverStatusMapper;
}
// (...)
}
The class AService implements the interface ServerStatusCheck. There is also a BService class, also implementing ServerStatusCheck interface.
What I need to do: the injected AService object should be configurable in a configuration file, so that the service injected is either "AService" or "BService", depending on the configuration file values. What is the best way to achieve this using Spring Boot? If possible, I would like to keep the constructor-based autowiring (instead of field-based autowiring).
You can create the different beans in a configuration class with condition like https://reflectoring.io/spring-boot-conditionals/
#Configuration
public class ServiceConfiguration {
#ConditionalOnProperty(value="service.a.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckA() {
return new AService();
}
#ConditionalOnMissingBean
#ConditionalOnProperty(value="service.b.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckB() {
return new BService();
}
}
and then wire the bean into the constructor

Why does Spring reject #Qualifier for a request-scoped bean with proxy mode INTERFACES?

My interface:
public interface SomeInterface {}
is implemented by a request-scoped bean:
#Component
#Qualifier("q")
#Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.INTERFACES)
public class Implementation implements SomeInterface { }
Considering advice like INTERFACES or TARGET_CLASS: Which proxyMode should I choose? suggests the proxy mode INTERFACES should fit this pretty clean setup.
As I want to end up with several alternative implementations of SomeInterface, I throw in an additional #Qualifier("q").
However, Spring rejects the qualifier completely, behaving as if there was no suitable #Component around.
For example, the following test fails with the typical UnsatisfiedDependencyException saying Spring "expected at least 1 bean which qualifies as autowire candidate", but found none for #org.springframework.beans.factory.annotation.Qualifier(value=q).
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {SomeInterface.class, Implementation.class})
public class AutowiringTest {
#Autowired
#Qualifier("q")
private SomeInterface cut;
#Test
public void autowires() {
assertThat(cut).isInstanceOf(SomeInterface.class);
}
}
In contrast, choosing the standard bean name makes the thing work:
#Autowired
#Qualifier("implementation")
private SomeInterface cut; // <-- works
Also, varying the standard bean name instead of adding a #Qualifier makes the thing work:
#Component("q")
#Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.INTERFACES)
public class Implementation implements SomeInterface { }
Also, changing the proxy mode to TARGET_CLASS makes the thing work:
#Component("q")
#Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Implementation implements SomeInterface { }
What is so special about the proxy mode INTERFACES that it won't work in combination with #Qualifier?

Why spring boot does not load beans configuration in order?

When i try to run a spring boot project, it tolde me that it can not autowire some beans whitch are instanciated in a configuration classes.
I think that spring can not load those configuration classes in order.
The stack trace : no bean found the be autowired Ignite<Long,MyEntity> myEntityCache in MyDao
Here is the source :
The main class
#SpringBootApplication
// The beans in the IgniteConfig have to be loaded before dao, service, and Controller
#ComponentScan(basePackageClasses={IgniteConfig.class,AppConfig.class})
public class DemoIgnite {
public static void main(String[] args) {
SpringApplication.run(DemoIgnite .class, args);
}
}
Config Class 1
#Configuration
public class IgniteConfig {
#Bean
public SpringContext springContext() {
return new SpringContext();
}
#Bean
public Ignite igniteInstance(#Autowired SpringContext springContext) {
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setIgniteInstanceName("instance");
List<CacheConfiguration> ccDas = new ArrayList<>();
CacheConfiguration cch = new CacheConfiguration<>("myEntitycache");
cch.setCacheMode(CacheMode.REPLICATED);
cch.setIndexedTypes(Long.class, myEntity.class);
ccDas.add(cch);
cfg.setCacheConfiguration( ccDas.toArray(new CacheConfiguration[0]));
SpringCacheManager springCacheManager = new SpringCacheManager();
springCacheManager.setConfiguration(cfg);
return Ignition.start(cfg);
}
#Bean
public IgniteCache<Long, MyEntity> myEntityCache(#Autowired Ignite igniteInstance) {
return igniteInstance.cache("myEntitycache");
}
Config class 2
#Configuration
#ComponentScan({
"com.demo.repository",
"com.demo.service",
"com.demo.controller"
})
public class AppConfig {
}
Dao class
#Repository
public class MyDao{
#Autowired
private Ignite<Long,MyEntity> myEntityCache;
...
Service class:
#Service
public class MyService{
#Autowird
private MyDao dao;
...
Controller class:
#RestController
#RequestMapping
public class MyController{
#Autowired
private MyService service;
....
This means that you don't have a bean of Ignite<Long,MyEntity> type in your context. Moreover springContext bean seems redundant, it's not used by igniteInstance bean. As pointed out by moilejter it probably should be:
IgniteConfig
#Bean
public Ignite ignite() {
...
}
#Bean
public IgniteCache<Long, MyEntity> myEntityCache() {
return ignite().cache("myEntitycache");
}
MyDao
#Repository
public class MyDao {
#Autowired
private IgniteCache<Long, MyEntity> myEntityCache;
...
}
In principle Spring performs the bean setup in few phases as explained in chapter 1.3.2. Instantiating Beans docs:
Bean definition discovery - resources like #Configuration classes or XML files are scanned and bean signatures are collected.
Eager beans instantiation e.g. singletons - from the definitions collected in point 1 while resolving dependencies between definitions. That's why there is no explicit bean instantiation order as the process is driven from dependencies.
Lazy beans instantiation e.g. #Lazy annotated - when the context is already up, this beans will be constructed only when accessed from code.

Does registering bean inside #Component class respect #Scope?

This website says beans registered inside component classes are not cglib proxied and do not go through the spring container. So does this mean if I register a bean inside a component class (snippet below), adding #Scope("request") wont make any difference, and a new instance of AnotherBean will always be created whenever testBean.anotherBean() is called from some external class?
#Component
public class TestBean {
#Bean
#Scope("request")
public AnotherBean anotherBean() {
return new AnotherBean();
}
}
The bean that is not cglib proxied is the #Component itself, not the bean registered using the #Bean annotation. If you are not calling the anotherBean method explicitly, it won't make a difference because the proxy is used to return the bean when the method annotated with #Bean is called. See the example
The bean testBeanComponent is not cglib proxied :
#Component
public class TestBeanComponent {
#Bean
#Scope("request")
public AnotherBeanComponent anotherBeanComponent() {
return new AnotherBeanComponent();
}
}
The bean testBeanConfiguration is cglib proxied :
#Configuration
public class TestBeanConfiguration {
#Bean
#Scope("request")
public AnotherBeanConfiguration anotherBeanConfiguration() {
return new AnotherBeanConfiguration();
}
}
What it mean :
#Service
public class TestService {
#Autowired //Inject a normal bean
private TestBeanComponent testBeanComponent;
#Autowired //Inject a proxy
private TestBeanConfiguration testBeanConfiguration;
public void test() {
//Calling anotherBeanComponent always return a new instance of AnotherBeanComponent
testBeanComponent.anotherBeanComponent()
.equals(testBeanComponent.anotherBeanComponent()); // is false
//Calling anotherBeanConfiguration return the bean managed by the container
testBeanConfiguration.anotherBeanConfiguration()
.equals(testBeanConfiguration.anotherBeanConfiguration()); // is true
}
}
But if you are injecting the bean instead of using the method, everything will work as you expected :
#Service
public class TestService2 {
#Autowired //Inject a proxy with scope request
private AnotherBeanComponent anotherBeanComponent;
#Autowired //Inject a proxy with scope request
private AnotherBeanConfiguration anotherBeanConfiguration;
}

Java class xml vs java bean autowiring

In xml defined beans you can define two classes like this
<bean id="classA" class="ex.ClassA"/>
<bean id="classB" class="ex.classB"/>
Then in your java implementation you can autowire the constructor of one of the classes in example
public class ClassA {
#autowired
public(ClassB classB){
this.classB = classB;
}
Now how does one do that with java config beans since in example
#Bean
public ClassA classA(){
return new ClassB();
}
#Bean
public ClassB classB(){
return new ClassB()
}
the compiler would warn that Class a does not have any such constructor, how does one do that in java, with autowiring?
Thanks all
See http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-java-injecting-dependencies
Note that the ClassB bean is implicitly a singleton. The use of the annotation #Configuration on the Config class ensures that Spring returns the singleton instance of the ClassB bean in the classB() call.
#Configuration
public class Config {
#Bean
public ClassA classA(){
return new ClassA( classB() );
}
#Bean
public ClassB classB(){
return new ClassB();
}
}
Or you may prefer this approach (Spring 4.2.1+ required)
#Configuration
#Import(ClassA.class)
public class Config {
#Bean
public ClassB classB(){
return new ClassB();
}
}
#Component
public class ClassA {
#Autowired
public ClassA(ClassB classB) {
...
}
}
Pass the beans you want as parameters to the #Bean method, or use component scanning to create the dependent bean implicitly.

Resources