Is possible to specify that all setter should be autowired with one annotation?
This is my class:
#Component
public class MyClass {
private static Bean1 bean1;
//...
private static BeanN beanN;
public static Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
MyClass.bean1 = bean1;
}
//...
public static BeanN getBeanN() {
return beanN;
}
#Autowired
public void setBeanN(BeanN beanN) {
MyClass.beanN = beanN;
}
}
No. There is no such built-in annotation. Also, Spring doesn't care that your method is to be interpreted as a bean mutator (a setter). Any method can be annotated with #Autowired and Spring will try to invoke it with the appropriate arguments.
Since the whole point of Spring is dependency injection, there's no reason for you to have static fields. Just inject the bean where you need it.
Related
I have a question about spring bean injection in service tasks of Flowable, why only this kind of injection with a static modifier worked, and what is the logic of it?
I must inject a spring bean in a Flowable java service task, and I tested some different kind of injection Field, constructor, and setter injection, eventually setter injection with static modifier worked for me like this :
public class GetCurrentUserDlg implements JavaDelegate {
private static PersonService personService;
#Autowired
public void setPersonService(PersonService personService) {
this.personService = personService;
}
#Override
public void execute(DelegateExecution execution) {
personService.getCurrentUser();
}
}
While I can not answer your question, the following works fine for me:
public class SomeDelegate implements JavaDelegate {
#Autowired
private SomeBean bean;
#Override
public void execute(DelegateExecution execution) {
System.out.println(this.bean);
}
}
The class is then used in the process via flowable:class="packages.SomeDelegate"
But, be aware, that you may have problems with autowiring dependencies in the SomeBean bean. This dependencies are not injected when using the flowable:class attribute. In order for this to work you have to make the SomeDelegate a actual bean itself (e.g. via #Service) and use it in your process definition via flowable:delegateExpression="${someDelegate}"
Example:
#Service("someDelegate")
public class SomeDelegate implements JavaDelegate {
...
and
<serviceTask id="doSomething" name="Do Something" flowable:delegateExpression="${someDelegate}"/>
It should work like this:
#Component
public class GetCurrentUserDlg implements JavaDelegate {
#Autowired
private PersonService personService;
#Override
public void execute(DelegateExecution execution) {
personService.getCurrentUser();
}
}
#Component
public class PersonService {
// its methods
}
public class SomeClass {
private static final int num = 432;
#Bean
public int getNum(){
return num;
}
}
or would the method signature need to actually have the static keyword ?
I am not entirely sure about what you mean about a static Bean, Beans are instances in runtime.
If you mean Singleton, meaning that the bean would be created on application start and destroyed in application end.
Then by default, every Bean is #Bean(scope=DefaultScopes.SINGLETON), if you want a bean to be created every new usage of it, you can define it as #Bean(scope=DefaultScopes.PROTOTYPE)
Take a look at the doc: https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
My understanding of a static bean would be different. As an example, take a look at the EventPublisherHolder class from Eclipse Hawkbit:
public final class EventPublisherHolder {
private static final EventPublisherHolder SINGLETON = new EventPublisherHolder();
#Autowired
private ApplicationEventPublisher eventPublisher;
public static EventPublisherHolder getInstance() {
return SINGLETON;
}
public ApplicationEventPublisher getEventPublisher() {
return eventPublisher;
}
...
}
The way the ApplicationEventPublisher is injected into the EventPublisherHolder is through Spring magic
#Bean
EventPublisherHolder eventBusHolder() {
return EventPublisherHolder.getInstance();
}
The EventPublisherHolder class makes it easier to get the ApplicationEventPublisher statically.
As an example, take a look at the way this class is intended to be used:
From JpaAction class:
#Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new ActionCreatedEvent(...));
}
In this sense, you can consider the EventPublisherHolder bean to be a static bean.
When I use spring framework, I find something that should be extract, for example, the service component (or member variable that is autowired).
Code show as below:
abstract class Payment {
PaymentService paymentService;
void setPaymentService(OrderPaymentService paymentService) {
this.paymentService = paymentService;
}
}
#Component
public class CancelPayment extends Payment{
private OtherService2 otherSerivce2;
#Autowired
#Override
public void setPaymentService(PaymentService paymentService) {
super.setPaymentService(paymentService);
}
#Autowired
public CancelPayment(OtherService2 s2) {
this.otherSerivce2 = s2;
}
}
#Component
public class CreatePayment extends Payment{
private OtherService1 otherSerivce1;
#Autowired
#Override
public void setPaymentService(PaymentService paymentService) {
super.setPaymentService(paymentService);
}
#Autowired
public CreatePayment (OtherService1 s1) {
this.otherSerivce1 = s1;
}
}
As you can see, I use setter injection in each child class. Is this a better practice than autowire their parent's member variable?
Here are DI guidelines by Spring team:
A general guideline, which is recommended by Spring (see the sections on Constructor-based DI or Setter-based DI) is the following:
For mandatory dependencies or when aiming for immutability, use
constructor injection
For optional or changeable dependencies, use setter injection
Avoid field injection in most cases
Now if you are sure you will use PaymentService I would suggest you to use constructor injection in your abstract class like this so object won't instantiate without dependency, also making it more immutable, clearer and thread safe:
abstract class Payment {
PaymentService paymentService;
public Payment(OrderPaymentService paymentService) {
this.paymentService = paymentService;
}
}
Then you can simply call super on your extended classes like this:
#Component
public class CreatePayment extends Payment{
private OtherService1 otherSerivce1;
#Autowired
public CreatePayment(PaymentService paymentService) {
super(paymentService);
}
}
This simply allows you to inject parent class using constructor (if paymentService is mandatory).
I need to create multiple instances of a spring bean (let's call it MainPrototypeBean), which I can do with the prototype scope. It depends on some other beans, and I want to create new instances of them each time the main bean is created. However, there is a shared dependency between some of the beans, let's call it SharedPrototypeBean. How do I inject the same instance of SharedPrototypeBean in each of the dependent beans, while also creating a new instance for each MainPrototypeBean?
I'm looking into implementing a custom scope, but I'm hoping to find a cleaner way. Making any of the beans singletons is not an option, as they need to be isolated between different instances of MainPrototypeBean.
Here's an example of what I'm trying to do:
#SpringBootApplication
public class DIDemo {
public static void main(String[]args){
ConfigurableApplicationContext context = SpringApplication.run(DIDemo.class, args);
context.getBean(MainPrototypeBean.class);
}
#Component #Scope("prototype") static class SharedPrototypeBean {}
#Component #Scope("prototype") static class FirstPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class SecondPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class MainPrototypeBean {
#Autowired FirstPrototypeBean first;
#Autowired SecondPrototypeBean second;
}
}
And the output of executing it is:
FirstPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#1b84f475
SecondPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#539d019
You can use the FactoryBean for complex construction logic. Implement its abstract subclass AbstractFactoryBean for creating a MainPrototypeBean, and inject all three dependent beans into it. You can then wire them together in the createInstance method.
The FactoryBean implementation:
public class MainFactoryBean extends AbstractFactoryBean<MainPrototypeBean> implements FactoryBean<MainPrototypeBean> {
private FirstPrototypeBean firstPrototype;
private SecondPrototypeBean secondPrototpye;
private SharedPrototypeBean sharedPrototype;
public MainFactoryBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
this.firstPrototype = firstPrototype;
this.secondPrototpye = secondPrototype;
this.sharedPrototype = sharedPrototype;
}
#Override
protected MainPrototypeBean createInstance() throws Exception {
MainPrototypeBean mainPrototype = new MainPrototypeBean();
firstPrototype.setSharedPrototypeBean(sharedPrototype);
secondPrototpye.setSharedPrototypeBean(sharedPrototype);
mainPrototype.first = firstPrototype;
mainPrototype.second = secondPrototpye;
//call post construct methods on first and second prototype beans manually
firstPrototype.init();
secondPrototpye.init();
return mainPrototype;
}
#Override
public Class<?> getObjectType() {
return MainPrototypeBean.class;
}
}
Note: sharedPrototype is injected after the post-construct phase in the lifecycle of the first and second prototype. So, if you have post-construction logic in these beans that require the sharedPrototype, you need to manually call the init-method when creating the MainPrototypeBean.
Your annotation - configuration changes as as a consequence. The sharedPrototype attributes are no longer autowired (they are set inside FactoryBean), and MainPrototypeBean is not annotated anymore. Instead you need to create the MainFactoryBean.
#Configuration
public class JavaConfig {
//method name is the name refers to MainPrototypeBean, not to the factory
#Bean
#Scope("prototype")
public MainFactoryBean mainPrototypeBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
return new MainFactoryBean(firstPrototype, secondPrototype, sharedPrototype);
}
//Annotations are not needed anymore
static class MainPrototypeBean {
FirstPrototypeBean first;
SecondPrototypeBean second;
}
#Component
#Scope("prototype")
static class SharedPrototypeBean {
}
#Component
#Scope("prototype")
static class FirstPrototypeBean {
private SharedPrototypeBean shared;
//no autowiring required
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {//reference to shared will be null in post construction phase
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component
#Scope("prototype")
static class SecondPrototypeBean {
private SharedPrototypeBean shared;
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
}
After reading the comments and the other answer, I realized that the design is indeed too complex. I made SharedPrototypeBean, FirstPrototypeBean and SecondPrototypeBean regular POJOs, not managed by Spring. I then create all of the objects in a #Bean annotated method.
#Bean
public MainPrototypeBean mainPrototypeBean() {
Shared shared = new Shared();
First first = new First(shared);
Second second = new Second(shared);
return new MainPrototypeBean(first, second);
}
I just start a CDI project. In this project a Beans2 is injected inside a Beans1.
But the Beans2 has a method that create a file. This method instantiates the File Object like this :
new File('myPathFile');
Because this instantiation is not managed by the CDI container, Bean2 is not injected into Beans1. I try to make a producer to inject a File into the Beans2 but do i need to do the same thing for all java base class that i will use?
Is there another solution to simply use class that do not need to be inject?
Bean1 :
#Dependant
public class Bean1 implements Serializable {
private #Inject #Bean2Producer Bean2 bean2;
public void someMethod() {
bean2.foo();
}
}
Bean2 :
#Dependant
public class Bean2 extends AbstractClass implements Serializable {
private #Inject #PathDir String pathDir;
public Bean2(String param1, boolean param2) {
super(param1, param2);
}
public void foo() {
File file = new File(pathDir);
}
}
pathDir Producer :
#ApplicationScoped
public class ProjectProducer implements Serializable {
#Produces
#PathDir
public String getPathDir() {
try {
return PropsUtils.geetProperties().getProperty(PATH_DIR);
} catch (Exception e) {
e.printStackTrace();
}
}
}
PathDir annotation :
#Qualifier
#Target({FIELD, METHOD, PARAMETER, CONSTRUCTOR, TYPE})
#Retention(RUNTIME)
#Documented
public #interface PathDir {}
In this example, PathDir is not Inject if new File is invoke in foo() method.
The reason why Bean2 is not injected into Bean1 is that there is no suitable constructor in Bean2 so that CDI can automatically create an instance of it. Beans need to have a constructor with no parameters or a constructor with all parameters injected, see more in the Java EE tutorial
This is simple the limitation of CDI. If you want to pass arguments to your bean, you need to supply a setter method(s) to pass arguments after bean2 is injected. Or, in case you need to have some values during object creation (because the abstract parent requires them), you need to inject all constructor arguments, like this (#Param1 and #param2 are qualifiers):
public Bean2(#Inject #Param1 String param1, #Inject #Param2 boolean param2) {
super(param1, param2);
}
Alternatively, you do not need to turn every object into a CDI bean, especially if you have requirements on constructor parameters. You may inject all dependencies manually after the bean is created. It means you do use #Inject in Bean2, but provide a setter method, inject into bean1 and set the value in postconstruct method of bean1, example follows:
Bean1 :
#Dependant
public class Bean1 implements Serializable {
private #Inject #PathDir String pathDir; // inject pathDir here instead of in Bean2
private Bean2 bean2; // without inject, created in init()
#PostConstruct
public void init() {
bean2 = new Bean2("param1", "param2");
bean2.setPathDir(pathDir); // set injected value manually
}
public void someMethod() {
bean2.foo(); // here bean2.pathDir should be already initialized via setPathDir in init() method above
}
}
Bean2 :
#Dependant
public class Bean2 extends AbstractClass implements Serializable {
private String pathDir;
public Bean2(String param1, boolean param2) {
super(param1, param2);
}
public void setPathDir(String pathDir) {
this.pathDir = pathDir;
}
public void foo() {
File file = new File(pathDir);
}
}
Or even better, merge setPathDir and Bean2 constructor - it will be clear that pathDir is a required dependency for Bean2.