How to dynamically autowire beans in Spring with annotations? - java

A user can write down a url, and then, depending on the pattern of the url, my interface should use the correct implementation. Therefore, want to dynamically change my Spring's bean logic execution depending on that url that my controller receives.
Here is my controller:
#PostMapping(value = "/url",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_HTML_VALUE)
public ResponseEntity<InputStreamResource> parseUrl(#RequestParam String url) throws IOException {
myInterface.dosomething(url);
return ResponseEntity.ok();
}
My interface :
public interface Myterface {
void myInterface(String url);
}
and my implementations:
#Service
public class myImpl1 implements myInterface {
#Override
public void doSomething(String url) {}
}
I already tried #Qualifier, but it is not dynamic. The thing is that I will have a lot of different url patterns and therefore implementations overtime, I'd like to have to add only one class per pattern and not to have to modify anything.

You can try something like this in a configuration class or you can use #Profile annotation :
#Configuration
public class MyConfig {
#Bean
public MyInterface createBean(String URL) {
MyInterface bean;
switch(URL) {
case url1:
bean = new Implementation1();
break;
case url2:
bean = new Implementation2();
break;
default: bean = new DefaultImplementation();
}
return bean;
}
}
Check this answer for more details.

Related

Spring AOP doesn't work with #Component class

