I'm a newbie and I try to start a Spring application linked to Optaplanner which will solve and place exams on a timetable.
I fixed the various issues with missing jars and I started the app on main.
However, it gives error:
Exception in thread "main" java.lang.ClassCastException: class org.springframework.boot.context.event.ApplicationStartingEvent cannot be cast to class org.springframework.boot.context.event.ApplicationPreparedEvent (org.springframework.boot.context.event.ApplicationStartingEvent and org.springframework.boot.context.event.ApplicationPreparedEvent are in unnamed module of loader 'app')
Here is the TimeTable class :
package models;
import java.util.List;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
#PlanningSolution
public class Timetable {
#ValueRangeProvider(id = "PeriodeRange")
#ProblemFactCollectionProperty
public List<Periode> periodeList;
#ValueRangeProvider(id = "SalleRange")
#ProblemFactCollectionProperty
public List<Salle> salleList;
#PlanningEntityCollectionProperty
public List<Examen> examenList;
#PlanningScore
public HardSoftScore score;
public void TimeTable(List<Periode> periodeList, List<Salle> roomList,
List<Examen> examenList) {
this.periodeList = periodeList;
this.salleList = roomList;
this.examenList = examenList;
}
public List<Periode> getperiodeList() {
return periodeList;
}
public List<Salle> getsalleList() {
return salleList;
}
public List<Examen> getexamenList() {
return examenList;
}
public HardSoftScore getScore() {
return score;
}
}
And the class that defines the solver :
package models;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import modeles.Timetable;
import org.optaplanner.core.api.solver.SolverJob;
import org.optaplanner.core.api.solver.SolverManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/Timetable")
public class TimetableController {
#Autowired
public SolverManager<Timetable, UUID> solverManager;
#PostMapping("/solve")
public Timetable solve(#RequestBody Timetable problem) {
UUID problemId = UUID.randomUUID();
// Submit the problem to start solving
SolverJob<Timetable, UUID> solverJob = solverManager.solve(problemId, problem);
Timetable solution;
try {
// Wait until the solving ends
solution = solverJob.getFinalBestSolution();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException("Solving failed.", e);
}
return solution;
}
}
And here is the main :
package models;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class TimeTableSpringBootApp {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(TimeTableSpringBootApp.class, args);
}
}
Any answer would be gladly appreciated.
Thank you in advance.
I suspect your project's dependency tree somehow has that spring class ApplicationStartingEvent twice in it's classpath (coming from different jars), which causes the class cast exception.
Try running mvn dependency:tree on your project and the optaplanner spring boot school timetabling quickstart. I suspect you're mixing spring versions in your dependency tree.
Related
#SpringBootTest
#AutoConfigureMockMvc
#ExcludeTags({"no"})
public class MyClassTest {
#Test
public void test1() {
}
#Test
#Tag("no")
public void test2() {
}
...
}
#RunWith(JUnitPlatform.class)
#SelectClasses({MyClassTest.class})
#IncludeTags({"no"})
public class MyClassTestSuiteTest {
}
Having a Spring Boot 2.3.1 project and testing some REST controllers, in a test class some of the test methods are tagged, and shall not be run, when MyClassTest is run. The annotated methods are run in a test suite (with #IncludeTags("no"). JUnit 5.6.2.
With the test suite I'm not sure it #RunWith has to be used for a test suite, or the JUnit 5 #ExtendWith is the right one? In fact, if not necessary, I don't want to mix JUnit 4 and 5, stick to JUnit 5.
Is there a way to configure simply via annotation or similar, to not run the tagged methods when MyClassTest is run? Like #ExcludeTags for test suites, but this does not work on a class like in the example.
Perhaps two test suites can be created, one with #ExludeTags("no"), one with #IncludeTags("no"). But still, how to prevent then that MyClassTest it run at all?
I don't want to create some Run Configuration in a particular IDE. The preferred way would be to use annotations or similar. Perhaps a Maven configuration would also suffice.
Perhaps on test method level execution of the particular test method can be avoided with some criteria evaluation, if the executed test class is MyClassTest, then don't run that test method.
Interesting here is, I cannot replace #RunWith(JUnitPlatform.class) simply with #ExtendWith(JUnitPlatform.class) as there is type incompatibility. Using #ExtendWith(SpringExtension.class) doesn't give me the possibility to run the class (for example with right-click on the class name, no entry to Run/Debug). But #ExtendWith replaces #RunWith in JUnit 5, what extension to use to run the test suite?
Create Execution Condition ExcludeTagsCondition
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.util.AnnotationUtils;
public class ExcludeTagsCondition implements ExecutionCondition {
private static final ConditionEvaluationResult ENABLED_IF_EXCLUDE_TAG_IS_INVALID =
ConditionEvaluationResult.enabled(
"#ExcludeTags does not have a valid tag to exclude, all tests will be run");
private static Set<String> tagsThatMustBeIncluded = new HashSet<>();
public static void setMustIncludeTags(final Set<String> tagsThatMustBeIncluded) {
ExcludeTagsCondition.tagsThatMustBeIncluded = new HashSet<>(tagsThatMustBeIncluded);
}
#Override
public ConditionEvaluationResult evaluateExecutionCondition(
ExtensionContext context) {
final AnnotatedElement element = context
.getElement()
.orElseThrow(IllegalStateException::new);
final Optional<Set<String>> tagsToExclude = AnnotationUtils.findAnnotation(
context.getRequiredTestClass(),
ExcludeTags.class
)
.map(a ->
Arrays.asList(a.value())
.stream()
.filter(t -> !tagsThatMustBeIncluded.contains(t))
.collect(Collectors.toSet())
);
if (!tagsToExclude.isPresent() || tagsToExclude.get().stream()
.allMatch(s -> (s == null) || s.trim().isEmpty())) {
return ENABLED_IF_EXCLUDE_TAG_IS_INVALID;
}
final Optional<String> tag = AnnotationUtils.findAnnotation(element, Tag.class)
.map(Tag::value);
if (tagsToExclude.get().contains(tag.map(String::trim).orElse(""))) {
return ConditionEvaluationResult
.disabled(String.format(
"test method \"%s\" has tag \"%s\" which is on the #ExcludeTags list \"[%s]\", test will be skipped",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.get(),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
return ConditionEvaluationResult.enabled(
String.format(
"test method \"%s\" has tag \"%s\" which is not on the #ExcludeTags list \"[%s]\", test will be run",
(element instanceof Method) ? ((Method) element).getName()
: element.getClass().getSimpleName(),
tag.orElse("<no tag present>"),
tagsToExclude.get().stream().collect(Collectors.joining(","))
));
}
}
Create annotation #ExcludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#ExtendWith(ExcludeTagsCondition.class)
public #interface ExcludeTags {
String[] value();
}
On your test
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
#ExcludeTags({"foo", "bar"})
#SpringBootTest
class AppTest {
#Test
#Tag("foo")
void test1() {
System.out.println("test1");
}
#Test
#Tag("bar")
void test2() {
System.out.println("test2");
}
#Test
#Tag("baz")
void test3() {
System.out.println("test3");
}
}
When you run the test, you should see the following output:
test method "test1" has tag "foo" which is on the #ExcludeTags list "[bar,foo]", test will be skipped
test method "test2" has tag "bar" which is on the #ExcludeTags list "[bar,foo]", test will be skipped
test3
And your test runner should show 1 test passing and 2 skipped.
Now for your test suite:
Create an annotation #MustIncludeTags
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
public #interface MustIncludeTags {
String[] value();
}
Now setup your test suite like so:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.runner.RunWith;
#RunWith(JUnitPlatform.class)
#SelectClasses({MyTestSuite.SetupTests.class, AppTest.class})
#MustIncludeTags({"foo", "bar"})
public class MyTestSuite {
public static class SetupTests {
#BeforeAll
public static void beforeClass() {
ExcludeTagsCondition.setMustIncludeTags(
Optional.ofNullable(MyTestSuite.class.getAnnotation(MustIncludeTags.class))
.map(MustIncludeTags::value)
.map(Arrays::asList)
.orElse(new ArrayList<>())
.stream()
.collect(Collectors.toSet())
);
}
#Disabled
#Test
void testDummy() {
// this test needs to be present for the beforeAll to run
}
}
}
When you run your test suite with the #MustIncludeTags the #ExcludedTags are overridden.
As you can see from the following test execution:
I am using Eclipse Mars 2, maven 3.3.9 and apache-tomee-plus-1.7.4.
I have 2 projects (A and B)
Project A is a Web Service compiled like a WAR using maven 3.3.9 and deployed into TOMEE_HOME/webapps
Project B is an EJB module compiled like a EAR using maven 3.3.9 and deployed into TOMEE_HOME/apps (this project include other project with ejb clasess and compiled like a jar file)
These projects do not depend on each other in the pom.xml but I need to lookup an EJB in project A from project B.
---------- Project B Implementation -----------
Local Bean Interface in project B:
package co.edu.uniquindio.model.ejb.interfaces;
import javax.ejb.Local;
import org.springframework.context.support.ClassPathXmlApplicationContext;
#Local
public interface IReporte {
public Object generate1();
public Object generate2();
public void setContext(ClassPathXmlApplicationContext context);
}
Implement local bean interface in project B:
package co.edu.uniquindio.model.ejb;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import co.edu.uniquindio.model.ejb.interfaces.IReporte;
#Stateless
#EJB(beanInterface = IReporte.class, beanName="ReporteEJB", name="IReporte")
public class ReporteEJB implements IReporte{
private ClassPathXmlApplicationContext context;
#Override
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public Object generate1(){
// do somthing amazing
}
#Override
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public Object generate2(){
// do somthing amazing
}
#Override
public void setContext(ClassPathXmlApplicationContext context) {
this.context = context;
}
}
---------- Project A Implementation -----------
The way that I develop lookup is:
package co.swatit.rest.services;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import co.edu.uniquindio.model.ejb.interfaces.IReporte;
#Path("/ReporteWS")
public class ReporteWS {
#POST
#Path("generate1and2")
#Produces(MediaType.APPLICATION_JSON)
#Consumes({ MediaType.APPLICATION_JSON} )
public Response generate1and2() {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
try {
Context ctx = new InitialContext(props);
// I do not know if use IReporte or ReporteEJB to cast. and I do not know how to import it.
IReporte ejbLocal = (IReporte) ctx.lookup("java:global/Sac-report-ear-1.0.0/co.swatit-Sac-report-ejb-1.0.0/ReporteEJB");
ejbLocal.generate1();
ejbLocal.generate2();
} catch (Exception exception) {
exception.printStackTrace()
}
return Response.status(Status.OK)
.entity(ejbLocal).build();
}
}
I do not know if it is possible import the local bean in project A to lookup that bean:
import co.edu.uniquindio.model.ejb.interfaces.IReporte
I do not know if use IReporte or ReporteEJB to cast, and I do not know how to import it.
Thank you for your help.
The hibernate validations documentation describes how to create ConstraintMappingContributors here.
It states:
You then need to specify the fully-qualified class name of the
contributor implementation in META-INF/validation.xml, using the
property key hibernate.validator.constraint_mapping_contributors. You
can specify several contributors by separating them with a comma.
Given I have many of these, what would be the most appropriate way to auto-discover these i.e. via #Component and add them dynamically at runtime to the ConstrainMappingConfiguration during Spring Boot startup.
For example.. if a developer creates a new ConstraintMappingContributor, it should be picked up and added automatically when spring boot starts, requiring no other file changes.
This is what I came up with, seems to be working for me.
package...
import org.hibernate.validator.spi.cfg.ConstraintMappingContributor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
#Configuration
public class ValidationConfiguration {
private final List<ConstraintMappingContributor> contributors;
public ValidationConfiguration(Optional<List<ConstraintMappingContributor>> contributors) {
this.contributors = contributors.orElseGet(ArrayList::new);
}
#Bean
public LocalValidatorFactoryBean validatorFactory() {
return new ValidatorFactoryBean(this.contributors);
}
}
package...
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
import org.hibernate.validator.spi.cfg.ConstraintMappingContributor;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.validation.Configuration;
import java.util.List;
public class ValidatorFactoryBean extends LocalValidatorFactoryBean {
private final List<ConstraintMappingContributor> contributors;
ValidatorFactoryBean(List<ConstraintMappingContributor> contributors) {
this.contributors = contributors;
}
#Override
protected void postProcessConfiguration(Configuration<?> cfg) {
if (cfg instanceof HibernateValidatorConfiguration) {
HibernateValidatorConfiguration configuration = (HibernateValidatorConfiguration) cfg;
this.contributors.forEach(contributor -> contributor.createConstraintMappings(() -> {
DefaultConstraintMapping mapping = new DefaultConstraintMapping();
configuration.addMapping(mapping);
return mapping;
}));
}
}
}
I invoke it like this...
if(SpringValidatorAdapter.class.isInstance(this.validatorFactory)){
SpringValidatorAdapter.class.cast(this.validatorFactory).validate(entity, errors);
}
I'm trying upgrade a JHipster project, however I found the following issue:
Description:
Parameter 0 of constructor in com.cervaki.config.AsyncConfiguration required a single bean, but 2 were found:
- jhipster-io.github.jhipster.config.JHipsterProperties: defined in null
- io.github.jhipster.config.JHipsterProperties: defined in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
What I understood is that spring can't inject the correct bean because there are two candidates, but I only have the io.github.jhipster.config.JHipsterProperties implementation:
package com.cervaki.config;
import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
import io.github.jhipster.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
private final JHipsterProperties jHipsterProperties;
public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
#Override
#Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
executor.setThreadNamePrefix("cervaki-Executor-");
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
You can download the pom.xml here.
I did a search in the entire code and libs to find the jhipster-io.github.jhipster.config.JHipsterProperties file, however I didn't find anything.
What can I do to solve this problem?
I also faced this issue after generating new JhipsterApp,
And as you - I don't find the "jhipster-io" dependencies in project
How I solve this:
in src/main/java/your/package/config create a "AppConfiguration.java"
with content:
import io.github.jhipster.config.JHipsterProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class AppConfiguration {
#Bean
#Primary
public JHipsterProperties jHipsterProperties() {
return new JHipsterProperties();
}
}
even without #Primary - I haven't got this error
I'v implemented a spring-boot aop demo and it runs well, but when I want to use it to load some resource when the project starts, it doesn't work somehow
Aop:
package com.neo.mysql;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Created by li_weia on 2017/7/6.
*/
#Aspect
#Component
public class DynamicDataSourceAspect {
#Before("#annotation(VendorSource)")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在#DS注解
if (method.isAnnotationPresent(VendorSource.class)) {
VendorSource annotation = method.getAnnotation(VendorSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
#After("#annotation(VendorSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDB();
}
}
The VendorSource annotation:
package com.neo.mysql;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by li_weia on 2017/7/6.
*/
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface VendorSource {
String value() default "vendor-master";
}
It runs well here, I can successfully change datasource by annotation:
package com.neo.web;
import com.neo.entity.SiteEntity;
import com.neo.mapper.ClassMappingDao;
import com.neo.mysql.VendorSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
public class UserController {
private final ClassMappingDao siteMapper;
#Autowired(required = false)
public UserController(ClassMappingDao siteMapper) {
this.siteMapper = siteMapper;
}
#RequestMapping("/getSites")
#VendorSource("vendor-read")
public List<SiteEntity> getUsers() {
return siteMapper.getAllSite();
}
}
but it doesn't work here, the aop method is not invoked at all:
package com.neo.component;
import com.neo.entity.SiteEntity;
import com.neo.mapper.ClassMappingDao;
import com.neo.mysql.VendorSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Created by li_weia on 2017/7/7.
*/
#Component
public class TestComponent{
private final ClassMappingDao userMapper;
#Autowired(required = false)
public TestComponent(ClassMappingDao userMapper) {
this.userMapper = userMapper;
init();
}
#VendorSource("vendor-read")
public void init() {
List<SiteEntity> sites = userMapper.getAllSite();
for(SiteEntity site: sites){
System.out.println(site.getSite());
}
}
}
You need to fully qualify the annotation, like so:
#Before("execution(public * *(..)) && #annotation(com.neo.mysql.VendorSource)")
private void whatever() {}
Also, as mentioned in my comment above, you need to have spring-boot-starter-aop on classpath. Maybe you already do, but since you didn't say, it's worth mentioning.
Edit:
I didn't notice the real problem before, I wasn't paying attention.
Spring AOP only triggers if you make calls from another class. This is because Spring needs to be able to intercept the call and run the pointcut. Calling the method from constructor is not going to do anything.
You can do a hackish workaround. Create a #PostConstruct void postConstruct() {} method in your class (not constructor), autowire ApplicationContext, and then do MyClassWithInitMethod myClass = context.getBean(MyClassWithInitMethod.class) in the postConstruct method. Then call the method on myClass, and AOP will kick in.
Frankly, I didn't previously check what you are doing in your pointcut, and it's a terrible idea. When multiple threads run, they are going to overwrite the static context, and create a race-condition that you'll then create another question for. Don't do it! Use the factory pattern instead, and inject the DataSourceFactory in the classes that now have the annotation.