Subclass cannot create constructor - java

I am using Spring Boot to create an application. Currently, I have an abstract class that looks like this:
public abstract class DB {
private final String dbName;
private final String dbServerName;
public DB(String dbName, String dbServerName) {
this.dbName = dbName;
this.dbServerName = dbServerName;
}
public String getDbName() {
return dbName;
}
public String getDbServerName() {
return dbServerName;
}
abstract Boolean pushDataToDB();
}
This abstract class should be used by the following class:
#Component
public class InfluxDB extends DB {
#Autowired
public InfluxDB(String dbName, String dbServerName) {
super(dbName, dbServerName);
}
#Override
public Boolean pushDataToDB() {
return true;
}
}
However, I'm getting the error with the following error: Could not autowire, no beans of String Type found. I'm getting this error with the constructor's arguments for both dbName and dbServername. What is the reason for this error?

use #Value to inject value from properties file or system variables. It typically has a format like ${database.uri}

The #Autowired annotation injects the types defined in the constructor, in your case, two Strings, and if you need this class to be #Component and still need this constructor, then you need to define Spring Beans to be injected.
I would create a #ConfigurationProperties class and inject it instead of multiple Strings.

Remove the Autowired, it's not neccesary there.
Autowired is to call a Beaned object.
I recommend you this https://www.baeldung.com/spring-autowire

Related

Not able to pass String to a constructor in Java using Spring

