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).
Related
I am new to spring framework. I have to use spring boot and have a rest controller as below :-
#RestController
public class StatisticsController {
private TransactionCache transactionCache;
public StatisticsController(TransactionCache transactionCache) {
this.transactionCache = transactionCache;
}
#PostMapping("/tick")
public ResponseEntity<Object> addInstrumentTransaction(#Valid #RequestBody InstrumentTransaction instrumentTransaction) {
transactionCache.addTransaction(instrumentTransaction);
return new ResponseEntity<>(HttpStatus.CREATED);
}
and I have a class which needs to be singleton :-
#Component
public class TransactionStatisticsCacheImpl implements TransactionCache {
private static TransactionStatisticsCacheImpl instance;
public static TransactionStatisticsCacheImpl getInstance(){
if(Objects.isNull(instance)){
synchronized (TransactionStatisticsCacheImpl.class) {
if(Objects.isNull(instance)){
instance = new TransactionStatisticsCacheImpl();
}
}
}
return instance;
}
private TransactionStatisticsCacheImpl() {}
I want to know the correct way to call this singleton class in my rest controller. I know that by default the scope of a bean in spring is singleton. Is this the correct way to call the singleton class in rest controller?
#RestController
public class StatisticsController {
private TransactionCache transactionCache;
public StatisticsController(TransactionCache transactionCache) {
this.transactionCache = transactionCache;
}
#PostMapping("/tick")
public ResponseEntity<Object> addInstrumentTransaction(#Valid #RequestBody InstrumentTransaction instrumentTransaction) {
transactionCache.addTransaction(instrumentTransaction);
return new ResponseEntity<>(HttpStatus.CREATED);
}
or
We need to call it using the getInstance() method? Also do we need to explicitly have the getInstance method in the TransactionStatisticsCacheImpl class?
One of the major advantages of container injection is that you can get the benefits of singleton semantics without all the serious problems of "hard" singletons (such as difficulty testing). Get rid of the getInstance manual business and let Spring take care of ensuring that a single instance is created and used for the context.
Just for clarification: By default, the spring IOC container will create only one instance per bean definition, unless if you specified otherwise using the #Scope stereotype. But if you create an instance using getInstance() the bean pre-processors and post-processors will not work correctly on that bean definition. And also you can use the #Autowired stereotype to inject a bean definition as needed and if you have different implementations for the same definition you can use the #Qualifier stereotype to specify the implementation that you need to inject, alternatively, you can use the constructor injection to inject your bean definition as needed without auto wiring as mentioned here Spring #Autowire on Properties vs Constructor
I would stick to the answers above. However, if you want to preserve further instantiation of the class in your code (or you want to keep your specific implementation of singleton), you can do it with getInstance().
Firstly, get rid of #Component annotation in your class:
// #Component
public class TransactionStatisticsCacheImpl implements TransactionCache {
private static TransactionStatisticsCacheImpl instance;
public static TransactionStatisticsCacheImpl getInstance(){
if(Objects.isNull(instance)){
synchronized (TransactionStatisticsCacheImpl.class) {
if(Objects.isNull(instance)){
instance = new TransactionStatisticsCacheImpl();
}
}
}
return instance;
}
private TransactionStatisticsCacheImpl() {}
}
Then, you may instantiate your singleton #Bean by defining #Configuration class - this way your bean would get managed by spring container.
#Configuration
public class SingletonConfiguration {
#Bean
public TransactionCache transactionCache() {
return TransactionCacheImpl.getInstance();
}
}
Eventually, you can have your singleton injected in your RestController using #Autowired.
#RestController
public class StatisticsController {
private TransactionCache transactionCache;
#Autowired
public StatisticsController(TransactionCache transactionCache) {
this.transactionCache = transactionCache;
}
#PostMapping("/tick")
public ResponseEntity<Object> addInstrumentTransaction(#Valid #RequestBody InstrumentTransaction instrumentTransaction) {
transactionCache.addTransaction(instrumentTransaction);
return new ResponseEntity<>(HttpStatus.CREATED);
}
}
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
}
Having the following class structure:
public abstract class A {
String someProperty = "property"
public abstract void doSomething();
}
#Service
public class Aa extends A {
#Override
public abstract void doSomething() {
System.out.println("I did");
}
}
#Service
public class Ab extends A {
#Override
public abstract void doSomething() {
System.out.println("I did something else");
}
}
I need a way to tell Spring which A concrete class to Autowire in my Foo service, based on a property in a properties file.
#Service
public class Foo {
#Autowire
private A assignMeAConcreteClass;
}
And in my properties file I have this:
should-Aa-be-used: {true, false}
Remove the #Service annotation, instead write a #Bean-annotated method in a configuration class that reads the properties, and returns the appropriate A instance.
Not a new way but in your case I think that a possible suitable way would be to use
FactoryBean in the class that wants to inject the bean conditionally.
The idea is simple : you implement FactoryBean by parameterizing it with the interface of the bean that you want to inject and override getObject() to inject the wished implementation :
public class FactoryBeanA implements FactoryBean<A> {
#Autowired
private ApplicationContext applicationContext;
#Value("${should-Aa-be-used}")
private boolean shouldBeUsed;
#Override
public A getObject() {
if (shouldBeUsed) {
return applicationContext.getBean(Aa.class));
return applicationContext.getBean(Ab.class));
}
}
But FactoryBean instances are not classic beans. You have to configure it specifically.
You could configure it in a Spring Java configuration in this way :
#Configuration
public class FactoryBeanAConfiguration{
#Bean(name = "factoryBeanA")
public FactoryBeanA factoryBeanA() {
return new FactoryBeanA();
}
#Bean
public beanA() throws Exception {
return factoryBeanA().getObject();
}
}
I have a class with 2 static nested classes that do the same operation on 2 different generic types.
I exposed the 2 classes as beans and added #Autowired for the constructors as I usually do.
Here is the basic setup
abstract class <T> Parent implements MyInterface<T> {
private final Service service;
Parent(Service service){ this.service = service; }
#Override public final void doInterfaceThing(T thing){
T correctedT = map(thing);
service.doTheThing(correctedT);
}
protected abstract T map(T t);
#Service
public static class ImplA extends Parent<A> {
#Autowired ImplA (Service service){ super(service); }
A map(A a){ //map a }
}
#Service
public static class ImplB extends Parent<B> {
#Autowired ImplB (Service service){ super(service); }
B map(B b){ //map b }
}
}
And in another class I have
#Service
public class Doer {
private final List<MyInterface<A>> aImpls;
#Autowired public Doer(List<MyInterface<A>> aImpls){ this.aImpls = aImpls; }
public void doImportantThingWithA(A a){
aImpls.get(0).doInterfaceThing(a);
}
}
When I run the app, everything appears to be injected correctly and when I put a breakpoint in the ImplA and ImplB constructors, I have a not-null value for "service". I also have an ImplA bean in the aImpls list in Doer.
When I call doImportantThingWithA(a) however, "service" is null inside ImplA and I obviously die.
I'm not sure how this is possible because:
I see a nonnull value in my constructors for service which is a final field.
If spring is injecting ImplA and ImplB into another class, it should already have either injected a Service into ImplA or ImplB, or thrown an exception on bean initialization. I have nothing set to lazily load and all bean dependencies are required.
The reason for the nested classes is because the only thing that changes between the 2 implementations is the map() function. Trying to avoid extra classes for 1 line of varying code.
More info:
When I add a breakpoint in Parent.doInterfaceThing(), if I add a watch on "service" I get null as the value. If I add a getService() method, and then call getService() instead of referring directly to this.service, I get the correct bean for service. I don't know the implications of this but something seems weird with the proxying.
It looks like what is causing the issue is Parent.doInterfaceThing();
If I remove final from the method signature, "service" field is correctly populated and the code works as expected.
I don't understand at all why changing a method signature affects the injected value of final fields in my class... but it works now.
What I meant with my "use mappers" comment was something like this:
class MyInterfaceImpl implements MyInterface {
#Autowired
private final Service service;
#Override public final <T> void doInterfaceThing(T thing, UnaryOperator<T> mapper){
T correctedT = mapper.apply(thing);
service.doTheThing(correctedT);
}
// new interface to allow autowiring despite type erasure
public interface MapperA extends UnaryOperator<A> {
public A map(A toMap);
default A apply(A a){ map(a); }
}
#Component
static class AMapper implements MapperA {
public A map(A a) { // ... }
}
public interface MapperB extends UnaryOperator<B> {
public B map(B toMap);
default B apply(B b){ map(b); }
}
#Component
static class BMapper implements MapperB {
public B map(B a) { // ... }
}
}
This does have a few more lines than the original, but not much; however, you do have a better Separation of Concern. I do wonder how autowiring works in your code with the generics, it does look as if that might cause problems.
Your client would look like this:
#Service
public class Doer {
private final List<MapperA> aMappers;
private final MyInterface myInterface;
#Autowired public Doer(MyInterface if, List<MapperA> mappers){
this.myInterface = if;
this.aImpls = mappers; }
public void doImportantThingWithA(A a){
aMappers.stream().map(m -> m.map(a)).forEach(myInterface::doInterfaceThing);
}
}
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.