How to determine the effective class (Bean) to be used when using an interface as a class member using Spring Boot? - spring-boot

Is it possible to achieve the following?
I want to determine the type of a class to be used at runtime with Spring Boot. Hence, I defined an interface MyInterface defining the required methods:
public interface MyInterface<C extends DocumentContext> {
MyResult<C> doForX(C documentContext);
MyResult<C> doForY(C documentContext);
}
An abstract base class implements this interface and provide some common methods:
#Slf4j
#Component
public abstract class BaseClass<C extends DocumentContext> implements MyInterface<C> {
private MyInterface myInterface;
...
public MyResult<C> doForX(C documentContext) { ... }
public Myresult<C> doForY(C documentContext) { ... }
}
This base class is the parent for two subclasses:
#RequiredArgsConstructor
#Slf4j
#Component
#ConditionalOnProperty(name = "sanitizerType", havingValue = "REGEX", matchIfMissing = false)
public class FirstClass<C extends documentContext> extends BaseClass<C> implements<MyInterface<C> {
...override base methods
}
#RequiredArgsConstructor
#Slf4j
#Component
#ConditionalOnProperty(name = "sanitizerType", havingValue = "SAX", matchIfMissing = false)
public class SecondClass<C extends documentContext> extends BaseClass<C> implements<MyInterface<C> {
...override base methods
}
A class using one of the two concrete subclasses:
public class MyUsingClass extends AnotherClass {
public MyUsingclass(MyInterface<ADocumentContext> myInterfaceImpl, ...)
...
}
This compiles but when I try to run the application I get:
*************************** APPLICATION FAILED TO START
Description:
Parameter 1 of constructor in MyUsingClass required a bean of type
'MyInterface' that could not be found.
Action:
Consider defining a bean of type 'MyInterface' in your configuration.
In my application.properties I have:
# ... [SAX | REGEX]
#sanitizerType=REGEX
...and I have a Properties class:
#Value("${sanitizerType:REGEX}")
private SanitizerType sanitizerType;
, whereby SanitizerType is just:
public enum SanitizerType {
REGEX, SAX
}

You said you would like to "determine the type of a class to be used at runtime with Spring Boot".
I'm not sure if you want your "class to be used" change while your application is running depending on current condidionts or set once when the application boots and use it always until the app is running.
If you uncomment this line of your application.properties #sanitizerType=REGEX
Your code should work - but it simply defines the SanitizerType (hence your MyInterface implementation) once when you start your application.
Please note that by setting a private field of Properties class you don't set the property globally (at least not the way you showed in the question), but only set the private field value either according what's set in your properties file or (if value is missing in properties) to "REGEX".
If you want to use it in different places of your program you need to inject it differently

Related

Instantiate a service according to a property on Spring Boot

A good practice is defining a service as an interface and its implementation on a class.
Assuming I have 2 classes which implement the same interface, and I'd like to differentiate them according a property (not to a profile). I mean, if I have #Autowire private MyServiceInterface myService; I'd like to receive an instance of PotatoServiceImpl if I have myproperty=potato or an instance of TomatoServiceImpl if I have myproperty=tomato.
I'm not using profiles.
P.S. When I say a property,I mean a property in application.properties
Look:
public interface MyInterface {
}
#Component
#ConditionalOnProperty(prefix = "myproperty" havingValue = "potato", matchIfMissing = false)
public class MyPotatoImpl implements MyInterface {
}
#Component
#ConditionalOnProperty(prefix = "myproperty" havingValue = "tomato", matchIfMissing = false)
public class MyTomatoImpl implements Myinterface {
}
#Component
public class Consumer {
#Autowire
private MyInterface tomatoOrPotato; //depending on property myproperty value
}
This is for me a very elegant solution to implement the strategy creational design pattern spring styled.
Look here for docs about #ConditionalOnProperty annotation.

Injecting 2 bean with same class name

I have a app 'app_test' which consists a class TestClass with #Service anotation. I have a library class 'lib_test' with bean in XML file with id=''TestClass'. Both are in different package.
I m injecting #Service bean as follows
Import com.app.TestClass
Class TestController
{
Private final TestClass testClass;
#Inject
TestController (TestClass testClass)
{
This.testClass =testClass;
}
}
It should inject by type since they are in different package. But the controller is giving qualified bean not found.
I can resolve it by giving #Qualifier and giving name to #Service. But y is it needed? Since both are in different package it should autowire by type right? Or m missing some concept?
Although they are in different packages if they are of the same type Spring does not know which to use
I'd suggest marking any service class with #Primary.
package com.app.TestClass
#Primary
#Repository
public class TestClass implements XXX
This way it will be selected as the default autowire candididate, with no need to autowire-candidate on the other bean.
Also, rather than using #Autowired #Qualifier, I find it more elegant to use #Resource for picking specific beans.
I've always found this a strange limitation of Spring's standard bean naming convention. It does not include the package part of the class in the name leading to duplicates in large projects when classes have the same name.
This is why I always configure Spring projects with a different BeanNameGenerator:
public class CustomAnnotationConfigWebApplicationContext extends AnnotationConfigWebApplicationContext {
private BeanNameGenerator qualifiedAnnotationBeanNameGenerator = new QualifiedNameAnnotationBeanNameGenerator();
#Override
protected BeanNameGenerator getBeanNameGenerator() {
return this.qualifiedAnnotationBeanNameGenerator;
}
}
And the generator:
public class QualifiedNameAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
#Override
protected String buildDefaultBeanName(BeanDefinition definition) {
String qualifiedName = definition.getBeanClassName();
return Introspector.decapitalize(qualifiedName);
}
}
With this setup, common class names that are in different packages are automatically recognized as being different and the correct one gets injected.