I'm using Spring AOP for exception handling but there is one point that I guess my component class is out of Spring Proxy so Spring AOP annotation that I created doesn't work in that class.
#Configuration
#AllArgsConstructor
public class MGRuleConfig {
private final GRepository repository;
private final GInitializer initializer;
private final GMapper mapper;
#Bean
#Qualifier("mRules")
public List<GRules> mRules(){
SSRule rule1 = new SSRule();
CSRule rule2 = new CSRule();
MPRule rule3 = new MPRule();
EGRule rule4 = new EGRule();
return List.of(rule1, rule2, rule3, rule4);
}
#Bean
public GService gService() {
return new MGServiceImpl(repository, initializer, mapper);
}
}
Then I have this service;
#Service
#RequiredArgsConstructor
public class MGServiceImpl implements GService {
............
#Override
public GaDTO executeRules(String gId, Integer pN) {
Ga ga = repository.findById(gId);
GaDTO gaDTO = mapper.toDTO(ga);
List<GaRules> mRules = (List<GaRules>) applicationContext.getBean("mRules");
mRules.forEach(rule -> rule.apply(gaDTO, pN));
repository.save(mapper.toEntity(gaDTO));
return gaDTO;
}
I need to put my exception handling annotation into that apply method but aspect doesn't work in that method.
#Component
public class SSRule implements GaRules {
#Override
#IPException
public void apply(GaDTO gaDTO, Integer pN) {
PDTO p1 = gaDTO.getP1();
PDTO p2 = gaDTO.getP2();
if (PTEnum.P_1.equals(gaDTO.getPT())) {
sS(gaDTO, pN, p1, p2);
} else {
sS(gaDTO, pN, p2, p1);
}
}
Annotation doesn't work in there. Here's my aspect class;
#Aspect
#Component
public class IPExceptionAspect {
#Around("execution(public * c.m.s.r.i.SSRule.apply(..)) && " +
"#annotation(c.m.s.i.a.IPException)")
public Object checkIP(ProceedingJoinPoint pjp) throws Throwable {
pjp.proceed();
return pjp;
}
}
So, what should I do to make IPException annotation and my Spring AOP work and why doesn't it work?
The problem is your code, you are creating instances of those rules yourself inside a bean method and expose them as a List. Which means the bean is of type List not your own SSRule and thus it won't work.
Instead make an #Bean method, or use the detected instance to inject into the list. As your SSRule is annotated you will already have an instance, just inject that into your #Bean method.
Bean
#Qualifier("mRules")
public List<GRules> mRules(SSRule rule1){
CSRule rule2 = new CSRule();
MPRule rule3 = new MPRule();
EGRule rule4 = new EGRule();
return List.of(rule1, rule2, rule3, rule4);
}
Now you will get the Spring managed instance which will have AOP applied.
Although I would hardly call this AOP as it is too specific for one class (not really crosscutting in that regard).

Calling service depend on PathVariable using single controller

I need to call implementation service from single controller depend on PathVariable
/{variable}/doSomething
public void controller(#PathVariable("variable") variable)
if variable == 1
call service1Impl();
else if variable == 2
call service2Impl();
but I need my controller plain like this and not using if, else
public void controller(...) {
call service();
}
I need to find some solution for auto-configuration my app when getting any PathVariable, it should know which service needs to call.
I try to using
load Config.class as context - #Configuration
#Configuration
public class AppConfig {
#Bean(name = "variableValue1")
public DummyService getService1() {
return new DummyServiceImpl();
}
#Bean(name = "variableValue2")
public AnotherService getService2() {
return new AnotherServiceImpl();
}
but in controller I need to load this config as context then its not plain enough
bean factory
its work but my controller not enough plain for me
I need to do like this one but it must based on PathVariable not property name.
#Configuration
public class GreetingServiceConfig {
#Bean
#ConditionalOnProperty(name = "language.name", havingValue = "english", matchIfMissing = true)
public GreetingService englishGreetingService() {
return new EnglishGreetingService();
}
#Bean
#ConditionalOnProperty(name = "language.name", havingValue = "french")
public GreetingService frenchGreetingService() {
return new FrenchGreetingService();
}
}
------------------------------------------------
#RestController
public class HomeController {
#Autowired
GreetingService greetingService;
#RequestMapping("/")
public String home() {
return greetingService.greet();
}
}
So based on the pathvariable, the specific method needs to be executed..
This is just a suggestion, since you dont want to go for if else
you can use Hashmap for this,
HashMap<Integer, Runnable> hm = new HashMap<Integer, Runnable> ();
For example,
pathvariable is 1 -> method be executed is method1()
pathvariable is 2 -> method be executed is method2()
hm.put(1, method1())
hm.put(2, method2())
So in controller,
if PathVariable is 1,
hm.get(1).run(); // hm.get(variable).run()

Unable to use Aspect around over a method

This is in a Spring project where I have Aspect setup in a different module on which other modules are dependant on. I am using custom annotations. It works fine on some methods but not on others. Could I get advice on what the problem is?
This Aspect class is coming from a module and other modules are dependant on this.
#Aspect
#Component
public class VirtualizeJsonAspect {
#Around("#annotation(virtualizeJson)")
public Object virtualizeJsonAround(ProceedingJoinPoint pjp, VirtualizeJson virtualizeJson) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Class returnType = signature.getReturnType();
// ....... This is working fine
}
Following 3 classes in same module if relevant
It works here when I try to use Aspect
Path for reference -> com/domain/abc/service/helper/DataHelper.java
#Component
#PropertySource("classpath:something.properties")
public class DataHelper {
#VirtualizeJson(serviceNm = "ABC", virtualizedDataPath = "/url/some.json")
public SomeResponse getOffers(SomeRequest someRequest){
HttpEntity httpRequestEntity = createRequestEntity(request);
ResponseEntity<SomeResponse> httpResponseEntity;
SomeResponse someResponse = null;
// ......... Aspect works here. I do not get in here. Instead I land in the Aspect class as expected
}
}
Config file
Path for reference -> com/domain/abc/service/config/AppConfig.java
#Configuration
#ComponentScan(basePackages = {{"com.domain.virtualize"})
#EnableAspectJAutoProxy
public class AppConfig extends WebMvcConfigurationSupport{
// some bean setups not rlevant to Aspect
}
It is not working here when I try to use Aspect
Path for reference -> com/domain/abc/service/rules/CheckSomething.java
#Component
public class CheckSomething extends SomeRule {
#VirtualizeJson(serviceNm = "ABC", virtualizedDataPath = "/url/some.json")
public SomeResponse checkOffers(String someNumber){
int a = 1;
return null;
// I land inside this method which is incorrect. I shouldh have landed at the Aspect class instead
}
}
Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface VirtualizeJson {
public String serviceNm();
public String virtualizedDataPath();
}

Alternative to ApplicationContext.getBean() in Spring

I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?
#RestController
#RequestMapping("/api")
public class APIHandler {
#Value("${fromConfig}")
String fromConfig;
private ApplicationContext applicationContext;
public Bot(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
someInterface.doSomething();
}
}
I have an interface called SomeInterface defined like:
public interface SomeInterface {
void doSomething();
}
And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.
Any directions would be appreciated.
Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:
public class APIHandler {
#Autowired
private Map<String, SomeInterface> impls;
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}
assuming you have two implementations like following
// qualifier can be omitted, then it will be "UseClass1" by default
#Service("beanName1")
public class UseClass1 implements SomeInterface { }
// qualifier can be omitted, then it will be "UseClass2" by default
#Service("beanName2")
public class UseClass2 implements SomeInterface { }
This is only code works for me to get beans dynamically from ApplicationContext
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}
You can define your spring bean component with
#Profile("dev") , #Profile("test")
and inject as mention comment, then switch profile with
-Dspring.profiles.active=test jvm argument
The real question is not how to solve this, but why would you inject something different based on a configuration value?
If the answer is testing, then perhaps it's better to use #Profiles as #murat suggested.
Why are different implementations of an interface there on your classpath?
Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)
I think you should probably use a configuration class to produce your bean based on the fromConfig string value:
Your controller:
#RestController
#RequestMapping("/api")
public class APIHandler {
#Autowired
SomeInterface someInterface;
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
someInterface.doSomething();
}
}
The bean producer:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce() {
if (fromConfig.equals("aValueForUseClass1") {
return new UseClass1();
} else {
return new UseClass2();
}
//...
}
}
or if you have DI in UseClass1 and/or UseClass2:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce(#Autowired YourComponent yourComponent) {
SomeInterface someInterface;
if (fromConfig.equals("aValueForUseClass1") {
someInterface = new UseClass1();
someInterface.setYourComponent(yourComponent);
// or directly with the constructor if you have one with yourComponent as parameter.
} else {
someInterface = new UseClass2();
someInterface.setYourComponent(yourComponent);
}
//...
}
}

Set common variable in Spring Configuration

I have a Java Spring Configuration class like this. I want to set a variable that several of my beans depend on, turn it into a bean, and use it as a dependency. How can I make the setVariable() method happen first? I'm converting my code from Guice, where this variable was being set in the overridden 'Configuration' method. Does Spring have something like that?
#Configuration
class SpringConfiguration{
String variable;
public void setVariable(){
variable = System.getenv("whatever")
}
#Bean
public void variable(){
return variable;
}
#Bean
public void myService(){
return new MyService(variable);
}
#Bean
public void myService2(){
return new MyService2(variable);
}
You can do something like this :
#Configuration
class SpringConfiguration {
#Bean(name="variable")
public String geVariable() {
return System.getenv("whatever");
}
#Bean
#DependsOn("variable")
public MyService getMyService() {
return new MyService(geVariable());
}
#Bean
#DependsOn("variable")
public MyService2 getMyService2() {
return new MyService2(geVariable());
}
}
Like that you can insure that variable will be initialized before service1 and service2, note that DependsOn in this case is just for clarification purposes.

Categories