How to prevent Spring from scoped bean prototype instance when updating context?

I use protan bean definitions in my Spring XML descriptors to set default properties (these beans have many properties), and then I call the application context with getBean(beanName, ctorArgs[]) to instantiate.

Bean definitions require 2-3 constructor arguments that provide logically unique keys used for things like key properties for JMX ObjectName , etc. In addition, the variables that the constructor arguments are written are final.

What I see is that when the application context is updated, it tries to create an instance of these prototypes, which seems completely opposite to what you want to do prototypes. These are patterns, not actual instances. To get around this, I set up prototypes with fake ctor values, so these dummy bean instances are created, and I just filter the created MBeans later in the code.

My question is: how can I configure the application context to register these bean prototypes but not instantiate until I make a getBean call?

+4
source share
2 answers

UPDATE:

The problem is a little more complicated than I thought. In fact, lazy is the default behavior for beans with a prototype. I stumbled a bit, and I was able to reproduce your problem and find a solution. So what is the problem?

You probably have <aop:scoped-proxy/> enabled or ( @ComponentScan(scopedProxy=...) ). During a context update, Spring wraps your bean prototype ( ClosedMetricSubscriberFeed ) with a polarized proxy. It uses a proxy class because (a) the proxy classes are selected or (b) the class has no interfaces.

A class-based proxy is basically a subclass of the CGLIB of your bean, which should call (due to JVM rules) the base class constructor. The generated CGLIB class always calls the no-arg constructor.

I know this sounds complicated, here is what you can do:

  • Disable <aop:scoped-proxy/> . Similar.

  • Provide the no-arg dummy constructor and just in case condemn it. Unfortunately, you will have to detect such dummy cases in manunally. Note that in this case the class will be of type: `.

  • Extract the interface from your class and use the interfaces for trusted proxies:

.

 @Scope( value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.INTERFACES) 

Old answer:

Use lazy initialization using the @Lazy annotation or lazy-init="true" (see 4.4.4 Lazy-initialized beans in the reference documentation).

 <bean id="proto" class="MyPrototype" scope="prototype" lazy-init="true"/> 

or

 @Service @Scope("prototype") @Lazy public class MyPrototype {/*...*/} 
+7
source

I use a closed, obsolete no-arg constructor that throws an IllegalStateException. The context loads fine, getBean () with the args constructor works fine, and getBean () with no args arguments throws an exception.

 package a; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component("myCommand") @Scope("prototype") public class Command { final protected String name; @Deprecated private Command() {throw new IllegalStateException("Only for Spring"); } public Command(String name) { super(); this.name = name; } @Override public String toString() { return "Command [name=" + name + "]"; } } 
0
source

Source: https://habr.com/ru/post/1386849/


All Articles