0

I have a parent class that is not managed by Spring which has multiple @Component's inherited from it:

public class Parent {

  public void commonMethod(SomeObj obj) {
    //...
  }
}

@Component
public class ChildA extends Parent {

  public MyObj doSomething(/*...*/) {
    // Some external call
    commonMethod(obj);
  }
}

@Component
public class ChildB extends Parent {

  public MyObj doSomething(/*...*/) {
    // Some external call
    commonMethod(obj);
  }
}

Now I need to call a Spring managed @Service from the Parent class. Of course, since Parent is not a Spring-managed bean, I was thinking to do something like:

@Service
public class SomeServiceImpl implements SomeService {

  public void serviceWork(MyObj obj) {
    // Some static method call
  }
}

public class Parent {

  private SomeService someService;

  public Parent(SomeService someService) { this.someService = someService; }

  public void commonMethod(MyObj obj) {
    someService.serviceWork(obj);
    //...
  }
}

@Component
public class ChildA extends Parent {

  public ChildA(@Autowired SomeService someService) { super(someService); }
  
  public MyObj doSomething(/*...*/) {
    // Some external call
    commonMethod(obj);
  }
}

@Component
public class ChildB extends Parent {

  public ChildA(@Autowired SomeService someService) { super(someService); }

  public MyObj doSomething(/*...*/) {
    // Some external call
    commonMethod(obj);
  }
}

My question is, is this thread safe? Second, is there a better design for this since now I have to @Autowired SomeService and pass it to Parent's constructor for every child class.

Can I make the Parent class a Spring-managed class? Would that cause any issues since now it becomes a singleton class that's being shared among all the children?

NuCradle
  • 665
  • 1
  • 9
  • 31
  • Why not just have Parent be a @Component, have SomeService autowired there, and have the Child classes directly use the parent's service field? – Simon Oct 08 '20 at 14:27
  • That's my question, can both the child and parent be `@Component`'s? How does one do this with annotations (Spring 5.2+)? Also, I don't need to use the parent's service field in child, but rather only in the parent as part of refactoring. I'm not changing the state of either Child, Parent or the other `@Service` class for that matter. Simply passing an object to their methods (stack per thread). – NuCradle Oct 08 '20 at 23:01

3 Answers3

0

You're confusing classes and instances of objects.

Using your schema above you would have 3 objects instantiated

1 instance of class SomeServiceImpl

1 instance of class ChildA (which happens to implement Parent)

1 instance of class ChildB (which happens to implement Parent)

This is because Spring will create these objects as they are defined as Components/Services. The 1 instance of SomeServiceImpl will be handed to both the instance of ChildA and the instance of ChildB.

Each instance of class ChildA and ChildB would have their own copy of all the data fields in their own class and all super classes. However because you've auto wired the SomeServiceImpl they are pointing at the same instance of SomeServiceImpl.

So if you goal is to call SomeServiceImpl from ChildA and ChildB and have them thread safe this won't work since they are both talking to the same instance.

The general strategy looks correct, you'd have to mark the functions on SomeServiceImpl as synchronised. Assuming it is that you want ChildA and ChildB to share the SomeServiceImpl.

Pete
  • 11
  • 1
  • And that's ok as far as both `ChildA` and `ChildB` using the same `SomeService` instance, since I'm just passing a newly created `MyObj` to `SomeService.doWork()`, and therefore, per each thread, this is going to on the thread stack, which makes it thread safe. – NuCradle Oct 08 '20 at 23:03
0

My question is, is this thread safe

no : Spring does not guarantee thread safety. look at Are Spring objects thread safe?

is there a better design for this since now I have to @Autowired SomeService and pass it to Parent's constructor for every child class.

public class Parent implements ApplicationContextAware {
 
private static ApplicationContext context;
public Parent() { this.someService = context.getBean(someService.class); }

But if parent need some bean maybe parent must be a bean to and let the appContext use DI

CLAIN Cyril
  • 123
  • 9
  • Since neither of these classes changing a state of `SomeService`'s implementation class, and simply calling a method, the call will be placed on a stack, per thread. Can I just annotate `Parent` as `@Component`, which would make it Spring managed, but a singleton. Would that cause an issue with child-parent where both are Spring managed "and" inherited? – NuCradle Oct 08 '20 at 23:06
0

First of all, please have your future questions more focused and do not ask 5 questions in one post.


How to Inject a Spring Service into a Non-Component Parent Class?

You cannot.

You cannot inject an object managed by Spring, into another object NOT managed by Spring.

Why? because, Spring has no idea what other objects in your program are, and hence, it will never have any reference(s) to them, unless they're also managed by Spring, i.e. they are beans.


My question is, is this thread safe?

There is nothing about Thread-safety here.


Second, is there a better design for this since now I have to @Autowired SomeService and pass it to Parent's constructor for every child class?

There are several ways of injection: Setter, Field, and Constructor. Look it up in Google;


Can I make the Parent class a Spring-managed class? Would that cause any issues since now it becomes a singleton class that's being shared among all the children?

Yes, you can. You can read Bean Definition Inheritance to see how.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
  • Thanks Giorgi. If I can just mark the `Parent` class as `@Component`, then that should resolve the issue (and I believe that's the only thing I need to be doing, other than `@Autowiring` `SomeService` in the `Parent` class. That said, I updated the solution above, and locally, it seems like I can deploy it just fine. Can you take a look at it and see what you think? – NuCradle Oct 08 '20 at 15:00