I can autowire a list of service beans in spring boot but I need a way to pick the one I need using the name they have given.
#Service("myService")
public class DefaultService implements MyService {
}
#Service("myService2")
public class DefaultService2 implements MyService {
}
#Autowire
List<MyService> services;
is it possible to get DefaultService2 and DefaultService separately from the list.
If you want to get your services by name , implement the pattern factory as below:
Your service implementations :
public interface MyService {
void sayHello();
}
public class DefaultService implements MyService {
private static Logger log = LoggerFactory.getLogger(DefaultService.class);
#Override
public void sayHello() {
log.info("Hello from DefaultService");
}
}
public class DefaultService2 implements MyService {
private static Logger log = LoggerFactory.getLogger(DefaultService2.class);
#Override
public void sayHello() {
log.info("Hello from DefaultService2");
}
}
The factory interface :
public interface MyServiceFactory {
public MyService getMyServiceByName(String name);
}
The factory beans :
#Configuration
public class MyServiceFactoryBean {
#Bean
public FactoryBean serviceLocatorFactoryBean(){
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(MyServiceFactory.class);
return factoryBean;
}
#Bean("myService")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DefaultService defaultService(){
return new DefaultService();
}
#Bean("myService2")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DefaultService2 defaultService2(){
return new DefaultService2();
}
}
Usage :
#Autowired
MyServiceFactory myServiceFactory;
#Override
public void run(String... strings) throws Exception {
myServiceFactory.getMyServiceByName("myService").sayHello();
myServiceFactory.getMyServiceByName("myService2").sayHello();
}
Results :
2017-08-11 11:32:31.126 INFO 12827 --- [ restartedMain] c.m.test.DefaultService : Hello from DefaultService
2017-08-11 11:32:31.129 INFO 12827 --- [ restartedMain] c.m.test.DefaultService2 : Hello from DefaultService2
Add #Qualifier annotation:
#Autowired
#Qualifier("myService")
MyService myService;
Just a guess, but have you tried this?:
#Autowire
MyService myService;
#Autowire
MyService myService2;
The field name should be hint enough to Spring. Otherwise use #Qualifier as explained by albert_nil.
You can use :
#Autowired
#Qualifier("myService")
MyService myService;
Or use resource annotation. It should work.
#Resource
MyService myService;
#Resource
MyService myService2;
Try to iterate your bean in the List and identify each bean using reflection api .getClass().getSimpleName(). This will return the String name of your class.
for(MyService service : services) {
if(service.getClass().getSimpleName().equals("DefaultService")) {
MyService defaultService = service; // This is DefaultService class
}
if(service.getClass().getSimpleName().equals("DefaultService2")) {
MyService defaultService2 = service; // This is DefaultService2 class
}
}
With this, you will know what would be the Service you are using.
i can take out the beans seperately by sorting autowire order.naming is not needed now.
#Order(value=1)
#Service
public class DefaultService implements MyService {
}
#Service
#Order(value=2)
public class DefaultService2 implements MyService {
}
Related
Factory injection in Spring
//XML
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
//Java
How do you do dependency injection with only interfaces and factory classes and no configuration classes ?
//Service
public interface MyService {
void doSomething();
}
//ServiceFactory
public class MyServiceFactory {
MyService getInstance() {
//...
}
}
//Controller
#Controller
public class MyController {
#Autowired
private MyService myService;
}
you can do like this without adding the xml coding.
#Configuration
public class MyServiceFactory {
#Bean
MyService myService() {
return new MyService() {
#Override
public void doSomething() {
}
}
}
}
#Controller
public class MyController {
#Autowired
private MyService myService;
public void execute() {
myService.doSomething();
}
}
You can manually ask Spring to Autowire it.
Have your factory implement ApplicationContextAware. Then provide the following implementation in your factory:#Override public void setApplicationContext(final ApplicationContext applicationContext) { this.applicationContext = applicationContext; }
and then do the following after creating your bean:
YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.
I have 1 interface and 2 class implement this interface.
public interface Service{
void process();
}
#Component("ServiceA")
public class ServiceA implements Service{
public void process(){
// some code
}
}
#Component("ServiceB")
public class ServiceB implements Service{
public void process(){
// some code
}
}
I don't use #Qualifier. I want to use config file instead.
for example
# default serviceA
service.B.enable=true
And i use Service interface with #Autowire in another service.
#Service
public class MainService{
#Autowired
public Service service;
}
How can I do that?
You can set a condition annotation - #ConditionalOnProperty above ServiceA and ServiceB classes.
Config:
my.service=ServiceA
Changes in code:
public interface Service{
void process();
}
#Component("ServiceA")
#ConditionalOnProperty(prefix = "my", name = "service", havingValue = "ServiceA")
public class ServiceA implements Service{
public void process(){
// some code
}
}
#Component("ServiceB")
#ConditionalOnProperty(prefix = "my", name = "service", havingValue = "ServiceB")
public class ServiceB implements Service{
public void process(){
// some code
}
}
I'm getting NullPointerException when trying to inject a CDI bean into an EJB.
My ejb is
#Stateless(mappedName = "myEjb")
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyEjb implements MyEjbLocal, MyEjbRemote {
private static final Logger logger = Logger.getLogger(MyEjb .class);
#Inject
#Named("myService")
private MyService myService;
public MyEjb () {
}
public MyEjb (MyService myService) {
this.myService= myService;
}
#PostConstruct
public void postConstruct() {
logger.info("Injected myService" + myService+ " into this " + this);
}
And actually myService is always null.
Here is how I initialize myService by using CDI and a Factory
#ApplicationScoped
public class ServiceFactory {
private MyService myService;
#Inject
#Named("myDao")
private MyDao dao;
#PostConstruct
public void afterCreate() {
myService= new MyServiceImpl(dao);
}
#Produces
#ApplicationScoped
#Named("myService")
public MyService myService() {
return myService;
}
}
And MyService is just without annotations
public class MyServiceImpl implements MyService {
private final MyDao myDao;
public MyServiceImpl(MyDao dao) {
this.myDao = dao;
}
Well, if I invoke the EJB then myService is always null....what I'm missing? (bean.xml is there)
Am I missing something around the interaction ejb/cdi?
when i am using #autowire to inject my dependencies in Configuration
class its giving me as null please refer the code below .
#Configuration
public class DataSourceConfig {
#Autowired
AppService appService;
#Bean
public BeanDefinitionRegistryPostProcessor beanPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
createBeans(beanRegistry);
}
};
}
private void createBeans(BeanDefinitionRegistry beanRegistry,DataSourceConfigService ds) {
appService.getDbDetails();
appService is null here if i will call it using this way
BeanDefinitionRegistryPostProcessor beanPostProcessor(AppService
appService) then in AppServiceImpl class AppDao dependency will be null
}
}
//// Service
#Service
public class AppServiceImpl implements AppService{
#Autowired
AppDao ds;
#Override
public List<A> getDatabaseConfiguration() {
return ds.getDbDetails(); // here ds is null
}
}
//dao
#Repository
public class AppDaoImpl implements AppDao {
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
#Override
public List<A> getDbDetails() {
return nameParamJdbcTemplate.query(SELECT_QUERY, new DataSourceMapper()); // nameParamJdbcTemplate is null
}
// datasource config
#Configuration
public class DataSourceBuilderConfig {
#Bean(name = "dbSource")
#ConfigurationProperties(prefix = "datasource")
#Primary
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nameParamJdbcTemplate")
#DependsOn("dbSource")
#Autowired
public NamedParameterJdbcTemplate jdbcTemplate1(#Qualifier("dbSource") DataSource dbSource) {
return new NamedParameterJdbcTemplate(dbSource);
}
}
What i want is when ever my beanPostProcessor()
is executed i want all my dependent beans should be instantiated ie
#Autowired
AppService appService;
#Autowired
AppDao ds;
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
I am new to spring so any help or working examples would be great. Thanks
It is null because this #Configuration class also defines a BeanDefinitionRegistryPostProcessor that forces the context to create that bean very early on.
Because you are using field injection, the context has to resolve AppService bean but it can't yet because the post-processor have to be applied first.
Your configuration looks very complex so you may want to simplify it a bit:
Separate low-level infrastructure configuration from main configuration
Always define such post processor as public static method so that the context can invoke the #Bean method without having to construct the class first.
I'm doing unit test using spring mvc test framework.
The following is my source code:
com.exmple.main
MyController.java
#Controller
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public Map<Object, Object> myControllerFunction(#RequestBody final Object jsonRequest) {
/* do something */
return response;
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<My, String> {
#Query(value="select * from my d where (d.start_date<to_date(:date,'YYYY/DD/MM')) and (d.end_date>to_date(:date,'YYYY/DD/MM'))", nativeQuery=true)
List<My> findByDate(#Param("date") String date);
}
MyService.java
public interface MyService {
List<My> findByDate(String date);
}
MyServiceImpl.java
#Service
public class MyServiceImpl implements MyService {
#Autowired
MyRepository destRepo;
#Override
public List<My> findByDate(String date) {
List<My> listDest = destRepo.findByDate(date);
return listDest;
}
}
com.example.test
MyControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
MyService myService;
#Autowired
protected WebApplicationContext webApplicationContext;
#Before
public void setup() throws Exception {
// this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void listAllMy() throws Exception {
}
}
TestConfig.java
#Configuration
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new MyServiceImpl();
}
}
When I run test, the following error is displayed
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
I know the exception occurred because MyService didn't find any bean of MyRepository.
But I don't know how to create a bean of repository.
Please teach me how to create a bean of repository class using Java (not xml).
You need to enable the JPA repositories in your config class, specify the package that contains the repositories as below
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.repository"
})
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new DestinationServiceImpl();
}
}
Edit: looks like you haven't defined entityManager, and dataSource. Refer to a tutorial here and also answer to similar question here