I'm trying to pass parameter to one of constructor of my BBFilter component, however it throws the exception that No beans of String type found. I have autowired the constructor as well. Am I doing anything wrong? Please advise
#Bean
public MyBean bbFilter() {
BBBean bbBean = new BBBean();
bbBean.setFilter(new BBFilter("plan1"));
}
BBFilter
#Component
public class BBFilter implements Filter {
private String planType;
#Autowired
public BBFilter(String planType) { --> Could not autowire. No beans of String type found
this.planType = planType;
}
}
I am assuming you are using Spring. The #Component annotation tells spring to automatically create an Instance of BBFilter as a Bean.
You also annotated the constructor with #Autowired. So Spring searches it's beans for fitting types and injects the automatically on construction. Since you probably didn't define any String bean it cannot autowire the String and throws an exception.
But since you want to create the Filter manually anyways you can simply remove both annotations from your BBFilter Class:
public class BBFilter implements Filter {
private String planType;
public BBFilter(String planType) {
this.planType = planType;
}
}
This should fix the exception but you also can no longer inject it anywhere else (per #Autowire) if needed.
Declare bean of BBFilter like
#Bean
public BBFilter bbFilter() {
return new BBFilter("plan1");
}
And use it in BBBean like this
#Bean
public MyBean bbFilter() {
BBBean bbBean = new BBBean();
bbBean.setFilter(bbFilter());
}
And remove #Component and #Autowired from BBFilter

How to access application.properties values in constructor

I am trying to use #ConfigurationProperties to load key-value pairs from application.properties file.
application.properties
soap.action.segalRead=Segal/SegalRead
soap.action.mantilUpdate=Mantil/MantilUpdate
SoapUri.java
#ConfigurationProperties(prefix = "soap.action")
public class SoapUri {
#NotNull
private String segalRead;
#NotNull
private String mantilUpdate;
//getters and setters
}
SoapUriTests.java
#RunWith(SpringRunner.class)
#SpringBootTest
public class SoapUriTests {
#Autowired
private SoapUri soapUri;
#Test
public void testSoapUri_returnsSoapAction() {
assertThat(soapUri.getSegalRead()).isEqualTo("Segal/SegalRead");
assertThat(soapUri.getMantilUpdate()).isEqualTo("Mantil/MantilUpdate");
}
}
Above unit test works great.
However, I need to use SoapUri in real code.
Consider following code:
public class MantilUpdateReadVO extends RequestClientVO {
#Autowired
private SoapUri soapUri;
public MantilUpdateReadVO(final MantilUpdate mantilUpdate) {
super(mantilUpdate, soapUri.getMantilUpdate(), MantilUpdateResponse.class);
}
}
public class RequestClientVO {
private Object readRequest;
private String serviceName;
private Class<?> unmarshalTargetclass;
public MwsRequestClientVO(Object readRequest, String serviceName, Class<?> unmarshalTargetclass) {
super();
this.readRequest = readRequest;
this.serviceName = serviceName;
this.unmarshalTargetclass = unmarshalTargetclass;
}
//getters and setters
}
Above complains about: "Cannot refer to an instance field soapUri while explicitly invoking a constructor"
Does anyone know a workaround for injecting segalRead and mantilUpdate in constructor of super()
You are using field-injection, which is not a good idea. See Oliver Gierke's Why Field Injection is Evil for details.
The field cannot be injected until after the instance is constructed; so, you cannot use an injected field during construction.
Change the code like this:
#Autowired
public MantilUpdateReadVO(final SoapUri soapUri, final MantilUpdate mantilUpdate) {
super(mantilUpdate, soapUri.getMantilUpdate(), MantilUpdateResponse.class);
}
You also need to ensure MantilUpdateReadVO is a Spring Bean; might need to add #Component.
Good luck!

custom json serializer (Bean injection)

I have a interface which cntaining two implementations.
public interface IEncryptDecryptService {
String encrypt(String text);
String decrypt(String text);
}
one class is
#Service("SunJks")
public class GeneralEncryptDecryptServiceImpl implements
IEncryptDecryptService {
public String encrypt{
}
public String decrypt{
}
}
another class is
#Service("SafenetHsm")
public class SafenetHsmEncryptDecryptServiceImpl implements
IEncryptDecryptService {
public String encrypt{
}
public String decrypt{
}
}
I want to inject one of two classes in another class.
#Component
public class LogService implements ILogService {
#Resource(name = "${vault.encryptdecrypt.provider}")
private IEncryptDecryptService edservice;
public display{
edservice.encrypt("***");
}
This is the class where i need two inject the one of the two beans.
In application.properties i have configured that
#Provider Configurer
vault.encryptdecrypt.provider=SunJks
then "GeneralEncryptDecryptServiceImpl"is injected.
#Provider Configurer
vault.encryptdecrypt.provider=SafenetHsm
then SafenetHsmEncryptDecryptServiceImpl is injected into the "LogService" class.
it works fine.
and if i implement same thing in Custom JsonSerializer class it is not working,bean is not injected.
#Component
public class MaskSerializer extends JsonSerializer<Xclass> {
#Resource(name = "${vault.encryptdecrypt.provider}")
private IEncryptDecryptService edservice;
#Override
public void serialize(Xclass value, JsonGenerator gen,
SerializerProvider provider) throws IOException {
String str = value.getPersistenceValue();
String strr = edservice.encrypt(str);
gen.writeStartObject();
gen.writeFieldName(strr);
gen.writeEndObject();
}
i am getting nullpointer exception at edservice.encrypt(str) in above class.
Bean is not injected????
From your code I'm assuming you are using Spring. I can think of two reason why field edservice may be null null:
Property vault.encryptdecrypt.provider is not found in its application context.
Bean(s) with given name(s) are not found in its application context.
You can add
#Value("vault.encryptdecrypt.provider") private String provider;
to class MaskSerializer to determine if the property is found - if it is then you'll know that the bean and not the property is missing from the application context. Then you simply need to define that particular bean in that particular context. Otherwise you 'll need to define an appropriate PropertySource to read your property file.
The above is of course assuming that MaskSerializer and LogService beans are in different application contexts. Otherwise the only thing I can think of is that you're doing this:
MaskSerializer maskSerializer = new MaskSerializer();
instead of this (or equivalent with #Resource or #Inject):
#Autowired MaskSerializer maskSerializer;
somewhere in your code.

How to assign a value from application.properties to a static variable?

I am using Spring MVC. I have a UserService class annotated with #Service that has a lot of static variables. I would like to instantiate them with values from the application.properties file.
For example in application.properties I have: SVN_URL = http://some.url/repositories
Then in the class there is: #Value("${SVN_URL}") private static String SVN_URL
I get the Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError
I have also tried #Autowired private static Environment env;
And then: private static String SVN_URL=env.getProperty("SVN_URL");
It gives the same error.
Think about your problem for a second. You don't have to keep any properties from application.properties in static fields. The "workaround" suggested by Patrick is very dirty:
you have no idea when this static field is modified
you don't know which thread modifies it's value
any thread at any time can change value of this static field and you are screwed
initializing private static field that way has no sense to me
Keep in mind that when you have bean controlled by #Service annotation you delegate its creation to Spring container. Spring controls this bean lifecycle by creating only one bean that is shared across the whole application (of course you can change this behavior, but I refer to a default one here). In this case any static field has no sense - Spring makes sure that there is only one instance of UserService. And you get the error you have described, because static fields initialization happens many processor-cycles before Spring containers starts up. Here you can find more about when static fields are initialized.
Suggestion
It would be much better to do something like this:
#Service
public class UserService {
private final String svnUrl;
#Autowired
public UserService(#Value("${SVN_URL}") String svnUrl) {
this.svnUrl = svnUrl;
}
}
This approach is better for a few reasons:
constructor injection describes directly what values are needed to initialize the object
final field means that this value wont be changed after it gets initialized in a constructor call (you are thread safe)
Using #ConfigurationProperties
There is also another way to load multiple properties to a single class. It requires using prefix for all values you want to load to your configuration class. Consider following example:
#ConfigurationProperties(prefix = "test")
public class TestProperties {
private String svnUrl;
private int somePort;
// ... getters and setters
}
Spring will handle TestProperties class initialization (it will create a testProperties bean) and you can inject this object to any other bean initialized by Spring container. And here is what exemplary application.properties file look like:
test.svnUrl=https://svn.localhost.com/repo/
test.somePort=8080
Baeldung created a great post on this subject on his blog, I recommend reading it for more information.
Alternative solution
If you need somehow to use values in static context it's better to define some public class with public static final fields inside - those values will be instantiated when classloader loads this class and they wont be modified during application lifetime. The only problem is that you won't be able to load these values from Spring's application.properties file, you will have to maintain them directly in the code (or you could implement some class that loads values for these constants from properties file, but this sounds so verbose to the problem you are trying to solve).
Spring does not allow to inject value into static variables.
A workaround is to create a non static setter to assign your value into the static variable:
#Service
public class UserService {
private static String SVN_URL;
#Value("${SVN_URL}")
public void setSvnUrl(String svnUrl) {
SVN_URL = svnUrl;
}
}
Accessing application.properties in static member functions is not allowed but here is a work around,
application.properties
server.ip = 127.0.0.1
PropertiesExtractor.java
public class PropertiesExtractor {
private static Properties properties;
static {
properties = new Properties();
URL url = new PropertiesExtractor().getClass().getClassLoader().getResource("application.properties");
try{
properties.load(new FileInputStream(url.getPath()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static String getProperty(String key){
return properties.getProperty(key);
}
}
Main.class
public class Main {
private static PropertiesExtractor propertiesExtractor;
static{
try {
propertiesExtractor = new PropertiesExtractor();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public static getServerIP(){
System.out.println(propertiesExtractor.getProperty("server.ip")
}
}
static String profile;
#Value("${spring.profiles.active:Unknown}")
private void activeProfile(String newprofile) {
profile = newprofile;
};
In order to gain static access to Spring Boot properties you can create a Properties Holder Component which implements the Command Line Runner interface. The command line runner interface executes run() upon component instantiation by Spring Boot.
Since we have autowired access to our properties object in the PropertiesHolder component, it is possible to assign the autowired properties to a static Properties class variable upon CommandLineRunner execution of the run() method.
At this point any class can statically call PropertiesHolder.getProperties() to access the contents of Spring Boot properties values.
PropertiesHolder.class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class PropertiesHolder implements CommandLineRunner {
//Spring Boot Autowired Properties Object
#Autowired MyProperties myAutowiredProperties;
//Statically assigned Properties Object
private static MyProperties properties;
//Hide constructor (optional)
private PropertiesHolder(){}
public static MyProperties getProperties() throws NullPointerException{
if(PropertiesHolder.properties == null)
throw new NullPointerException("Properites have not been initialized by Spring Application before call.");
return PropertiesHolder.properties;
}
//Use to assign autowired properties to statically allocated properties
public static void makeAvailable(MyProperties myAutowiredProperties){
PropertiesHolder.properties = myAutowiredProperties;
}
//Spring Boot command line runner autoexecuted upon component creation
//which initializes the static properties access
public void run(String... args) {
PropertiesHolder.makeAvailable(myAutowiredProperties);
}
}
MyProperties.class
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
//Example: your_properties_file_prefix.properties
#ConfigurationProperties(prefix = "YOUR_PROPERTIES_FILE_PREFIX")
#Component
#Data
public class MyProperties {
private String property1;
private String property2;
private String property3;
}
At least one more simple solution with configuration file:
#Configuration
public class YourStaticPropertyConfiuration {
public static String PROPERTY_NAME;
#Value("${propertyName}")
public void setProperty(final String propertyName) {
PROPERTY_NAME = propertyName;
}
}
Use PROPERTY_NAME anywhere as static variable
For all those who, for whatever reason, want to provide setting properties imported from files as static properties, here is a solution that is as simple and safe as possible.
The Problem:
Spring Boot unfortunately doesn't provide a simple way to import properties from a file and bind them as static properties to a class. One possible solution to achieve that anyway would be to set the static properties using `#Value` annotations like this:
public class GlobalProperties {
public static String NAME;
#Value("${configs.boss.name}")
public void setName(String name) {
NAME = name;
}
}
However, this approach would mean that the static properties cannot be declared as final.And we certainly don't want that.
The Solution:
application.yml:
configs:
boss:
name: "Leeloo Multipass"
ConfigProperties.java:
#Validated
#ConfigurationProperties(prefix = "configs")
public record ConfigProperties(#NotNull Boss boss) {
public static final String BOSS_NAME = BOSS.NAME;
private static class Boss {
private static String NAME;
public Boss(String name) {
NAME = name;
}
}
}
The solution is based on the assumption that Spring Boot builds the configuration objects first, and the properties' configuration objects first of all. So at the time Spring Boot adds the prepared objects as Bean to the context, the nested classes are already setup and the static properties initialization of ConfigProperties can access the static properties of the nested classes (which still are not final, but also not accessible from outside). This way it is possible to provide all properties declared as static final. Unless Spring Boot doesn't decide to change its internal initialization process, everything is cream & cookie.
This approach was tested with Spring Boot 3 and Java 17. It is of course possible to provide the properties additionally via the configs-bean. For that, the properties of the nested classes must be explicitly specified and their corresponding getters must be implemented. In this case, some simplification can be achieved by using records instead of classes.

Spring Java Config: how do you create a prototype-scoped #Bean with runtime arguments?

Using Spring's Java Config, I need to acquire/instantiate a prototype-scoped bean with constructor arguments that are only obtainable at runtime. Consider the following code example (simplified for brevity):
#Autowired
private ApplicationContext appCtx;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = appCtx.getBean(Thing.class, name);
//System.out.println(thing.getName()); //prints name
}
where the Thing class is defined as follows:
public class Thing {
private final String name;
#Autowired
private SomeComponent someComponent;
#Autowired
private AnotherComponent anotherComponent;
public Thing(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
Notice name is final: it can only be supplied via a constructor, and guarantees immutability. The other dependencies are implementation-specific dependencies of the Thing class, and shouldn't be known to (tightly coupled to) the request handler implementation.
This code works perfectly well with Spring XML config, for example:
<bean id="thing", class="com.whatever.Thing" scope="prototype">
<!-- other post-instantiation properties omitted -->
</bean>
How do I achieve the same thing with Java config? The following does not work using Spring 3.x:
#Bean
#Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
Now, I could create a Factory, e.g.:
public interface ThingFactory {
public Thing createThing(String name);
}
But that defeats the entire point of using Spring to replace the ServiceLocator and Factory design pattern, which would be ideal for this use case.
If Spring Java Config could do this, I would be able to avoid:
defining a Factory interface
defining a Factory implementation
writing tests for the Factory implementation
That's a ton of work (relatively speaking) for something so trivial that Spring already supports via XML config.
In a #Configuration class, a #Bean method like so
#Bean
#Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
is used to register a bean definition and provide the factory for creating the bean. The bean that it defines is only instantiated upon request using arguments that are determined either directly or through scanning that ApplicationContext.
In the case of a prototype bean, a new object is created every time and therefore the corresponding #Bean method is also executed.
You can retrieve a bean from the ApplicationContext through its BeanFactory#getBean(String name, Object... args) method which states
Allows for specifying explicit constructor arguments / factory method
arguments, overriding the specified default arguments (if any) in the
bean definition.
Parameters:
args arguments to use if creating a prototype using explicit arguments
to a static factory method. It is invalid to use a non-null args value
in any other case.
In other words, for this prototype scoped bean, you are providing the arguments that will be used, not in the constructor of the bean class, but in the #Bean method invocation. (This method has very weak type guarantees since it uses a name lookup for the bean.)
Alternatively, you can use the typed BeanFactory#getBean(Class requiredType, Object... args) method which looks up the bean by type.
This is at least true for Spring versions 4+.
Note that, if you don't want to start with the ApplicationContext or BeanFactory for your bean retrieval, you can inject an ObjectProvider (since Spring 4.3).
A variant of ObjectFactory designed specifically for injection points,
allowing for programmatic optionality and lenient not-unique handling.
and use its getObject(Object... args) method
Return an instance (possibly shared or independent) of the object
managed by this factory.
Allows for specifying explicit construction arguments, along the lines
of BeanFactory.getBean(String, Object).
For example,
#Autowired
private ObjectProvider<Thing> things;
[...]
Thing newThing = things.getObject(name);
[...]
With Spring > 4.0 and Java 8 you can do this more type-safely:
#Configuration
public class ServiceConfig {
#Bean
public Function<String, Thing> thingFactory() {
return name -> thing(name); // or this::thing
}
#Bean
#Scope(value = "prototype")
public Thing thing(String name) {
return new Thing(name);
}
}
Usage:
#Autowired
private Function<String, Thing> thingFactory;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = thingFactory.apply(name);
// ...
}
So now you can get your bean at runtime. This is a factory pattern of course, but you can save some time on writing specific class like ThingFactory (however you will have to write custom #FunctionalInterface to pass more than two parameters).
Since Spring 4.3, there is new way to do it, which was sewed for that issue.
ObjectProvider - It enables you just to add it as a dependency to your "argumented" Prototype scoped bean and to instantiate it using the argument.
Here is a simple example of how to use it:
#Configuration
public class MyConf {
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public MyPrototype createPrototype(String arg) {
return new MyPrototype(arg);
}
}
public class MyPrototype {
private String arg;
public MyPrototype(String arg) {
this.arg = arg;
}
public void action() {
System.out.println(arg);
}
}
#Component
public class UsingMyPrototype {
private ObjectProvider<MyPrototype> myPrototypeProvider;
#Autowired
public UsingMyPrototype(ObjectProvider<MyPrototype> myPrototypeProvider) {
this.myPrototypeProvider = myPrototypeProvider;
}
public void usePrototype() {
final MyPrototype myPrototype = myPrototypeProvider.getObject("hello");
myPrototype.action();
}
}
This will of course print hello string when calling usePrototype.
UPDATED per comment
First, I'm not sure why you say "this does not work" for something that works just fine in Spring 3.x. I suspect something must be wrong in your configuration somewhere.
This works:
-- Config File:
#Configuration
public class ServiceConfig {
// only here to demo execution order
private int count = 1;
#Bean
#Scope(value = "prototype")
public TransferService myFirstService(String param) {
System.out.println("value of count:" + count++);
return new TransferServiceImpl(aSingletonBean(), param);
}
#Bean
public AccountRepository aSingletonBean() {
System.out.println("value of count:" + count++);
return new InMemoryAccountRepository();
}
}
-- Test File to execute:
#Test
public void prototypeTest() {
// create the spring container using the ServiceConfig #Configuration class
ApplicationContext ctx = new AnnotationConfigApplicationContext(ServiceConfig.class);
Object singleton = ctx.getBean("aSingletonBean");
System.out.println(singleton.toString());
singleton = ctx.getBean("aSingletonBean");
System.out.println(singleton.toString());
TransferService transferService = ctx.getBean("myFirstService", "simulated Dynamic Parameter One");
System.out.println(transferService.toString());
transferService = ctx.getBean("myFirstService", "simulated Dynamic Parameter Two");
System.out.println(transferService.toString());
}
Using Spring 3.2.8 and Java 7, gives this output:
value of count:1
com.spring3demo.account.repository.InMemoryAccountRepository#4da8692d
com.spring3demo.account.repository.InMemoryAccountRepository#4da8692d
value of count:2
Using name value of: simulated Dynamic Parameter One
com.spring3demo.account.service.TransferServiceImpl#634d6f2c
value of count:3
Using name value of: simulated Dynamic Parameter Two
com.spring3demo.account.service.TransferServiceImpl#70bde4a2
So the 'Singleton' Bean is requested twice. However as we would expect, Spring only creates it once. The second time it sees that it has that bean and just returns the existing object. The constructor (#Bean method) is not invoked a second time. In deference to this, when the 'Prototype' Bean is requested from the same context object twice we see that the reference changes in the output AND that the constructor (#Bean method) IS invoked twice.
So then the question is how to inject a singleton into a prototype. The configuration class above shows how to do that too! You should pass all such references into the constructor. This will allow the created class to be a pure POJO as well as making the contained reference objects immutable as they should be. So the transfer service might look something like:
public class TransferServiceImpl implements TransferService {
private final String name;
private final AccountRepository accountRepository;
public TransferServiceImpl(AccountRepository accountRepository, String name) {
this.name = name;
// system out here is only because this is a dumb test usage
System.out.println("Using name value of: " + this.name);
this.accountRepository = accountRepository;
}
....
}
If you write Unit Tests you will be ever so happy you created the classes this without all the #Autowired. If you do need autowired components keep those local to the java config files.
This will call the method below in the BeanFactory. Note in the description how this is intended for your exact use case.
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
* overriding the specified default arguments (if any) in the bean definition.
* #param name the name of the bean to retrieve
* #param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. It is invalid to use a non-null args value in any other case.
* #return an instance of the bean
* #throws NoSuchBeanDefinitionException if there is no such bean definition
* #throws BeanDefinitionStoreException if arguments have been given but
* the affected bean isn't a prototype
* #throws BeansException if the bean could not be created
* #since 2.5
*/
Object getBean(String name, Object... args) throws BeansException;
You can achieve a similar effect just by using an inner class:
#Component
class ThingFactory {
private final SomeBean someBean;
ThingFactory(SomeBean someBean) {
this.someBean = someBean;
}
Thing getInstance(String name) {
return new Thing(name);
}
class Thing {
private final String name;
Thing(String name) {
this.name = name;
}
void foo() {
System.out.format("My name is %s and I can " +
"access bean from outer class %s", name, someBean);
}
}
}
If you need to create a qualified bean you can do it this way:
#Configuration
public class ThingConfiguration {
#Bean
#Scope(SCOPE_PROTOTYPE)
public Thing simpleThing(String name) {
return new Thing(name);
}
#Bean
#Scope(SCOPE_PROTOTYPE)
public Thing specialThing(String name) {
Thing thing = new Thing(name);
// some special configuration
return thing;
}
}
// Usage
#Autowired
private ApplicationContext context;
AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
((DefaultListableBeanFactory) beanFactory).getBean("specialThing", Thing.class, "name");
Late answer with a slightly different approach.
That is a follow up of this recent question that refers this question itself.
Yes, as that was said you can declare the prototype bean that accepts a parameter in a #Configuration class that allows to create a new bean at each injection.
That will make this #Configuration class a factory and to not give this factory too much responsibilities, this should not include other beans.
#Configuration
public class ServiceFactory {
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Thing thing(String name) {
return new Thing(name);
}
}
But you can also inject that configuration bean to create Things :
#Autowired
private ServiceFactory serviceFactory;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = serviceFactory.thing(name); // create a new bean at each invocation
// ...
}
It is both type-safe and concise.
Nice solutions until now. But I want to post yet another alternative.
Spring has the #Lookup annotation. From javadoc:
An annotation that indicates 'lookup' methods, to be overridden by the
container to redirect them back to the BeanFactory for a getBean call.
you can declare your Thing as prototype bean:
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Thing {
#Autowired
private SomeComponent someComponent;
#Autowired
private AnotherComponent anotherComponent;
public Thing(String name) {
this.name = name;
}
}
then you can create instances by creating a method like createThing below in any other bean:
#Controller
public class MyController {
#Autowired
private ApplicationContext appCtx;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = createThing(name);
//System.out.println(thing.getName()); //prints name
}
//or public. And can be put in any #Component (including #Configuration)
#Lookup
protected Thing createThing(String name) {
throw new UnsupportedOperationException("Method implemented by Spring.");
}
}
in your beans xml file use the attribute scope="prototype"

Categories