What will be best practice for bean creation fallback? - java

I have 3 beans which implements same interface. But i want to instantiate only one of them, if A is available then A, if there is no A then B, and if there is no A and B then C.
When i had only two of them it was simple, default one had '#ConditionalOnMissingBean' annotation. But not sure will that work with 3 of them.
Is it possible to solve this with annotations?
I don't want to create factory method, as I have multiple applications using those components, and i dont have ability to change some of them

Yes, using the #ConditionalOnMissingBean annotation , you should set the code like this:
#Configuration
public class ConditionalOnMissingBeanConfig {
#Bean
public A beanA(){
return new A(); // will initialize as normal
}
#Bean
#ConditionalOnMissingBean(name="beanA")
public B beanB(){
return new B(); // it will not initialize as
// beanA is present in the beanFactory.
}
#Bean
#ConditionalOnMissingBean(name="beanB")
public C beanC(){
return new C(); // will get initialized as there is no
// bean with name beanB in BeanFactory.
}
}
Conditional that only matches when the specified bean is missing from the beanfactory. This will only match when the bean definition is processed by the application context and as such is recommended to be used by auto-configuration classes only.

Let me propose an alternative approach:
You can introduce a configuration variable that will denote the "domain" related explanation of what should the system do
Example:
If A, B, C are, say, different caching strategies (in-memory, redis, ehcache), then introduce the following configuration value:
caching.type=ehcache // or in-memory or redis
Then the beans can be declared as follows:
#ConditionalOnProperty(name="caching.type" , havingValue="in-memory", matchIfMissing = true) // this is a fallback if no property is specified
class InMemoryCaching implements CachingStrategy {}
#ConditionalOnProperty(name="caching.type" , havingValue="ehcache", matchIfMissing = false)
class EhcacheCaching implements CachingStrategy {}
#ConditionalOnProperty(name="caching.type" , havingValue="redis", matchIfMissing = false)
class RedisCaching implements CachingStrategy {}
I know its slightly different solution, but it has the following benefits:
its easy to add yet another implementation of the interface - no code changes in existing beans
beans are not aware of each other even not at the level of bean names in spring
easy to debug - just look at the configuration property value
this approach can be applied to #Configuration and manage set of beans

Related

Spring naming strategy for #ConfigurationProperties

Lets say i have properties class ExampleProps:
#ConfigurationProperties(prefix = "prefix.stuff")
#ConstructorBinding
#AllArgsConstructor
public class ExampleProps {
...
}
Which is located in src/main/java/com/example/props/.
When spring scans for ConfigurationProperties it gives it following name
"prefix.stuff-com.example.props.ExampleProps"
Can i somehow change naming strategy to the regular one (Class name with lowercase first letter)?
You can control the name of the bean to which configuration properties are bound if you define it using a #Bean method:
#Configuration
class ExampleConfiguration {
#Bean("theNameThatYouWantTheBeanToHave")
#ConfigurationProperties("prefix.stuff")
ExampleProps exampleProps {
return new ExampleProps();
}
}
For this to work, you'd have to stop using constructor binding and switch to setter-based binding instead. In outline form, that would leave ExampleProps looking like this:
public class ExampleProps {
// Properties fields
// Getters and setters
}
Having said this, I would think twice about following this approach. Generally speaking, the name that's assigned to a bean should not matter. You may want to look at why the name is important and see if that can be changed rather than trying to force the bean to have a particular name.

Can I negate (!) a collection of spring profiles?

