Implement a simple factory pattern with Spring 3 annotations
Asked Answered
Y

15

92

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

Yuan answered 17/6, 2011 at 19:27 Comment(2)
came it late, but I think this info might help other. You can use ServiceLocatorFactoryBean of in Spring. It's really neat in dealing with factory pattern.Broussard
Have you found the right answer? So many wrong answers hereTrifocals
G
48

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).

Glim answered 17/6, 2011 at 19:35 Comment(7)
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?Delossantos
Good question. Maybe because we want to decouple the client from different implementations? Have a look at my edit I did in the meantime.Glim
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.Yuan
I will have to take a look at the Strategy Pattern. Thanks againYuan
The first option won't work because getMyService() is static. Therefore also myServiceOne, myServiceTwo must be static as well.Indite
What? And if My service has 10 methods? Are you going to implement all of them in your "Adapter" (which is a misleading name)Trifocals
You cloud have used the beanFactory instead of using the @Autowired for every injection.Marler
S
149

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.

Strontianite answered 7/9, 2016 at 4:58 Comment(6)
Where is the "private List<MyService> services " initialized from. It needs to have the beans created and managed by Spring and not the manually created beans.Upas
@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 classesStrontianite
@Strontianite 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...Transient
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<com.company.common.IManager>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}Tifanytiff
This does not work until you replace @ Component with @ Service. Rest is fine.Deputize
Thanks for the answer, could you please tell how we unit test this?Jerlenejermain
G
48

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).

Glim answered 17/6, 2011 at 19:35 Comment(7)
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?Delossantos
Good question. Maybe because we want to decouple the client from different implementations? Have a look at my edit I did in the meantime.Glim
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.Yuan
I will have to take a look at the Strategy Pattern. Thanks againYuan
The first option won't work because getMyService() is static. Therefore also myServiceOne, myServiceTwo must be static as well.Indite
What? And if My service has 10 methods? Are you going to implement all of them in your "Adapter" (which is a misleading name)Trifocals
You cloud have used the beanFactory instead of using the @Autowired for every injection.Marler
Z
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;
    }
}
Zechariah answered 8/3, 2019 at 9:33 Comment(5)
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.Reciprocation
@Reciprocation When and where the 'List<MyService> services' will be initialized? I don't see initialize of the variable 'List<MyService> services'Laevo
@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.Reciprocation
What about testing in this case? If the constructor will be private, how will you do unit testing?Josefajosefina
keeping private then we wouldn't able to write test case. so i did publicJawbreaker
C
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.

Clump answered 4/1, 2012 at 23:32 Comment(2)
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.Pantheon
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 spring.io/blog/2012/05/23/…Drongo
S
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.

Safety answered 28/2, 2013 at 20:21 Comment(0)
P
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

Pigheaded answered 9/4, 2013 at 9:58 Comment(1)
Another example for ServiceLocatorFactoryBean can be found here as well : jcombat.com/spring/…Mure
R
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).

Rhyme answered 9/7, 2019 at 8:43 Comment(0)
P
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.

Pironi answered 19/8, 2021 at 16:16 Comment(0)
C
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);
    }
}
Coltun answered 7/3, 2016 at 23:22 Comment(3)
How you can reference a applicationContext inside a static function ... ? it gives me an error, since it is not also staticAleshia
Oh yes, you are obviously right. A simple mistake. I just removed "static" from the method "getMyService"Coltun
How to follow above technique if I have parametrized constructors with diff arguments in diff implementation classes?Very
N
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" />
Nickolai answered 9/6, 2015 at 15:7 Comment(0)
I
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);
    }
} 
Inaccessible answered 30/9, 2021 at 15:36 Comment(0)
B
0

Try this:

public interface MyService {
 //Code
}

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

@Component("Two")
public class MyServiceTwo implements MyService {
 //Code
}
Bicapsular answered 1/5, 2019 at 6:2 Comment(0)
F
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));
 }
Function answered 27/8, 2020 at 10:42 Comment(0)
L
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();
}
Libna answered 22/10, 2021 at 20:23 Comment(0)
R
-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();
    }
}
Rondure answered 4/5, 2022 at 2:28 Comment(1)
Please don't post code-only answers. Explain why this code solves the problem at hand.Sarge

© 2022 - 2024 — McMap. All rights reserved.