Search code examples
javaspringjavabeanslifetime

What is the right design pattern to get a prototype-bean from a component-bean?


I am just wondering what a good architecture design looks like.

  • Let's say we have a CarRepository which manages all beans of type Car in a car rental application.
  • Car beans are of type prototype
  • CarRepository bean is of type repository (singleton)
  • Now, the CarRepository is asked to create a new Car bean, e.g. when the rental company has bought a new car.

Of course, I could implement ApplicatioContextAware and use context.getBean("car"), but for me, it doesn't fit well to the idea of dependency injection. What is best-practice for injecting a shorter-lived bean into a singleton?

Update: Maybe I should add an example to make it more clear.

@Repository
public class CarRepository {
private List<Car> cars;

    public void registerNewCar(int id, String model) {
        // I don't want to access the Spring context via ApplicationContextAware here

        // Car tmp = (Car) context.getBean("car");
        // car.setId(id);
        // car.setModel(model);
        // cars.add(tmp);
    }
}

@Scope("prototype")
public class Car {
    private int id;
    private String model;

    // getter and setters
}

Solution

  • Spring offers a mechanism that handles injecting a shorter-lived bean in a longer-lived one. It's called a scoped proxy. How it works is that the singleton is injected with a proxy that will handle method calls by searching the shorter scope (like session or request) for a bean instance and delegating to that instance.

    You didn't specify, if you are using xml or annotations to configure your application or what version of Spring you are using. You can read more about configuring the scope proxy with xml in the reference guide. I'm going to give you an example how to configure it with annotations in a Spring 4-ish environment.

    For me the best way is to use the meta-annotations mechanism. It allows you to create your own annotations that will be later used by Spring to configure your app. For example:

    @Retention(RUNTIME)
    @Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.TARGET_CLASS)
    public @interface SessionScoped{
    }
    

    Such an annotation, when specified on a @Component (or @Service or any other specialization) class or a @Bean method in your Java config will cause that bean to be injected as a proxy. For example:

    @Configuration
    @EnableAspectJAutoProxy
    public class MyConfig{
    
        @SessionScoped
        @Bean
        public MyClass myBean(){
            // return your bean
        }
    }
    

    All that being said, your example really makes me think you should be working with entities (Car) and repositories. See Spring Data if you are designing the model layer and you want to store Cars data in your database etc.