Is it possible to configure a bean in such a way that it wont be used by a group of profiles? Currently I can do this (I believe):
#Profile("!dev, !qa, !local")
Is there a neater notation to achieve this? Let's assume I have lots of profiles. Also, if I have a Mock and concrete implementation of some service (or whatever), Can I just annotate one of them, and assume the other will be used in all other cases? In other words, is this, for example, necessary:
#Profile("dev, prof1, prof2")
public class MockImp implements MyInterface {...}
#Profile("!dev, !prof1, !prof2") //assume for argument sake that there are many other profiles
public class RealImp implements MyInterface {...}
Could I just annotate one of them, and stick a #Primary annotation on the other instead?
In essence I want this:
#Profile("!(dev, prof1, prof2)")
Thanks in advance!
Since Spring 5.1 (incorporated in Spring Boot 2.1) it is possible to use a profile expression inside profile string annotation (see the description in Profile.of(..) for details).
So to exclude your bean from certain profiles you can use an expression like this:
#Profile("!dev & !prof1 & !prof2")
Other logical operators can be used as well, for example:
#Profile("test | local")
Short answer is: You can't in versions of Spring prior to Spring 5.1 (i.e. versions of Spring Boot prior to 2.1).
But there is a neat workarounds that exists thanks to the #Conditional annotation.
Create Condition matchers:
public static abstract class ProfileCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (matchProfiles(conditionContext.getEnvironment())) {
return ConditionOutcome.match("A local profile has been found.");
}
return ConditionOutcome.noMatch("No local profiles found.");
}
protected static abstract boolean matchProfiles(final Environment environment);
}
public class DevProfileCondition extends ProfileCondition {
protected boolean matchProfiles(final Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
return prof.equals("dev") || prof.equals("prof1") || prof.equals("prof2");
});
}
}
public static class ProdProfileCondition extends ProfileCondition {
protected boolean matchProfiles(final Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
return (!prof.equals("dev") && !prof.equals("prof1") && !prof.equals("prof2"));
});
}
}
Use it
#Conditional(value = {DevProfileCondition.class})
public class MockImpl implements MyInterface {...}
#Conditional(value = {ProdProfileCondition.class})
public class RealImp implements MyInterface {...}
However, this aproach requires Springboot.
From what I understand, what you want to do is be capable of replacing some of your beans with some stub/mock beans for specific profiles. There are 2 ways to address this:
Exclude the not needed beans for the corresponding profiles and include by default everything else
Include only the required beans for each profile
The first option is feasible but difficult. This is because the default behaviour of Spring when providing multiple profiles in #Profile annotation is an OR condition (not an AND as you would need in your case). This behaviour of Spring is the more intuitive, because ideally each profile should correspond to each configuration of your application (production, unit testing, integration testing etc.), so only one profile should be active at each time. This is the reason OR makes more sense than AND between profiles. As a result of this, you can work around this limitation, probably by nesting profiles, but you would make your configuration very complex and less maintainable.
Thus, I suggest you go with the second approach. Have a single profile for each configuration of your application. All the beans that are the same for every configuration can reside in a class that will have no #Profile specified. As a result, these beans will be instantiated by all the profiles. For the remaining beans that should be distinct for each different configuration, you should create a separate #Configuration class (for each Spring profile), having all of them with the #Profile set to the corresponding profile. This way, it will be really easy to tract what is injected in every case.
This should be like below:
#Profile("dev")
public class MockImp implements MyInterface {...}
#Profile("prof1")
public class MockImp implements MyInterface {...}
#Profile("prof2")
public class MockImp implements MyInterface {...}
#Profile("the-last-profile") //you should define an additional profile, not rely on excluding as described before
public class RealImp implements MyInterface {...}
Last, #Primary annotation is used to override an existing beans. When there are 2 beans with the same type, if there is no #Primary annotation, you will get an instantiation error from Spring. If you define a #Primary annotation for one of the beans, there will be no error and this bean will be injected everywhere this type is required (the other one will be ignored). As you see, this is only useful if you have a single Profile. Otherwise, this will also become complicated as the first choice.
TL;DR: Yes you can. For each type, define one bean for each profile and add a #Profile annotation with only this profile.

What is the purpose of the Spring Bean annotation name attribute?

