Question

We have an interface:

public interface NotifyService {

  public void send();

And a class that implements it

public class EmailNotifyService implements NotifyService {

  private EmailBuilder _builder;

  @Autowired
  PersonRepository _personRepository;

  ... other Autowired Repositories ...

  public EmailNotifyService(EmailBuilder builder) {
   this._builder = builder;
  }

  public void send() {
    // send mail using _builder.getRecipient().getEmailAddress(), etc.
  }

We used to instantiate EmailNotifyService with a builder:

public class EmailBuilder {

  private Person _recipient;

  private EmailType _type;

  private Event _event;

  public EmailNotifyService build() {
    return new EmailNotifyService(this);
  }

  public EmailBuilder recipient(Person recipient) {
    this._recipient = recipient;
    return this;
  }

... and so on. But now, instead of using build() to create a new EmailNotifyService, we are trying to use Autowire with Spring instead. The problem is that everywhere else in our app, we are Autowiring interfaces, not classes. And from what I've read it's a good idea in general. In fact, I've tried rewriting the NotifyService to be an Abstract class, and then have EmailNotifyService just extend it. But Spring isn't Autowiring it correctly, it doesn't create a Proxy like it does for interfaces, and all of my Autowired fields are null.

So it would seem we're stuck with Autowiring the NotifyService interface. Fine. What I can't get my head around is - how can I get the data I used to assign with the builder -- the Person, EmailType and Event -- into a Spring Autowired interface?

I suppose I could change the interface definition to have a setPerson(), setEmailType(), etc., but apart from being really ugly, it defeats the purpose of using an interface in the first place. A different NotifyService (WebServiceNotifyService or RestNotifyService for example) night not have need for that info.

Is there any elegant, best-practice way to do this?

Thanks.

EDIT

I am using annotations, very little xml. And I am also using transaction management, which might explain why the abstract class isn't properly autowired? This is the only pertitnent info I have in xml:

<context:annotation-config />  
<context:component-scan base-package="com.myco.myapp" />  
<tx:annotation-driven transaction-manager="transactionManager"/>  

What I mean when I say "autowiring isn't working correctly" is that when I try to autowire the abstract class, Spring doesn't seem to be creating a Proxy like it does for interfaces, and all the Autowired fields in my EmailNotifyService (PersonRepository, others ...) are null. When I use an interface, all the Autowired fields are wired correctly.

But my main problem is that I used to work explicitly with a concrete class, using a builder to create a new EmailNotifyService() directly, and pass it info -- Person, EmailType and Event. These are just normal beans. There are no setters/getters for them in EmailNotifyService but there are the EmailBuilder, which used to live inside EmailNotifyService.

But now I am using the NotifyService interface, which knows nothing about Person, EmailType or Event. But I need this info in order for EmailNotifyService to work.

So my question is, if I use Spring to Autowire my EmailNotifyService like this:

@Autowired
@Qualifier("email") // so Spring knows I want to use the EmailNotifyService implementation
NotifyService _notifyService

How can I set the Person, EmailType and Event data, since NotifyService knows nothing about them?

Currently we are using the mailer service within a web app but theoretically the mailer service should be able to work stand-alone. Regardless, I don't see how request scoped beans can help me here.

Was it helpful?

Solution

Robert what do you mean by not autowiring correctly? Are you getting any error?

Generally both interface and class auto-wiring works in Spring unless you have some autoproxy configured example @Transactional.

You do not need to have setPerson(), setEmailType(), etc. in your interface but have them autowired in the concrete class which requires them.

But seems Person is not a service but a bean which holds data and its specific to a request. If yours is a web application then look at request scope proxy to inject Person like bean.


So you are using transactions which is why class based injection is failing. Add proxy-target-class="true" to tx:annotation-driven.

Regarding your injection of Person and EmailType then you have to do that to the bean EmailNotifyService. In EmailNotifyService I do not see any Person or EmailType variables defined. Also read what I said about Person bean above.


Your design is not correct. You should not make EmailBuilder a bean and look to autowire to the EmailNotifyService. Instead in EmailNotifyService you should have a method send(EmailBuilder builder) where you pass the builder which you created somewhere dynamically.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top