1

Say I have this:

interface Something {
}

interface Fetcher {
}

class FetcherImpl implements Fetcher {
   private final Class<?> klass;

   public FetcherImpl(Class<?> klass) {
      this.klass = klass;
   }

   public Something fetch() {
      // Fetch an instance Something depending on the klass
   }
}

class ClassA {
   private final Fetcher fetcher;

   public ClassA(Fetcher fetcher) {
      this.fetcher = fetcher;
   }

   public void someMethod() {
      // ...
      Something something = fetcher.fetch();
      // ...
   }
}

class ClassB {
   private final Fetcher fetcher;

   public ClassB(Fetcher fetcher) {
      this.fetcher = fetcher;
   }

   public void someMethod() {
      // ...
      Something something = fetcher.fetch();
      // ...
   }
}

So, the idea is:

  • I have an data class, Something
  • I have a fetcher for that data class, Fetcher, that fetches it depending on the class. For example, it creates it depending on the contents of the file from the path /tmp/<class-name>.txt
  • There are a lot of classes that are using (their respective instance of) the fetcher, ClassA and ClassB in the above example

Some important points:

  • All clients of the fetcher are doing constructor injection, i.e. using a final field to store the fetcher. Thus bean post processors cannot be used
  • The set of classes using the fetcher is an open one
  • The creation of the fetcher instances should not be manually configured. I.e. when adding a class ClassC above that uses its own instance of the fetcher similar to ClassA and ClassB, there should not be additional Spring configuration for that particular class

Some real world examples of a similar pattern:

  • Loggers, e.g. instead of using

    final Logger logger = LoggerFactory.getLogger(this.getClass())
    

    the logger would be injected via the constructor

  • Cache implementations (i.e. get a cache for a specific class)
  • DAO objects (i.e. get a DAO for a particular bean, provided it can be generic enough)

Is this possible using Spring? If so, can you please provide some pointers or similar examples that I can read through? I am using Java configuration for Spring if that matters.

Cœur
  • 37,241
  • 25
  • 195
  • 267
levant pied
  • 3,886
  • 5
  • 37
  • 56
  • 1
    Why is no post bean processor a restriction? Is it only because you're making the field final? There is a way to do what you want using a post bean processor. All clients of the fetcher are doing constructor injection, i.e. using a final field to store the fetcher. Thus bean post processors cannot be used – Jazzepi Dec 26 '14 at 14:31
  • @Jazzepi Correct, because the field is final, so cannot be injected using a post processor. – levant pied Dec 26 '14 at 14:34
  • Seems simple enough to make the field non-final and solve your problem. – Jazzepi Dec 26 '14 at 14:42
  • @Jazzepi Agreed, it's simple. It also breaks good practices. http://stackoverflow.com/questions/137868/using-final-modifier-whenever-applicable-in-java. Obviously, if following good practices means it becomes complex, it's up for debate. Hence why I asked the question - I want to see if I can have my cake and eat it too. – levant pied Dec 26 '14 at 17:33
  • 1
    I am not getting the question. Are the ClassA, ClassB and ClassC spring beans? If yes, how can you avoid specific configuration for ClassC? If not, is it a spring question at all? Anyway could not the problem be solved by fetchers beans having distinctive names and referring/retrieving them using names (or some name-based strategy)? – Michal Dec 26 '14 at 20:16
  • @Michal ClassA/B/C/... are Spring beans. You can avoid the configuration by doing auto-configuration using a template. Say Class/A/B/C/... implemented WithFetcher interface with had setFetcher() method. You could create a BeanPostProcessor, only take into account WithFetcher descendants and process them by setting a separate Fetcher instance on each of these. There would be no need for configuration, as you could instantiate the fetcher based e.g. on class name. My problem is how to do this when constructing, as Fetcher is a final variable of ClassA/B/C/.... If unclear, let me know, I'll edit – levant pied Dec 26 '14 at 20:55
  • If I understand you well you use Spring Java Config to define the beans. That means you need to introduce new bean definition for ClassC, something like @Bean beanC() { return new ClassC(fetcher); }. For me the method beanC is the place to bind the ClassC with concrete fetcher. The fetcher is bean retrieved via name-based injection, qualifier, profile, via fetcherConfig.fetcherC(), whatever. You might then introduce some kind of strategy which would decide which ClassX gets which fetcher, just like the BeanPostProcssor. – Michal Dec 26 '14 at 21:12
  • I personally gave up on few Java-only rules when working with spring beans. Using constructor parameters to compose bigger part from smaller is one of such rules I do not follow. I usually do setter injection and put initialization code into @PostConstruct annotated init() method instead of using constructor. As the setter injection is possible without setter/getter method I also live happily without the final field, I just have to remember not to re-assign the field inside the bean itself, that is not so hard to follow. – Michal Dec 26 '14 at 21:16
  • Thanks @Michal - I'm actually doing a `@Component` + `@Autowired`, so it's all automatic. So I don't necessarily need to do any configuration - and the whole reason I'm asking the question is to avoid doing it. I get your points on doing a custom factory and not doing final, though. – levant pied Dec 26 '14 at 21:32

0 Answers0