I'm just learning spring, and something struck me as very odd about the annotation configurations using the name attribute as a string.
#Bean(name = "com.my.injected.Service")
public InjectedService injectedService() {
return injectedService;
}
Is this name similar to the Spring Bean XML configuration id and class attributes?
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
Why isn't this simply
#Bean(clazz = com.my.injected.Service.class)
public InjectedService injectedService() {
return injectedService;
}
instead?
You're fully qualifying the path in both cases and actually using the class makes it way easier for your IDE to tell you when you've screwed it up. I understand that the XML configuration came first, and naturally it was always looking up things by string, so is this just a holdover? Is there some advantage to using strings or major disadvantage to using .class?
Question was originally based on a false premise. I edited it to spell out what this premise was and make it less confusing for new people who come along. Hopefully I did this such that the given answers are still exactly applicable; apologies if not.
#Bean annotation is meant to provide a spring bean. The type of the bean to provide will be the same type of the class/interface you define in the return method. So, instead of declaring to return a concrete class in the method, return the top (abstract) class/interface instead.
Imagine this case:
public interface MyEntityDao {
MyEntity get(String id);
}
#Repository
public class MyEntityDaoDatabaseImpl implements MyEntityDao {
#Override
public MyEntity get(String id) {
/* implementation that goes to database every time */
}
}
#Repository
public class MyEntityDaoCacheImpl implements MyEntityDao {
#Override
public MyEntity get(String id) {
/* implementation that looks the data
up in cache, never in database */
}
}
#Configuration
public class MyAppConfiguration {
#Bean
public MyEntityDaoDatabaseImpl method1() {
return new MyEntityDaoDatabaseImpl();
}
#Bean
public MyEntityDaoCacheImpl method2() {
return new MyEntityDaoCacheImpl();
}
}
#Service
public class MyEntityService {
#Autowired //what to inject here?
MyEntityDao dao;
}
In case above, there are two implementations of the proposed interface. How the framework may be able to understand which implementation to use except for the name?
#Service
public class MyEntityService {
#Autowired
#Qualifier("properBeanNameToInject")
MyEntityDao dao;
}
Bean name is not necessarily related to its class or even any of interfaces it implements. It is a name and nothing more. When you use the annotation configuration, Spring figures out what the exact class or interface the #Bean provides like the rest of java code would: either through the fully qualified name in the code or through the imports specified in the file. In your case, you presumably have an import com.my.injected.Service; statement at the top of the java file.
Your example is using the fully qualified class name as the bean name. It is your choice. You could use any other identifier. Using the fully qualified name could be useful if your code is providing an object that is named exactly like another 3rd party #Bean object that your code must include or consume. However, you could just as easily use name = "myService".
The bean name helps Spring (and application programmer) to distinguish between multiple instances of of the same bean class because you can deploy the same class as bean several times. If only one instance of bean type appear you event do not have to give it name manually: spring does this by default.
If you have several beans that have the same type or implement the same interface and you want to refer specific bean use #Qualifier annotation.

Spring Autowire Annotation with Several Interface Implementations

