I'm trying to find the most elegant and Spring way to create an (unlimited) of Spring managed beans.
One of the questions is why I'd need to do it. The most common use-case I have is when using Vaadin I have to create a list of layout depending on a collection fetched from database. Based on that I'll create a list of layouts that might require access to Spring services.
There are 2 methods that I'm using at the moment:
//Spring ApplicationContext
#Component
public class MainLayout {
#Autowired ApplicationContext ctx;
public void init() {
items.forEach(i -> this.add(ctx.getBean(ChildLayout.class, i));
}
}
//This works well if ChildLayout has autowired dependencies and no arguments
#Component
#Scope("prototype")
public class ChildLayout {
#Autowired ServiceA serviceA;
#Autowired ServiceB serviceB;
public ChildLayout(MyDTO item) {
//IDE Interpreter will complain that MyDTO is not a Spring Component (because it's not)
...
}
}
Then I figured out that I could create use a factory pattern and move to a Constructor Based bean injection as it's recommended.
#Component
public class ChildLayoutFactory {
private final ServiceA serviceA;
private final ServiceB serviceB;
public ChildLayoutFactory(ServiceA a, Service b) {...}
public ChildLayout create(MyDTO dto) {
return new ChildLayout(a, b, dto)
}
}
public class ChildLayout {
ServiceA serviceA;
ServiceB serviceB;
public ChildLayout(ServiceA a, ServiceB b, MyDTO item) {
this.serviceA = a;
this.serviceB = b;
...
}
}
#Component
public class MainLayout {
#Autowired ChildLayoutFactory childLayoutFactory; //Or constructor based
public void init() {
items.forEach(i -> this.add(childLayoutFactory.create(i));
}
}
Both methods work fine. The problem with the second is that I need an extra class and that if I have also some extra dependency that I need to create on the fly I'll need to pass the dependency on the constructors upstream.
Is there any way what I could combine the advantages of both? Is it recommended to do it as shown?
That is bit unclear what problem you are trying to solve: your first code snippet looks good, except the "modern way" is to create factory bean and write something like:
public class MainLayout {
#Autowired
private ObjectProvider<ChildLayout> layoutProvider;
public void init() {
items.forEach(i -> this.add(layoutProvider.getObject(i));
}
}
#Configuration
public class ChildLayoutConfiguration {
#Bean
#Scope("prototype")
public ChildLayout childLayout(MyDTO item) {
return new ChildLayout(item);
}
}
However, some guys advocate some controversial opinions like:
using #Autowired is code smell because you get coupled with spring framework
you "should prefer" to use constructor DI over other options
and the problem is those guys do not tell you the whole truth:
even if you are working hard to not get coupled with spring framework, the only way to prove that is to perform integration/manual testing against every DI/IoC framework
constructor DI assumes that framework must somehow resolve dependency graph, however it sometimes does and sometimes does not, practically that means if you even perform integration tests it means nothing: at any moment you PROD env may not start
So here actually the issue is how to inject prototype into a singleton bean (the class marked with #Component annotation in this case).
There are ways to implement this so that your code won't be coupled with spring framework.
Option 1
Use javax.inject.Provider<T>:
add to your pom.xml (or gradle build file) the following dependency:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
Its a small jar with only some interfaces and is not related to spring
Then you can create a singleton like this:
#Component
public class MainLayout {
private final ServiceA serviceA;
private final ServiceB serviceB;
private final Provider<ChildLayout> childLayoutProvider;
public MainLayout(ServiceA a, Service b, Provider<ChildLayout> childLayoutProvider) {...}
public void init(MyDTO dto) {
List<MyDTO> dtos = ...
for(MyDTO dto : dtos) {
var childLayout = provier.get();
childLayout.setDto(dto);
// child Layout is ready
}
}
}
While in some cases this method works well, one significant drawback is that you break encapsulation of the childLayout and have to set the dto with a setter.
So, you need something similar but in a way that can accept a parameter in constructor (unlike provider.get())
Spring allows this style by using java.util.Function as a bean.
Option 2
#Configuration
public class MyConfig {
#Bean
public Function<MyDTO, ChildLayout> beanFactory() {
return dto -> childLayout(dto);
}
#Bean
public MainLayout(ServiceA serviceA, ServiceB serviceB, Function<MyDTO,
ChildLayout> beanFactory) {
return new MainLayout(serviceA, ServiceB, beanFactory);
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ChildLayout childLayout(MyDTO dto
... /**possibly other parameters **/) {
return new ChildLayout(dto);
}
}
#AllArgsConstructor
public class MainLayout {
private final ServiceA serviceA;
private final ServiceB serviceB;
private final Function<MyDTO, ChildLayout> beanFactory;
public void init () {
List<MyDTO> dtos = ...
for(MyDTO dto : dtos) {
ChildLayout layout = beanFactory.apply(dto);
...
}
}
}
In general there are many ways to inject prototypes into singleton beans. You might want to read This Article for more details (it also describes the way I've written about here). In general these are my favorite ones because they're not coupled to spring in any case, so you can easily unit test your singleton bean.
Related
I was new to Springboot application using the #Autowired to perform the dependency injection. We can use #Autowired directly by initiate that class object for class that has none or default parameterless constructor. But what if a class has some parameter in its constructor, and I would like to initiate it during runtime conditionally and automatically, is it possible to do that?
For example
#Component
public class SomeContext {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Component
public class SomeBuilder {
private final SomeContext ctx;
#Autowired
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
public void run(SomeContext ctx) {
if (ctx != null) {
// I want someBuilder to be initiated here in someway with my input ctx
// but NOT doing through new with constructor like below
// someBuilder = new SomeBuilder(ctx);
someBuilder.test(); // ctx name: null, I would expect to see "ctx name: someUser", while ctx was injected into someBuilder in any possible way
}
}
}
#RestController
public class HelloWorldController
{
#Autowired
SomeService someService;
#RequestMapping("/")
public String hello() {
SomeContext someContext = new SomeContext();
someContext.setName("someUser");
someService.run(someContext);
return "Hello springboot";
}
}
I'm not sure I've got your question right, but from the code it looks like you really want to create a new instance of SomeBuilder every time you call the run method of SomeService.
If so, I think the first thing to understand is that in general the injection magic happens only if the class is managed by Spring by itself. Read, if spring creates the object of the class - it will inject stuff into it otherwise you're on your own here.
The next thing to understand is that, if you have a object of class SomeBuilder managed by spring and you want to inject SomeContext into it, this SomeContext instance has to be managed by spring as well.
Bottom line, spring can deal only with objects that it manages. These objects are stored in a 'global registry' of all the objects called ApplicationContext in spring.
Now Spring has a concept of prototype scope vs. singleton scope. By Default all the beans are singletons, however you can easily alter this behavior. This has two interesting consequences:
You Can create prototype objects being injected into the singleton upon each invocatino (of method run in your case, so the SomeBuilder can and I believe should be a prototype)
Prototype objects are not stored in the application contexts so the capabilities of injecting stuff in there during the runtime are rather limited
With all this in mind:
If you want to create SomeContext like you do in the controller, its not managed by spring, so you can't use Injection of spring as is into the builder.
The builder is a singleton, so if you inject it with a regular #Autowire into another singleton (SomeService in your case), you'll have to deal with the same instance of the builder object - think about concurrent access to the method run of SomeService and you'll understand that this solution is not really a good one.
So these are the "inaccuracies" in the presented solution.
Now, in terms of solution, you can:
Option 1
Don't manage builders in Spring, not everything should be managed by spring, in this case you'll keep your code as it is now.
Option 2
and this is a the solution, although pretty advanced one:
Use Java Configuration to create prototype beans in runtime with an ability to inject parameters into the bean.
Here is an example:
// Note, I've removed all the annotations, I'll use java configurations for that, read below...
public class SomeBuilder {
private final SomeContext ctx;
public SomeBuilder(SomeContext ctx) {
this.ctx = ctx;
}
public void test() {
System.out.println("ctx name: " + ctx.getName());
}
}
Now the class SomeService will also slightly change:
public class SomeService {
private Function<SomeContext, SomeBuilder> builderFactory;
public void run(SomeContext ctx) {
SomeBuilder someBuilder = builderFactory.apply(ctx);
someBuilder.test();
}
}
And now you should "glue" it to spring in an advanced way with Java Configurations:
#Configuration
public class MyConfiguration {
#Bean
public Function<SomeContext, SomeBuilder> builderFactory() {
return ctx -> someBuilder(ctx);
}
#Bean
#Scope(value = "prototype")
public SomeBuilder someBuilder(SomeContext ctx) {
return new SomeBuilder(ctx);
}
#Bean
public SomeService someService() {
return new SomeService(builderFactory());
}
}
For more details with a really similar example, see this tutorial
I've already gone through some tips on how to do this, like this one: stack overflow; but my problem is that my own implementation needs other things to be injected in it. Here's the example:
public interface MyService {}
public class ServiceImplA implements MyService {
#Autowired
private final SomeStuffA a_stuff;
}
public class ServiceImplB implements MyService {
#Autowired
private final SomeStuffB b_stuff;
}
#Configuration
public class SpringConfig {
#Bean
#Scope("singleton")
public MyService getService() {
boolean useA = // read config file and decide impl
return useA ? new ServiceImplA() : new ServiceImplB();
// I can't instantiate this, so i need them to be injected as well
}
}
I'm familiar with Google Guice, where I would do something like this:
bind(MyServicle.class).to(useA ? ServiceImplA.class : ServiceImplB.class);
So, I need a way to do this using Spring
I think your problem is that your base class MyService is not marked for any profile.
When you define profile the beans that have such specification will override beans that implement the same interface and do not have profile definition. This does not work this way.
When working with active profile X spring starts all beans that are not targeted for any profile and beans targeted for current profile. In your case you can choose wisely.
I think that if you want to use profiles you should define at least 2: A and B (the names are taken just for example.)
Now mark ServiceImplA as A and ServiceImplB as B:
#Service
#Profile("A")
public ServiceImplA interface MyService { ... }
and some development implementation
#Service
#Profile("B")
public ServiceImplB interface MyService { ... }
More about profiling you will get here
In a java-spring web-app I would like to be able to dynamically inject beans.
For example I have an interface with 2 different implementations:
In my app I'm using some properties file to configure injections:
#Determines the interface type the app uses. Possible values: implA, implB
myinterface.type=implA
My injections actually loaded conditionally relaying on the properties values in the properties file. For example in this case myinterface.type=implA wherever I inject MyInterface the implementation that will be injected will be ImplA (I accomplished that by extending the Conditional annotation).
I would like that during runtime - once the properties are changed the following will happen (without server restart):
The right implementation will be injected. For example when setting myinterface.type=implB ImplB will be injected where-ever MyInterface is used
Spring Environment should be refreshed with the new values and re-injected as well to beans.
I thought of refreshing my context but that creates problems.
I thought maybe to use setters for injection and re-use those setters once properties are re-configured. Is there a working practice for such a requirement?
Any ideas?
UPDATE
As some suggested I can use a factory/registry that holds both implementations (ImplA and ImplB) and returns the right one by querying the relevant property.
If I do that I still have the second challenge - the environment. for example if my registry looks like this:
#Service
public class MyRegistry {
private String configurationValue;
private final MyInterface implA;
private final MyInterface implB;
#Inject
public MyRegistry(Environmant env, MyInterface implA, MyInterface ImplB) {
this.implA = implA;
this.implB = implB;
this.configurationValue = env.getProperty("myinterface.type");
}
public MyInterface getMyInterface() {
switch(configurationValue) {
case "implA":
return implA;
case "implB":
return implB;
}
}
}
Once property has changed I should re-inject my environment. any suggestions for that?
I know I can query that env inside the method instead of constructor but this is a performance reduction and also I would like to think of an ider for re-injecting environment (again, maybe using a setter injection?).
I would keep this task as simple as possible. Instead of conditionally load one implementation of the MyInterface interface at startup and then fire an event that triggers dynamic loading of another implementation of the same interface, I would tackle this problem in a different way, that is much simpler to implement and maintain.
First of all, I'd just load all possible implementations:
#Component
public class MyInterfaceImplementationsHolder {
#Autowired
private Map<String, MyInterface> implementations;
public MyInterface get(String impl) {
return this.implementations.get(impl);
}
}
This bean is just a holder for all implementations of the MyInterface interface. Nothing magic here, just common Spring autowiring behavior.
Now, wherever you need to inject a specific implementation of MyInterface, you could do it with the help of an interface:
public interface MyInterfaceReloader {
void changeImplementation(MyInterface impl);
}
Then, for every class that needs to be notified of a change of the implementation, just make it implement the MyInterfaceReloader interface. For instance:
#Component
public class SomeBean implements MyInterfaceReloader {
// Do not autowire
private MyInterface myInterface;
#Override
public void changeImplementation(MyInterface impl) {
this.myInterface = impl;
}
}
Finally, you need a bean that actually changes the implementation in every bean that has MyInterface as an attribute:
#Component
public class MyInterfaceImplementationUpdater {
#Autowired
private Map<String, MyInterfaceReloader> reloaders;
#Autowired
private MyInterfaceImplementationsHolder holder;
public void updateImplementations(String implBeanName) {
this.reloaders.forEach((k, v) ->
v.changeImplementation(this.holder.get(implBeanName)));
}
}
This simply autowires all beans that implement the MyInterfaceReloader interface and updates each one of them with the new implementation, which is retrieved from the holder and passed as an argument. Again, common Spring autowiring rules.
Whenever you want the implementation to be changed, you should just invoke the updateImplementations method with the name of the bean of the new implementation, which is the lower camel case simple name of the class, i.e. myImplA or myImplB for classes MyImplA and MyImplB.
You should also invoke this method at startup, so that an initial implementation is set on every bean that implements the MyInterfaceReloader interface.
I solved a similar issue by using org.apache.commons.configuration.PropertiesConfiguration and org.springframework.beans.factory.config.ServiceLocatorFactoryBean:
Let VehicleRepairService be an interface:
public interface VehicleRepairService {
void repair();
}
and CarRepairService and TruckRepairService two classes that implements it:
public class CarRepairService implements VehicleRepairService {
#Override
public void repair() {
System.out.println("repair a car");
}
}
public class TruckRepairService implements VehicleRepairService {
#Override
public void repair() {
System.out.println("repair a truck");
}
}
I create an interface for a service factory:
public interface VehicleRepairServiceFactory {
VehicleRepairService getRepairService(String serviceType);
}
Let use Config as configuration class:
#Configuration()
#ComponentScan(basePackages = "config.test")
public class Config {
#Bean
public PropertiesConfiguration configuration(){
try {
PropertiesConfiguration configuration = new PropertiesConfiguration("example.properties");
configuration
.setReloadingStrategy(new FileChangedReloadingStrategy());
return configuration;
} catch (ConfigurationException e) {
throw new IllegalStateException(e);
}
}
#Bean
public ServiceLocatorFactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean serviceLocatorFactoryBean = new ServiceLocatorFactoryBean();
serviceLocatorFactoryBean
.setServiceLocatorInterface(VehicleRepairServiceFactory.class);
return serviceLocatorFactoryBean;
}
#Bean
public CarRepairService carRepairService() {
return new CarRepairService();
}
#Bean
public TruckRepairService truckRepairService() {
return new TruckRepairService();
}
#Bean
public SomeService someService(){
return new SomeService();
}
}
By using FileChangedReloadingStrategy your configuration be reload when you change the property file.
service=truckRepairService
#service=carRepairService
Having the configuration and the factory in your service, let you can get the appropriate service from the factory using the current value of the property.
#Service
public class SomeService {
#Autowired
private VehicleRepairServiceFactory factory;
#Autowired
private PropertiesConfiguration configuration;
public void doSomething() {
String service = configuration.getString("service");
VehicleRepairService vehicleRepairService = factory.getRepairService(service);
vehicleRepairService.repair();
}
}
Hope it helps.
If I understand you correctly then the goal is not to replace injected object instances but to use different implementations during interface method call depends on some condition at run time.
If it is so then you can try to look at the Sring TargetSource mechanism in combination with ProxyFactoryBean. The point is that proxy objects will be injected to beans that uses your interface, and all the interface method calls will be sent to TargetSource target.
Let's call this "Polymorphic Proxy".
Have a look at example below:
ConditionalTargetSource.java
#Component
public class ConditionalTargetSource implements TargetSource {
#Autowired
private MyRegistry registry;
#Override
public Class<?> getTargetClass() {
return MyInterface.class;
}
#Override
public boolean isStatic() {
return false;
}
#Override
public Object getTarget() throws Exception {
return registry.getMyInterface();
}
#Override
public void releaseTarget(Object target) throws Exception {
//Do some staff here if you want to release something related to interface instances that was created with MyRegistry.
}
}
applicationContext.xml
<bean id="myInterfaceFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="MyInterface"/>
<property name="targetSource" ref="conditionalTargetSource"/>
</bean>
<bean name="conditionalTargetSource" class="ConditionalTargetSource"/>
SomeService.java
#Service
public class SomeService {
#Autowired
private MyInterface myInterfaceBean;
public void foo(){
//Here we have `myInterfaceBean` proxy that will do `conditionalTargetSource.getTarget().bar()`
myInterfaceBean.bar();
}
}
Also if you want to have both MyInterface implementations to be Spring beans, and the Spring context could not contains both instances at the same time then you can try to use ServiceLocatorFactoryBean with prototype target beans scope and Conditional annotation on target implementation classes. This approach can be used instead of MyRegistry.
P.S.
Probably Application Context refresh operation also can do what you want but it can cause other problems such as performance overheads.
This may be a duplicate question or at least very similar, anyway I answered this sort of question here: Spring bean partial autowire prototype constructor
Pretty much when you want a different beans for a dependency at run-time you need to use a prototype scope. Then you can use a configuration to return different implementations of the prototype bean. You will need to handle the logic on which implementation to return yourself, (they could even be returning 2 different singleton beans it doesn't matter) But say you want new beans, and the logic for returning the implementation is in a bean called SomeBeanWithLogic.isSomeBooleanExpression(), then you can make a configuration:
#Configuration
public class SpringConfiguration
{
#Bean
#Autowired
#Scope("prototype")
public MyInterface createBean(SomeBeanWithLogic someBeanWithLogic )
{
if (someBeanWithLogic .isSomeBooleanExpression())
{
return new ImplA(); // I could be a singleton bean
}
else
{
return new ImplB(); // I could also be a singleton bean
}
}
}
There should never be a need to reload the context. If for instance, you want the implementation of a bean to change at run-time, use the above. If you really need to reload your application, because this bean was used in constructors of a singleton bean or something weird, then you need to re-think your design, and if these beans are really singleton beans. You shouldn't be reloading the context to re-create singleton beans to achieve different run-time behavior, that is not needed.
Edit The first part of this answer answered the question about dynamically injecting beans. As asked, but I think the question is more of one: 'how can I change the implementation of a singleton bean at run-time'. This could be done with a proxy design pattern.
interface MyInterface
{
public String doStuff();
}
#Component
public class Bean implements MyInterface
{
boolean todo = false; // change me as needed
// autowire implementations or create instances within this class as needed
#Qualifier("implA")
#Autowired
MyInterface implA;
#Qualifier("implB")
#Autowired
MyInterface implB;
public String doStuff()
{
if (todo)
{
return implA.doStuff();
}
else
{
return implB.doStuff();
}
}
}
You can use #Resource annotation for injection as originally answered here
e.g.
#Component("implA")
public class ImplA implements MyInterface {
...
}
#Component("implB")
public class ImplB implements MyInterface {
...
}
#Component
public class DependentClass {
#Resource(name = "\${myinterface.type}")
private MyInterface impl;
}
and then set the implementation type in properties file as -
myinterface.type=implA
Be aware that - if interesting to know about - FileChangedReloadingStrategy makes your project highly dependent on the deployment conditions: the WAR/EAR should be exploded by container and your should have direct access to the file system, conditions that are not always met in all situations and environments.
You can use Spring #Conditional on a property value. Give both Beans the same name and it should work as only one Instance will be created.
Have a look here on how to use #Conditional on Services and Components:
http://blog.codeleak.pl/2015/11/how-to-register-components-using.html
public abstract class SystemService {
}
public class FooSystemService extends FileSystemService {
}
public class GoSystemService extends FileSystemService {
}
#Configuration
public class SystemServiceConf {
#Bean
#Conditional(SystemServiceCondition.class)
public SystemService systemService(#Value("${value.key}") value) {
switch (value) {
case A:
return new FooSystemService();
case B:
return new GoSystemService();
default:
throw new RuntimeException("unknown value ");
}
}
}
public class SystemServiceCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return true;
}
}
I have a project with some independent bean X, that is autowired in a bunch of services. Services are used by each other, and finally used in single entry point (controller). Now there is new requirement: implement several versions of X, and decide witch one is to use according to entry point's parameter (enum XType). It would be nice to do it without changing services.
My idea of solution is to create custom scope UsesX and implement BeanFactoryPostProcessor, that will converts each BeanDefinition with UsesX to set of singletons for each XType. Also, it will adds qualifiers to this beans, to make it possible to make factory method for X and parameter-based selection in controller. But how to add this qualifier to #Autowired in services implicitly, without changing their classes?
UPD
Ok, example, I want to use db url "jdbc:mysql://Adb" when A requested, and "jdbc:mysql://Bdb" when B:
enum DatabaseType {A, B}
#Controller
#RequestMapping(/)
class MyController {
#Autowired ServiceProvider provider; // some way to get service by DatabaseType
void foo(#RequestParam DatabaseType dbType) {
ServiceA a = provider.getA(dbType);
a.bar();
ServiceB b = provider.getB(dbType);
b.baz();
}
}
#Service
class ServiceA {
// Don't want to get information about different databases in services
#Autowired ServiceB b;
#Autowired ServiceC c;
#Autowired DaoFoo dao;
//...
}
#Service
class ServiceB {
#Autowired ServiceC c;
#Autowired DaoFoo daoFoo;
#Autowired DaoBar daoBar;
//...
}
#Service
class ServiceC {
#Autowired DaoBar daoBar;
//...
}
#Repository
class DaoFoo {
DaoFoo(String dbURL) {/*...*/}
}
#Repository
class DaoBar {
DaoFoo(String dbURL) {/*...*/}
}
Also, it is required to "jdbc:mysql://Adb" and "jdbc:mysql://Bdb" be configured in XML configuration.
I want to wrap up your requirements so that it would be clear if I'm getting you right.
You have a set of #Services that you don't want to modify.
At this moment you have only one implementation of X type which is used by this services.
The choice of X implementation to be used in services would be defined by XType enum, which in turn would be available from request.
You want X type beans be configurable from xml.
OP: What X implementation should be used in case if one of this services would be called w/o XType?
So if my understanding is correct, it seems like you need Proxy for X type.
Within this Proxy you need to get this XType implicitly (f.ex. through ThreadLocal var).
As #Autowired is used, beans are identified by type in first place. Therefore, you need to use already existing X implementation for proxing and extract your current implementation and new one to different type.
As a result you might end up with following:
interface newX {
void save();
}
#Repository
class DaoFoo implements newX {
public void save() {...};
}
#Repository
class DaoBar implements newX {
public void save() {...};
}
class XImpl implements X, newX {
public final ThreadLocal<XType> currentXType = new ThreadLo...;
Map<XType, newX> mapping = ....
public void save() {mapping.get(currentXType.get()).save();};
}
Your example is a little confusing because your Services are named A and B, but you also use A and B for your DatabaseType. But I think I understand what you want.
I don't think you can do this with Autowired, but you can make your Services as #Scope("prototype") and retrieve them from context. The context should instantiate the Service the first time you request it, then reuse the same bean when the same input is provided.
#Configuration
public class ServiceProvider{
...
#Bean
#Scope("prototype")
public ServiceA serviceA(DatabaseType dbType) {
...
}
#Bean
#Scope("prototype")
public ServiceB serviceB(DatabaseType dbType) {
...
}
}
#Controller
#RequestMapping(/)
class MyController {
#Autowired
ConfigurableApplicationContext context
void foo(#RequestParam DatabaseType dbType) {
AutowireCapableBeanFactory beanFactory = context.getBeanFactory();
ServiceA serviceA = (ServiceA)context.getBean("serviceA", dbType);
...
}
}
Create a service interface like :
interface ServiceInterface{
public boolean isTheOne(String type); // or some suitable name.
}
all your services then need to implement this interface, then in controller
#Controller
#RequestMapping(/)
class MyController {
#Autowired
Set<ServiceInterface> provider;
void foo(#RequestParam DatabaseType dbType) {
ServiceInterface service = provider.stream().filter(s -> s.isTheOne(String dbType));
service.bar();
}
}
You can maintain your XType as an enumerator within a customised Qualifier by developing #Interface.
Please find below a sample which mentions conditional wiring of beans based upon different types of datatype:
#Target({ElementType.FIELD,
ElementType.METHOD,
ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public static #interface DBHost{
public static enum DatabaseType {
A,
B
}
}
#Autowired
#DBHost(DBHost.DatabaseType.A)
ServiceBean serviceInstanceA;
Find additional usage of Qualifier annotation here
I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.