How to write test class with #Autowired variable for inherited class

I have problem with writing test cases using SPOCK. Could anyone please help me?
I have classes & interfaces like below,
//Helper class
public class ObjClass{
//Defining all property variables & corresponding getters & setters methods.
}
//Interface
public interface B{
//Declaring custom methods for Mongo repository.
public int getId();
}
public interface A extends MongoRepository<ObjClass, Serializable>, B{
//Defining some standard MongoRepository methods here
}
// Implementation Classes
public class Aimpl implements B{
//implementing all B interface methods
}
public class ctrlClass{
#Autowired
A aObj;
public int getIdValue(){
return aObj.getId();
}
}
And below is the corresponding SPOCK test cases:
class test extends Specification
{
ctrlClass obj1
A obj2 //interface class object
def setup(){
obj1 = new ctrlClass();
obj2 = new Aimpl(); //Creating object for interface using impl class.
obj1.aObj = obj2
}
def "test"(){
when:
def a = obj2.getIdValue()
then:
//validating some conditions here with 'a' value
}
}
Getting below error while executing above test case,
Cannot cast object Aimpl to class A.
The same above scenario is working fine with Spring #Autowired. But not in Spock.
*
Is there any alternate available for #Autowired in SPOCK? Please suggest me some solutions & your comments.
*
The problem you have is the ability of Spring to bind the interface with the related implementation.
If your interface has only one implementation and the single implementation has the annotation #Component with Spring's component scan enabled, than Spring framework success to infer the relationship between the interface and its implementation.
In case the component scan is not enabled, then the bean should be explicitly defined in your spring configuration file (such as application-config.xml).
The casting of Aimpl and A cannot succeed because the inheritance classes/interface are different.
You should change the code like the following:
public class ctrlClass{
#Autowired
Aimpl aObj;
public int getIdValue(){
return aObj.getId();
}
}
And in the test class make the following change:
A obj2 //interface class object
Should be changed to:
Aimpl obj2

Inject a list of classes that extends an abstract class with Spring

Hy,
I have the next code:
public abstract class MyClass{
protected abstract void method1();
}
Classes that extend the first one:
#Component
public class MyClass1 extends MyClass{
.....
}
#Component
public class MyClass2 extends MyClass{
.....
}
My class where I try to inject list of classes that extends an abstract class
#Component
public class SpringClass{
#Autowired **//It doesnt work, nothing is inyected!**
List<MyClass> classes
}
My problem is it doesnt work, it doesnt inject the list of classes that extend MyClass in property classes. Why?
Thanks
You can remove #Autowired annotation for the instance variable and add it to the setter method. After doing this your spring class would be
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class SpringClass {
List<MyClass> classes;
#Autowired
public void setClasses(List<MyClass> classes) {
this.classes = classes;
}
}
Hope this helps.
#Component
public class SpringClass {
#Autowired
private Map<String, YourInterface> map;
}
String in map will be contain all classes name that implements the YourInterface as String.
if you want to get all instances - use map.values()
if you want to get specific instance - get it by class name.
in additional, you can customize the key by another recognize such as Enum.
in this case - you need to configure this as #Bean
as bellow :
#Configuration
public class CalculationHandlerConfig {
#Bean
public HashMap<OperatorTypeEnum, CalculatorService> CalculationHandlers(Map<String, CalculatorService> beansMap) {
HashMap<OperatorTypeEnum, CalculatorService> map = new HashMap<>();
for (CalculatorService bean : beansMap.values()) {
map.put(bean.getOperatorType(), bean);
}
return map;
}
}
It looks like it could be dependent on the version of Spring being used, however, you may also want to try using an interface that all the desired classes implement, and injecting the list referencing the interface.
In other words, instead of this..
List<AbstractOrConcreteBaseClass>
use...
List<Interface>

Spring Security and super class

fIn my app I'm using the Spring Security and have defined next classes.
public abstract class AbstractService {
public void save(){
.....
}
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS)
publist class UserService extends AbstractService {
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS_X)
publist class XService extends AbstractService{
}
I need #PreAuthorize annotation from the child class to be applied to the super class methods( for example: save()).Is there any way to achieve it by avoiding to override super class methods?
AbstractService will have more than one child( > 10) wherein each have own #PreAuthorize value.
You can try to use SPEL for that.
Because AFAIK, you must annotate methods or the superclass or the superclass itself, and the annotation must be a plain string (or a static final which is the same). But the string may contain SPEL expressions that will reference the target object. Example if only roles were used :
#PreAuthorize("hasAnyRole(#root.target.requiredRoles)")
public abstract class AbstractService {
public abstract String getRequiredRoles();
public void save(){
.....
}
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS)
publix class UserService extends AbstractService {
#Override
public String getRequiredRoles() {
return "ROLE_USER, ROLE_CLIENT";
}
....
}
As the condition is evaluated by SPEL at runtime, it will use the overriden getter and the list of roles can be defined in child class.
Without knowing what are your requirements for authorization expressions, I cannot be sure if that will do the trick, but I successfully use that for caching methods in a superclass, with keys depending on values in child classes.

Resources