88

I was wondering how I could implement the simple factory pattern with Spring 3 annotations. I saw in the documentation that you can create beans that call the factory class and run a factory method. I was wondering if this was possible using annotations only.

I have a controller that currently calls

MyService myService = myServiceFactory.getMyService(test);
result = myService.checkStatus();

MyService is an interface with one method called checkStatus().

My factory class looks like this:

@Component
public class MyServiceFactory {

    public static MyService getMyService(String service) {
        MyService myService;
        
        service = service.toLowerCase();
        
        if (service.equals("one")) {
            myService = new MyServiceOne();
        } else if (service.equals("two")) {
            myService = new MyServiceTwo();
        } else if (service.equals("three")) {
            myService = new MyServiceThree();
        } else {
            myService = new MyServiceDefault();
        }
        
        return myService;
    }
}

MyServiceOne class looks like this :

@Autowired
private LocationService locationService;

public boolean checkStatus() {
      //do stuff
}

When I run this code the locationService variable is always null. I believe this is because I am creating the objects myself inside the factory and autowiring is not taking place. Is there a way to add annotations to make this work correctly?

Thanks

ACV
  • 9,964
  • 5
  • 76
  • 81
blong824
  • 3,920
  • 14
  • 54
  • 75
  • came it late, but I think this info might help other. You can use [ServiceLocatorFactoryBean](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html) of in Spring. It's really neat in dealing with factory pattern. – Truong Nguyen Oct 21 '16 at 19:55
  • 2
    Have you found the right answer? So many wrong answers here – ACV Dec 17 '19 at 13:16

15 Answers15

145

The following worked for me:

The interface consist of you logic methods plus additional identity method:

public interface MyService {
    String getType();
    void checkStatus();
}

Some implementations:

@Component
public class MyServiceOne implements MyService {
    @Override
    public String getType() {
        return "one";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

@Component
public class MyServiceTwo implements MyService {
    @Override
    public String getType() {
        return "two";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

@Component
public class MyServiceThree implements MyService {
    @Override
    public String getType() {
        return "three";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

And the factory itself as following:

@Service
public class MyServiceFactory {

    @Autowired
    private List<MyService> services;

    private static final Map<String, MyService> myServiceCache = new HashMap<>();

    @PostConstruct
    public void initMyServiceCache() {
        for(MyService service : services) {
            myServiceCache.put(service.getType(), service);
        }
    }

    public static MyService getService(String type) {
        MyService service = myServiceCache.get(type);
        if(service == null) throw new RuntimeException("Unknown service type: " + type);
        return service;
    }
}

I've found such implementation easier, cleaner and much more extensible. Adding new MyService is as easy as creating another spring bean implementing same interface without making any changes in other places.

DruidKuma
  • 2,352
  • 2
  • 17
  • 20
  • 2
    Where is the "private List services " initialized from. It needs to have the beans created and managed by Spring and not the manually created beans. – sunil bhardwaj Oct 12 '19 at 19:36
  • @sunilbhardwaj Spring will create and manage beans on its own if you are using at least Spring 2 for enabling annotation based processing, and put corresponding annotations on top of your needed classes. Here Component and Service annotations will tell Spring to create bean instances of the corresponding classes – DruidKuma Oct 16 '19 at 15:30
  • @DruidKuma Great implementation but I think that service is singleton by default so maybe you can remove the static parts.. The consumer can use your factory with Autowired as a standard service... – Yaron Apr 13 '20 at 17:19
  • For magic is not happening , to collect all services which implement interface are not wired: For me it throws exception for : No qualifying bean of type 'java.util.List' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} – Slok Mar 25 '21 at 15:41
  • This does not work until you replace @ Component with @ Service. Rest is fine. – Manish Bansal Sep 29 '21 at 14:42
47

You are right, by creating object manually you are not letting Spring to perform autowiring. Consider managing your services by Spring as well:

@Component
public class MyServiceFactory {

    @Autowired
    private MyServiceOne myServiceOne;

    @Autowired
    private MyServiceTwo myServiceTwo;

    @Autowired
    private MyServiceThree myServiceThree;

    @Autowired
    private MyServiceDefault myServiceDefault;

    public static MyService getMyService(String service) {
        service = service.toLowerCase();

        if (service.equals("one")) {
            return myServiceOne;
        } else if (service.equals("two")) {
            return myServiceTwo;
        } else if (service.equals("three")) {
            return myServiceThree;
        } else {
            return myServiceDefault;
        }
    }
}

But I would consider the overall design to be rather poor. Wouldn't it better to have one general MyService implementation and pass one/two/three string as extra parameter to checkStatus()? What do you want to achieve?

@Component
public class MyServiceAdapter implements MyService {

    @Autowired
    private MyServiceOne myServiceOne;

    @Autowired
    private MyServiceTwo myServiceTwo;

    @Autowired
    private MyServiceThree myServiceThree;

    @Autowired
    private MyServiceDefault myServiceDefault;

    public boolean checkStatus(String service) {
        service = service.toLowerCase();

        if (service.equals("one")) {
            return myServiceOne.checkStatus();
        } else if (service.equals("two")) {
            return myServiceTwo.checkStatus();
        } else if (service.equals("three")) {
            return myServiceThree.checkStatus();
        } else {
            return myServiceDefault.checkStatus();
        }
    }
}

This is still poorly designed because adding new MyService implementation requires MyServiceAdapter modification as well (SRP violation). But this is actually a good starting point (hint: map and Strategy pattern).

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 1
    if you're going to do that, why don't you cut out the middleman and just autowire the different service types directly into the bean who would be calling the factory? – stevevls Jun 17 '11 at 19:40
  • 3
    Good question. Maybe because we want to decouple the client from different implementations? Have a look at my edit I did in the meantime. – Tomasz Nurkiewicz Jun 17 '11 at 19:43
  • You suggestion made the code work correctly. I originally had the code in one class but I thought it would be easier to add Services later that implement the MyService interface and I would only have to change code in the factory class. – blong824 Jun 17 '11 at 19:47
  • I will have to take a look at the Strategy Pattern. Thanks again – blong824 Jun 17 '11 at 19:53
  • 15
    The first option won't work because getMyService() is static. Therefore also myServiceOne, myServiceTwo must be static as well. – luksmir May 29 '13 at 08:21
  • 1
    What? And if My service has 10 methods? Are you going to implement all of them in your "Adapter" (which is a misleading name) – ACV Dec 17 '19 at 13:15
  • You cloud have used the beanFactory instead of using the @Autowired for every injection. – Rushikesh Sabde Dec 21 '22 at 09:21
38

Following answer of DruidKuma

Litte refactor of his factory with autowired constructor:

@Service
public class MyServiceFactory {

    private static final Map<String, MyService> myServiceCache = new HashMap<>();

    @Autowired
    private MyServiceFactory(List<MyService> services) {
        for(MyService service : services) {
            myServiceCache.put(service.getType(), service);
        }
    }

    public static MyService getService(String type) {
        MyService service = myServiceCache.get(type);
        if(service == null) throw new RuntimeException("Unknown service type: " + type);
        return service;
    }
}
jumping_monkey
  • 5,941
  • 2
  • 43
  • 58
Pavel Černý
  • 481
  • 4
  • 3
  • 2
    Nice Pavel, to put the cherry on the cake, consider changing the constructor's accessibility to private, as the class does not need to be constructed/accessible from the outside, and Spring will still be able to do the auto-wiring by reflection. I tested it and it works. – jumping_monkey Jun 23 '20 at 13:34
  • 1
    @jumping_monkey When and where the 'List services' will be initialized? I don't see initialize of the variable 'List services' – Ferry Sanjaya Sep 28 '20 at 13:04
  • 1
    @FerrySanjaya, that's the beauty of `@Autowired`, it's magical. All classes that implements "MyService" will _automagically_ get wired inside the services `List`. Try it. – jumping_monkey Sep 29 '20 at 01:40
  • What about testing in this case? If the constructor will be private, how will you do unit testing? – Max Nov 02 '20 at 08:29
  • keeping private then we wouldn't able to write test case. so i did public – Mugeesh Husain Jun 22 '22 at 02:56
11

Why not add the interface FactoryBean to MyServiceFactory (to tell Spring that it's a factory), add a register(String service, MyService instance) then, have each of the services call:

@Autowired
MyServiceFactory serviceFactory;

@PostConstruct
public void postConstruct() {
    serviceFactory.register(myName, this);
}

This way, you can separate each service provider into modules if necessary, and Spring will automagically pick up any deployed and available service providers.

Augustas
  • 1,167
  • 21
  • 31
Jeff Wang
  • 1,837
  • 1
  • 15
  • 29
  • I like this solution, just to extend it a bit, instead of interface use abstract MyService that will Autowire the factory and will do the PostConstruct. so your services are clean. – Yoav A Feb 23 '13 at 11:40
  • 2
    Just a warning, I ran into a problem once when implementing similar code for spring service classes with methods annotated with the @Transactional annotation. Basically, when you register a class using the ` this` keyword, and said class is proxied, the class that is passed to your service factory is not proxied. See https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring – Peter Kirby Nov 07 '16 at 18:38
10

You can manually ask Spring to Autowire it.

Have your factory implement ApplicationContextAware. Then provide the following implementation in your factory:

@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}

and then do the following after creating your bean:

YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.

This will autowire your LocationService perfectly fine.

user26270
  • 6,904
  • 13
  • 62
  • 94
rogermenezes
  • 1,143
  • 8
  • 12
7

You could also declaratively define a bean of type ServiceLocatorFactoryBean that will act as a Factory class. it supported by Spring 3.

A FactoryBean implementation that takes an interface which must have one or more methods with the signatures (typically, MyService getService() or MyService getService(String id)) and creates a dynamic proxy which implements that interface

Here's an example of implementing the Factory pattern using Spring

One more clearly example

Karim M. Fadel
  • 338
  • 1
  • 8
kyiu
  • 1,926
  • 1
  • 24
  • 30
  • 1
    Another example for ServiceLocatorFactoryBean can be found here as well : http://www.jcombat.com/spring/factory-implementation-using-servicelocatorfactorybean-in-spring – Suketu Bhuta Sep 22 '15 at 21:16
6

Based on solution by Pavel Černý here we can make an universal typed implementation of this pattern. To to it, we need to introduce NamedService interface:

    public interface NamedService {
       String name();
    }

and add abstract class:

public abstract class AbstractFactory<T extends NamedService> {

    private final Map<String, T> map;

    protected AbstractFactory(List<T> list) {
        this.map = list
                .stream()
                .collect(Collectors.toMap(NamedService::name, Function.identity()));
    }

    /**
     * Factory method for getting an appropriate implementation of a service
     * @param name name of service impl.
     * @return concrete service impl.

     */
    public T getInstance(@NonNull final String name) {
        T t = map.get(name);
        if(t == null)
            throw new RuntimeException("Unknown service name: " + name);
        return t;
    }
}

Then we create a concrete factory of specific objects like MyService:

 public interface MyService extends NamedService {
           String name();
           void doJob();
 }

@Component
public class MyServiceFactory extends AbstractFactory<MyService> {

    @Autowired
    protected MyServiceFactory(List<MyService> list) {
        super(list);
    }
}

where List the list of implementations of MyService interface at compile time.

This approach works fine if you have multiple similar factories across app that produce objects by name (if producing objects by a name suffice you business logic of course). Here map works good with String as a key, and holds all the existing implementations of your services.

if you have different logic for producing objects, this additional logic can be moved to some another place and work in combination with these factories (that get objects by name).

Andreas Gelever
  • 1,736
  • 3
  • 19
  • 25
4

I have recently worked on similar requirement where in I wanted to use factory pattern but I was not okay with the if else logic which keeps on growing in future and violates the Single Responsibility Principle.

First step, create an interface and have a getType() method, in the given context it will return "one", "two" etc, otherwise it can be anything. This is the common solution most of the people suggested above.

public interface MyService {
    String getType();
    void checkStatus();
}

Some implementations:

@Component
public class MyServiceOne implements MyService {
    @Override
    public String getType() {
        return "one";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

@Component
public class MyServiceTwo implements MyService {
    @Override
    public String getType() {
        return "two";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

@Component
public class MyServiceThree implements MyService {
    @Override
    public String getType() {
        return "three";
    }

    @Override
    public void checkStatus() {
      // Your code
    }
}

And the factory itself as following:

@Service
public class MyServiceFactory {

    @Autowired
    private List<MyService> services;

    public static MyService getService(final String type) {
        return services
       .stream().filter(service -> type.equals(service.getType()))
       .findFirst()
       .orElseThrow(throw new RuntimeException("Unknown service type: " + type));
        
    }
}

This solution doesn't require extra Map for storing key value of the instances against the type. This solution is extensible without any further code change, as the factory has List auto wiring, so any future implementation of MyService would be easily work. And hence the Single Responsibility Principle is also ensured.

I have used streams() and predicates as I was using Java 8, for earlier version simple for loop would do the work.

Sanjay Bharwani
  • 3,317
  • 34
  • 31
3

You could instantiate "AnnotationConfigApplicationContext" by passing all your service classes as parameters.

@Component
public class MyServiceFactory {

    private ApplicationContext applicationContext;

    public MyServiceFactory() {
        applicationContext = new AnnotationConfigApplicationContext(
                MyServiceOne.class,
                MyServiceTwo.class,
                MyServiceThree.class,
                MyServiceDefault.class,
                LocationService.class 
        );
        /* I have added LocationService.class because this component is also autowired */
    }

    public MyService getMyService(String service) {

        if ("one".equalsIgnoreCase(service)) {
            return applicationContext.getBean(MyServiceOne.class);
        } 

        if ("two".equalsIgnoreCase(service)) {
            return applicationContext.getBean(MyServiceTwo.class);
        } 

        if ("three".equalsIgnoreCase(service)) {
            return applicationContext.getBean(MyServiceThree.class);
        } 

        return applicationContext.getBean(MyServiceDefault.class);
    }
}
OlivierTerrien
  • 2,451
  • 1
  • 19
  • 31
2

I suppose you to use org.springframework.beans.factory.config.ServiceLocatorFactoryBean. It will much simplify your code. Except MyServiceAdapter u can only create interface MyServiceAdapter with method MyService getMyService and with alies to register your classes

Code

bean id="printStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
        <property name="YourInterface" value="factory.MyServiceAdapter" />
    </bean>

    <alias name="myServiceOne" alias="one" />
    <alias name="myServiceTwo" alias="two" />
Mikhail
  • 2,690
  • 4
  • 28
  • 43
1

This is a variation of the above answers that creates fresh instances.

If the Service depends only on Spring managed beans.

public interface MyService {
 //Code
}

@Component("One")
@Scope("prototype")
public class MyServiceOne implements MyService {
 //Code
   public MyServiceOne(Dependency dep){
   ...
   }
}

@Component("Two")
@Scope("prototype")
public class MyServiceTwo implements MyService {
 //Code
}

public class Factory {
    Map<String,MyService> services;
    ApplicationContext context;
    Dependency dep; 

    public Factory(Map<String, MyService> components, ApplicationContext context, Dependency dep) {
     ...
    }

    MyService service(String type){
        return context.getBean(services.get(type).getClass());
    }
}

@Configuration
public class Config {
       @Bean
       Factory languageFactory(Map<String,Service> map, ApplicationContext context, Dependency dep){
        return new Factory(map,context,dep);
    }
}



If you want to include custom parameters in the factory method that are not all managed by Spring you can try one of the following sketched below

  1. ensure a Bean can be instantiated when first discovered by adding empty constructors
@Component("One")
@Scope("prototype")
public class MyServiceOne implements MyService {
 //Code
   public MyServiceOne(){
   ...
   }

   public MyServiceOne(Dependency dep){
   ...
   }

   public MyServiceOne(Dependency dep, Integer myFactoryValue){
   ...
   }
}

  1. Or you manually create them in the configuration to be discovered
\\ no longer available in autoscan
public class MyServiceOne implements MyService {
 //Code
   public MyServiceOne(){
   ...
   }

   public MyServiceOne(Dependency dep, Integer myFactoryValue){
   ...
   }
}

@Configuration
public class Config {
       @Bean("One")
       @Scope("prototype")      
       Service serviceOne(){
            // used only for dynamic discovery
            return new ServiceOne();
       }
       ...

       @Bean
       Factory languageFactory(Map<String,Service> map, ApplicationContext context, Dependency dep){
        return new Factory(map,context,dep);
    }
}

And both solutions allow you to define your factory method like this

public class Factory {
    ....

    MyService service(String type, Integer someParameter){
        // you provide the parameters for the constructor
        return context.getBean(services.get(type).getClass(),dep,someParameter);
    }
} 
Radu Ionescu
  • 3,462
  • 5
  • 24
  • 43
0

Try this:

public interface MyService {
 //Code
}

@Component("One")
public class MyServiceOne implements MyService {
 //Code
}

@Component("Two")
public class MyServiceTwo implements MyService {
 //Code
}
0

Following the answer from DruidKuma and jumping_monkey

You can also include optional and make your code a bit nicer and cleaner:

 public static MyService getService(String type) {
        return Optional.ofNullable(myServiceCache.get(type))
                .orElseThrow(() -> new RuntimeException("Unknown service type: " + type));
 }
dextertron_
  • 921
  • 1
  • 10
  • 19
0

Spring won't Autowire beans if there isn't a clear path as to which one it should use at build time. Since the factory doesn't change, you can Autowire your LocationService there and pass it down to your different services. This can get a bit cumbersome if your classes have more than one dependency e.g. services, repos, etc.

If you don't intend on having a lot of dependencies for your "MyService" classes you can do this:

@Component
public class MyServiceFactory(){

    @Autowired
    LocationService locationService;
    
    public static MyService getMyService(String service){
        service = service.toLowerCase();
        switch(service){
            case "one":
                return new MyServiceOne(locationService);
            case "two":
                return new MyServiceTwo(locationService);
            case "three":
                return new MyServiceThree(locationService);
            default:
                return new MyServiceDefault(locationService);
        }
    }
}

Your MyServiceOne class:

@Service
public class MyServiceOne implements MyService{

    public LocationService locationService;

    public MyServiceOne(LocationService service){
        locationService = service;
    }

    @Override
    public checkStatus(){
        // your code
    }
}

MyService interface

interface MyService{
    boolean checkStatus();
}
jwsinner
  • 358
  • 3
  • 9
-1
public interface MyService {
    public void save(); 
//Code
}

@Component("One")
public class MyServiceOne implements MyService {
 //Code
    public void save(){
      System.out.println("one");
    }
}

@Component("Two")
public class MyServiceTwo implements MyService {
 //Code
    public void save(){
      System.out.println("two");
    }
}

public class FatoryClass{
    @Autowired
   //@Qualifier("One") //This is your default child class use qualifier or just my sample
    MyService One;
    public MyService setMyservice(int typeId){
      switch(typeId){
          case 1:
            One = new MyServiceTwo();
            break;
          default:
            System.out.println("Default child instance");
      }
      return One;
    }
}

@Service
public class serviceComponent{
    @Autowired
    FatoryClass facto;

    public void setFactoryMethod(int typeId){
        facto.setMyService(typeId);
        facto.save();
    }
}
Qazi
  • 1