Suppose you have one interface
public interface A {
public void doSomething();
}
and two implementation classes
#Component(value="aImpl1")
public class AImpl1 implements A {
}
#Component(value="aImpl2")
public class AImpl2 implements A{
}
And finally a class that will use an "A" implementation:
#Component
public class MyClass {
#Autowire
A a;
}
Now if I want to inject AImpl1 I add the #Qualifier("aImpl1") while if I want to inject AImpl2 I add #Qualifier("aImpl2")
The question is: Is it possible to instruct spring somehow to look up all implementations of "A" in this case AImpl1 and AImpl2 and use some application specific conventions to choose the most appropriate implementation? for example in this case my convention could be use the implementation with the greatest suffix (i.e. AImpl2)?
EDIT: the class MyClass should not be aware at all about the implementation lookup logic, it should just find its property "a" set with an object of AImpl2.
You can inject all implentations as List:
#Autowired
List<A> as;
or as Map with bean name as key:
#Autowired
Map<String, A> as;
and then choose proper implementation manually (perhaps, in a setter method):
#Autowired
public void setAs(Map<String, A> as) {
this.a = ...;
}
Assuming you already have hundreds of interfaces and implementations (as you said in a comment), and you do not want to refactor all the code... then is a tricky problem... and this is a tricky solution:
You could create a custom BeanDefinitionRegistryPostProcessor and implement either the method postProcessBeanDefinitionRegistry or postProcessBeanFactory.
This way you have access to all bean definitions before they are instantiated and injected. Do your logic to find which is the preferred implementation for each one of your interfaces, and then, set that one as primary.
#Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) throws BeansException {
// this method can be used to set a primary bean, although
// beans defined in a #Configuration class will not be avalable here.
}
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
// here, all beans are available including those defined by #configuration, #component, xml, etc.
// do some magic to somehow find which is the preferred bean name for each interface
// you have access to all bean-definition names with: beanFactory.getBeanDefinitionNames()
String beanName = "aImpl2"; // let's say is this one
// get the definition for that bean and set it as primary
beanFactory.getBeanDefinition(beanName).setPrimary(true)
}
}
The hard part is to find the bean name, it depends of the specifics of your application. I guess that having a consistent naming convention will help.
Update:
It seems that both methods in the interface BeanDefinitionRegistryPostProcessor can be used for this purpose. Having in mind that in the postProcessBeanDefinitionRegistry phase, beans configured through #configuration classes are not yet available, as noted in the comments below.
On the other hand they are indeed available in postProcessBeanFactory.
If you have a Configuration class you could use a method in that to make the decision of which implementation of A to return. Then the autowired will inject the appropriate instance for that class.
#Configuration
public class ApplicationConfiguration {
#Bean
A getA() {
// instantiate the implementation of A that you would like to have injected
// or you could use reflection to find the correct class from the classpath.
// return the instance
}
}
This assumes you always want to use the same instance everywhere you are injecting A. If not, then you could have different #Bean annotated methods with names to get different versions.
You can try to use Spring Profiles.

Using autowired beans in an object created at runtime

I have a class B which implements W interface. It has a default implementation of W's method. class C and D override default implementation for which they need a service whose bean is instantiated by spring. String a and b comes from user and hence there is no way I can create a bean of B/C/D in advance. So I have a factory which creates a new object based on user parameters (it will create B/C/D based on parameters). Is there any clean way I can use service beans(aa/bb/cc/dd etc.) from inside C and D (spring autowires during server startup, at that time parameter required for instantiating B/C/D are not available) or is there any better way to solve the problem ?
Class B implements W{
String a;
String b;
B (String a, String b);
w_method(){
}
}
Class C extends B {
#Autowired
Service aa;
#Autowired
Service bb;
#Autowired
Service cc;
#override
w_method(){
}
}
Class D extends B {
#Autowired
Service dd;
#override
w_method(){
}
}
There are three possible approaches:
The fact that constructor arguments come from user doesn't mean that these objects cannot be created by Spring.
You can declare them as Spring beans of scope prototype and do the following in your factory:
public C createC(String a, String b) {
return applicationContext.getBean("c", a, b);
}
A disadvantage of this method is that your factory depends on ApplicationContext.
You can annotate these classes with #Configurable and enable AspectJ weaving. In this case Spring will configure objects of these classes even if you create them with new. See 7.8.1 Using AspectJ to dependency inject domain objects with Spring.
You can trigger autowiring manually as
applicationContext.getAutowireCapableBeanFactory().autowireBean(...);
This approach is useful when you don't have control over creation of objects that you need to autowire (servlets, etc).
I think the simplest method would be to wire the services into the factory and call setters on the B / C / D objects before you return them, rather than attempting to use #Autowired.
Or use axtavt's constructor argument method. If you want to avoid depending on ApplicationContext, you can use Lookup Method Injection, but you'll have to patch Spring per this blog post: http://nurkiewicz.blogspot.co.uk/2010/08/creating-prototype-spring-beans-on.html

Categories

Resources